Merge "Fuzzy_fastboot must set back the original slot after test"
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 0e43dae..ebe5f4a 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -78,3 +78,5 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/llndk.libraries.txt)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/vndksp.libraries.txt)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/sbin/charger)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/sbin/charger)
diff --git a/TEST_MAPPING b/TEST_MAPPING
index cc85408..716378b 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -10,6 +10,9 @@
       "name": "fs_mgr_unit_test"
     },
     {
+      "name": "fs_mgr_vendor_overlay_test"
+    },
+    {
       "name": "init_tests"
     },
     {
diff --git a/adb/Android.bp b/adb/Android.bp
index 3813578..01e00dd 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -24,7 +24,8 @@
         "-Wno-missing-field-initializers",
         "-Wthread-safety",
         "-Wvla",
-        "-DADB_HOST=1", // overridden by adbd_defaults
+        "-DADB_HOST=1",         // overridden by adbd_defaults
+        "-DALLOW_ADBD_ROOT=0",  // overridden by adbd_defaults
     ],
     cpp_std: "experimental",
 
@@ -79,7 +80,8 @@
     product_variables: {
         debuggable: {
             cflags: [
-                "-DALLOW_ADBD_ROOT",
+                "-UALLOW_ADBD_ROOT",
+                "-DALLOW_ADBD_ROOT=1",
                 "-DALLOW_ADBD_DISABLE_VERITY",
                 "-DALLOW_ADBD_NO_AUTH",
             ],
@@ -402,6 +404,14 @@
         "liblog",
     ],
 
+    product_variables: {
+        debuggable: {
+            required: [
+                "remount",
+            ],
+        },
+    },
+
     target: {
         android: {
             srcs: [
diff --git a/adb/adb.cpp b/adb/adb.cpp
index a5b2f7b..3c07882 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -1018,9 +1018,10 @@
     return 0;
 }
 
-bool handle_host_request(const char* service, TransportType type, const char* serial,
-                        TransportId transport_id, int reply_fd, asocket* s) {
-    if (strcmp(service, "kill") == 0) {
+HostRequestResult handle_host_request(std::string_view service, TransportType type,
+                                      const char* serial, TransportId transport_id, int reply_fd,
+                                      asocket* s) {
+    if (service == "kill") {
         fprintf(stderr, "adb server killed by remote request\n");
         fflush(stdout);
 
@@ -1032,29 +1033,49 @@
         exit(0);
     }
 
-    // "transport:" is used for switching transport with a specified serial number
-    // "transport-usb:" is used for switching transport to the only USB transport
-    // "transport-local:" is used for switching transport to the only local transport
-    // "transport-any:" is used for switching transport to the only transport
-    if (!strncmp(service, "transport", strlen("transport"))) {
+    LOG(DEBUG) << "handle_host_request(" << service << ")";
+
+    // Transport selection:
+    if (service.starts_with("transport") || service.starts_with("tport:")) {
         TransportType type = kTransportAny;
 
-        if (!strncmp(service, "transport-id:", strlen("transport-id:"))) {
-            service += strlen("transport-id:");
-            transport_id = strtoll(service, const_cast<char**>(&service), 10);
-            if (*service != '\0') {
-                SendFail(reply_fd, "invalid transport id");
-                return true;
+        std::string serial_storage;
+        bool legacy = true;
+
+        // New transport selection protocol:
+        // This is essentially identical to the previous version, except it returns the selected
+        // transport id to the caller as well.
+        if (ConsumePrefix(&service, "tport:")) {
+            legacy = false;
+            if (ConsumePrefix(&service, "serial:")) {
+                serial_storage = service;
+                serial = serial_storage.c_str();
+            } else if (service == "usb") {
+                type = kTransportUsb;
+            } else if (service == "local") {
+                type = kTransportLocal;
+            } else if (service == "any") {
+                type = kTransportAny;
             }
-        } else if (!strncmp(service, "transport-usb", strlen("transport-usb"))) {
-            type = kTransportUsb;
-        } else if (!strncmp(service, "transport-local", strlen("transport-local"))) {
-            type = kTransportLocal;
-        } else if (!strncmp(service, "transport-any", strlen("transport-any"))) {
-            type = kTransportAny;
-        } else if (!strncmp(service, "transport:", strlen("transport:"))) {
-            service += strlen("transport:");
-            serial = service;
+
+            // Selection by id is unimplemented, since you obviously already know the transport id
+            // you're connecting to.
+        } else {
+            if (ConsumePrefix(&service, "transport-id:")) {
+                if (!ParseUint(&transport_id, service)) {
+                    SendFail(reply_fd, "invalid transport id");
+                    return HostRequestResult::Handled;
+                }
+            } else if (service == "transport-usb") {
+                type = kTransportUsb;
+            } else if (service == "transport-local") {
+                type = kTransportLocal;
+            } else if (service == "transport-any") {
+                type = kTransportAny;
+            } else if (ConsumePrefix(&service, "transport:")) {
+                serial_storage = service;
+                serial = serial_storage.c_str();
+            }
         }
 
         std::string error;
@@ -1063,27 +1084,29 @@
             s->transport = t;
             SendOkay(reply_fd);
 
-            // We succesfully handled the device selection, but there's another request coming.
-            return false;
+            if (!legacy) {
+                // Nothing we can do if this fails.
+                WriteFdExactly(reply_fd, &t->id, sizeof(t->id));
+            }
+
+            return HostRequestResult::SwitchedTransport;
         } else {
             SendFail(reply_fd, error);
-            return true;
+            return HostRequestResult::Handled;
         }
     }
 
     // return a list of all connected devices
-    if (!strncmp(service, "devices", 7)) {
-        bool long_listing = (strcmp(service+7, "-l") == 0);
-        if (long_listing || service[7] == 0) {
-            D("Getting device list...");
-            std::string device_list = list_transports(long_listing);
-            D("Sending device list...");
-            SendOkay(reply_fd, device_list);
-        }
-        return true;
+    if (service == "devices" || service == "devices-l") {
+        bool long_listing = service == "devices-l";
+        D("Getting device list...");
+        std::string device_list = list_transports(long_listing);
+        D("Sending device list...");
+        SendOkay(reply_fd, device_list);
+        return HostRequestResult::Handled;
     }
 
-    if (!strcmp(service, "reconnect-offline")) {
+    if (service == "reconnect-offline") {
         std::string response;
         close_usb_devices([&response](const atransport* transport) {
             if (!ConnectionStateIsOnline(transport->GetConnectionState())) {
@@ -1096,10 +1119,10 @@
             response.resize(response.size() - 1);
         }
         SendOkay(reply_fd, response);
-        return true;
+        return HostRequestResult::Handled;
     }
 
-    if (!strcmp(service, "features")) {
+    if (service == "features") {
         std::string error;
         atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t != nullptr) {
@@ -1107,10 +1130,10 @@
         } else {
             SendFail(reply_fd, error);
         }
-        return true;
+        return HostRequestResult::Handled;
     }
 
-    if (!strcmp(service, "host-features")) {
+    if (service == "host-features") {
         FeatureSet features = supported_features();
         // Abuse features to report libusb status.
         if (should_use_libusb()) {
@@ -1118,16 +1141,16 @@
         }
         features.insert(kFeaturePushSync);
         SendOkay(reply_fd, FeatureSetToString(features));
-        return true;
+        return HostRequestResult::Handled;
     }
 
     // remove TCP transport
-    if (!strncmp(service, "disconnect:", 11)) {
-        const std::string address(service + 11);
+    if (service.starts_with("disconnect:")) {
+        std::string address(service.substr(11));
         if (address.empty()) {
             kick_all_tcp_devices();
             SendOkay(reply_fd, "disconnected everything");
-            return true;
+            return HostRequestResult::Handled;
         }
 
         std::string serial;
@@ -1139,26 +1162,26 @@
         } else if (!android::base::ParseNetAddress(address, &host, &port, &serial, &error)) {
             SendFail(reply_fd, android::base::StringPrintf("couldn't parse '%s': %s",
                                                            address.c_str(), error.c_str()));
-            return true;
+            return HostRequestResult::Handled;
         }
         atransport* t = find_transport(serial.c_str());
         if (t == nullptr) {
             SendFail(reply_fd, android::base::StringPrintf("no such device '%s'", serial.c_str()));
-            return true;
+            return HostRequestResult::Handled;
         }
         kick_transport(t);
         SendOkay(reply_fd, android::base::StringPrintf("disconnected %s", address.c_str()));
-        return true;
+        return HostRequestResult::Handled;
     }
 
     // Returns our value for ADB_SERVER_VERSION.
-    if (!strcmp(service, "version")) {
+    if (service == "version") {
         SendOkay(reply_fd, android::base::StringPrintf("%04x", ADB_SERVER_VERSION));
-        return true;
+        return HostRequestResult::Handled;
     }
 
     // These always report "unknown" rather than the actual error, for scripts.
-    if (!strcmp(service, "get-serialno")) {
+    if (service == "get-serialno") {
         std::string error;
         atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t) {
@@ -1166,9 +1189,9 @@
         } else {
             SendFail(reply_fd, error);
         }
-        return true;
+        return HostRequestResult::Handled;
     }
-    if (!strcmp(service, "get-devpath")) {
+    if (service == "get-devpath") {
         std::string error;
         atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t) {
@@ -1176,9 +1199,9 @@
         } else {
             SendFail(reply_fd, error);
         }
-        return true;
+        return HostRequestResult::Handled;
     }
-    if (!strcmp(service, "get-state")) {
+    if (service == "get-state") {
         std::string error;
         atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t) {
@@ -1186,39 +1209,46 @@
         } else {
             SendFail(reply_fd, error);
         }
-        return true;
+        return HostRequestResult::Handled;
     }
 
     // Indicates a new emulator instance has started.
-    if (!strncmp(service, "emulator:", 9)) {
-        int  port = atoi(service+9);
-        local_connect(port);
+    if (ConsumePrefix(&service, "emulator:")) {
+        unsigned int port;
+        if (!ParseUint(&port, service)) {
+          LOG(ERROR) << "received invalid port for emulator: " << service;
+        } else {
+          local_connect(port);
+        }
+
         /* we don't even need to send a reply */
-        return true;
+        return HostRequestResult::Handled;
     }
 
-    if (!strcmp(service, "reconnect")) {
+    if (service == "reconnect") {
         std::string response;
         atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &response, true);
         if (t != nullptr) {
             kick_transport(t);
             response =
-                "reconnecting " + t->serial_name() + " [" + t->connection_state_name() + "]\n";
+                    "reconnecting " + t->serial_name() + " [" + t->connection_state_name() + "]\n";
         }
         SendOkay(reply_fd, response);
-        return true;
+        return HostRequestResult::Handled;
     }
 
-    if (handle_forward_request(service,
-                               [=](std::string* error) {
-                                   return acquire_one_transport(type, serial, transport_id, nullptr,
-                                                                error);
-                               },
-                               reply_fd)) {
-        return true;
+    // TODO: Switch handle_forward_request to string_view.
+    std::string service_str(service);
+    if (handle_forward_request(
+                service_str.c_str(),
+                [=](std::string* error) {
+                    return acquire_one_transport(type, serial, transport_id, nullptr, error);
+                },
+                reply_fd)) {
+        return HostRequestResult::Handled;
     }
 
-    return false;
+    return HostRequestResult::Unhandled;
 }
 
 static auto& init_mutex = *new std::mutex();
diff --git a/adb/adb.h b/adb/adb.h
index 9209997..c60dcbc 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -59,7 +59,7 @@
 std::string adb_version();
 
 // Increment this when we want to force users to start a new adb server.
-#define ADB_SERVER_VERSION 40
+#define ADB_SERVER_VERSION 41
 
 using TransportId = uint64_t;
 class atransport;
@@ -145,7 +145,8 @@
 #endif
 
 #if ADB_HOST
-asocket* host_service_to_socket(const char* name, const char* serial, TransportId transport_id);
+asocket* host_service_to_socket(std::string_view name, std::string_view serial,
+                                TransportId transport_id);
 #endif
 
 #if !ADB_HOST
@@ -153,7 +154,7 @@
 #endif
 
 #if !ADB_HOST
-unique_fd execute_binder_command(std::string_view command);
+unique_fd execute_abb_command(std::string_view command);
 #endif
 
 #if !ADB_HOST
@@ -218,8 +219,15 @@
 #define USB_FFS_ADB_IN USB_FFS_ADB_EP(ep2)
 #endif
 
-bool handle_host_request(const char* service, TransportType type, const char* serial,
-                         TransportId transport_id, int reply_fd, asocket* s);
+enum class HostRequestResult {
+    Handled,
+    SwitchedTransport,
+    Unhandled,
+};
+
+HostRequestResult handle_host_request(std::string_view service, TransportType type,
+                                      const char* serial, TransportId transport_id, int reply_fd,
+                                      asocket* s);
 
 void handle_online(atransport* t);
 void handle_offline(atransport* t);
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
index 605d27d..f5cdcb5 100644
--- a/adb/adb_io.cpp
+++ b/adb/adb_io.cpp
@@ -34,7 +34,7 @@
 #include "adb_utils.h"
 #include "sysdeps.h"
 
-bool SendProtocolString(int fd, const std::string& s) {
+bool SendProtocolString(int fd, std::string_view s) {
     unsigned int length = s.size();
     if (length > MAX_PAYLOAD - 4) {
         errno = EMSGSIZE;
@@ -69,7 +69,7 @@
     return WriteFdExactly(fd, "OKAY", 4);
 }
 
-bool SendFail(int fd, const std::string& reason) {
+bool SendFail(int fd, std::string_view reason) {
     return WriteFdExactly(fd, "FAIL", 4) && SendProtocolString(fd, reason);
 }
 
@@ -187,79 +187,3 @@
         return false;
     }
 }
-
-#if defined(__linux__)
-bool SendFileDescriptor(int socket_fd, int fd) {
-    struct msghdr msg;
-    struct iovec iov;
-    char dummy = '!';
-    union {
-        cmsghdr cm;
-        char buffer[CMSG_SPACE(sizeof(int))];
-    } cm_un;
-
-    iov.iov_base = &dummy;
-    iov.iov_len = 1;
-    msg.msg_name = nullptr;
-    msg.msg_namelen = 0;
-    msg.msg_iov = &iov;
-    msg.msg_iovlen = 1;
-    msg.msg_flags = 0;
-    msg.msg_control = cm_un.buffer;
-    msg.msg_controllen = sizeof(cm_un.buffer);
-
-    cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
-    cmsg->cmsg_len = CMSG_LEN(sizeof(int));
-    cmsg->cmsg_level = SOL_SOCKET;
-    cmsg->cmsg_type = SCM_RIGHTS;
-    ((int*)CMSG_DATA(cmsg))[0] = fd;
-
-    int ret = TEMP_FAILURE_RETRY(sendmsg(socket_fd, &msg, 0));
-    if (ret < 0) {
-        D("sending file descriptor via socket %d failed: %s", socket_fd, strerror(errno));
-        return false;
-    }
-
-    D("sent file descriptor %d to via socket %d", fd, socket_fd);
-    return true;
-}
-
-bool ReceiveFileDescriptor(int socket_fd, unique_fd* fd, std::string* error) {
-    char dummy = '!';
-    union {
-        cmsghdr cm;
-        char buffer[CMSG_SPACE(sizeof(int))];
-    } cm_un;
-
-    iovec iov;
-    iov.iov_base = &dummy;
-    iov.iov_len = 1;
-
-    msghdr msg;
-    msg.msg_name = nullptr;
-    msg.msg_namelen = 0;
-    msg.msg_iov = &iov;
-    msg.msg_iovlen = 1;
-    msg.msg_flags = 0;
-    msg.msg_control = cm_un.buffer;
-    msg.msg_controllen = sizeof(cm_un.buffer);
-
-    cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
-    cmsg->cmsg_len = CMSG_LEN(sizeof(int));
-    cmsg->cmsg_level = SOL_SOCKET;
-    cmsg->cmsg_type = SCM_RIGHTS;
-    ((int*)(CMSG_DATA(cmsg)))[0] = -1;
-
-    int rc = TEMP_FAILURE_RETRY(recvmsg(socket_fd, &msg, 0));
-    if (rc <= 0) {
-        *error = perror_str("receiving file descriptor via socket failed");
-        D("receiving file descriptor via socket %d failed: %s", socket_fd, strerror(errno));
-        return false;
-    }
-
-    fd->reset(((int*)(CMSG_DATA(cmsg)))[0]);
-    D("received file descriptor %d to via socket %d", fd->get(), socket_fd);
-
-    return true;
-}
-#endif
diff --git a/adb/adb_io.h b/adb/adb_io.h
index 2ccaa32..d6e65d8 100644
--- a/adb/adb_io.h
+++ b/adb/adb_io.h
@@ -20,6 +20,7 @@
 #include <sys/types.h>
 
 #include <string>
+#include <string_view>
 
 #include "adb_unique_fd.h"
 
@@ -27,10 +28,10 @@
 bool SendOkay(int fd);
 
 // Sends the protocol "FAIL" message, with the given failure reason.
-bool SendFail(int fd, const std::string& reason);
+bool SendFail(int fd, std::string_view reason);
 
 // Writes a protocol-format string; a four hex digit length followed by the string data.
-bool SendProtocolString(int fd, const std::string& s);
+bool SendProtocolString(int fd, std::string_view s);
 
 // Reads a protocol-format string; a four hex digit length followed by the string data.
 bool ReadProtocolString(int fd, std::string* s, std::string* error);
@@ -74,13 +75,4 @@
 
 // Same as above, but formats the string to send.
 bool WriteFdFmt(int fd, const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3)));
-
-#if !ADB_HOST
-// Sends an FD via Unix domain socket.
-bool SendFileDescriptor(int socket_fd, int fd);
-
-// Receives an FD via Unix domain socket.
-bool ReceiveFileDescriptor(int socket_fd, unique_fd* fd, std::string* error);
-#endif
-
 #endif /* ADB_IO_H */
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index a85ca8c..5800a62 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -110,7 +110,7 @@
 
 // Base-10 stroll on a string_view.
 template <typename T>
-inline bool ParseUint(T* result, std::string_view str, std::string_view* remaining) {
+inline bool ParseUint(T* result, std::string_view str, std::string_view* remaining = nullptr) {
     if (str.empty() || !isdigit(str[0])) {
         return false;
     }
@@ -135,6 +135,17 @@
     *result = value;
     if (remaining) {
         *remaining = str.substr(it - str.begin());
+    } else {
+      return it == str.end();
     }
+
     return true;
 }
+
+inline bool ConsumePrefix(std::string_view* str, std::string_view prefix) {
+  if (str->starts_with(prefix)) {
+    str->remove_prefix(prefix.size());
+    return true;
+  }
+  return false;
+}
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index 8518e17..bd676c2 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -206,6 +206,14 @@
             EXPECT_EQ(remaining, "foo");
         }
     }
+
+    // With trailing text, without remaining.
+    {
+        std::string text = std::string(string) + "foo";
+        uint32_t value;
+        bool success = ParseUint(&value, text, nullptr);
+        EXPECT_EQ(success, false);
+    }
 }
 
 TEST(adb_utils, ParseUint) {
diff --git a/adb/benchmark_device.py b/adb/benchmark_device.py
index e56ef5a..4d0cf49 100755
--- a/adb/benchmark_device.py
+++ b/adb/benchmark_device.py
@@ -17,6 +17,8 @@
 
 import os
 import statistics
+import subprocess
+import tempfile
 import time
 
 import adb
@@ -56,6 +58,41 @@
     msg = "%s: %d runs: median %.2f MiB/s, mean %.2f MiB/s, stddev: %.2f MiB/s"
     print(msg % (name, len(speeds), median, mean, stddev))
 
+def benchmark_sink(device=None, size_mb=100):
+    if device == None:
+        device = adb.get_device()
+
+    speeds = list()
+    cmd = device.adb_cmd + ["raw", "sink:%d" % (size_mb * 1024 * 1024)]
+
+    with tempfile.TemporaryFile() as tmpfile:
+        tmpfile.truncate(size_mb * 1024 * 1024)
+
+        for _ in range(0, 10):
+            tmpfile.seek(0)
+            begin = time.time()
+            subprocess.check_call(cmd, stdin=tmpfile)
+            end = time.time()
+            speeds.append(size_mb / float(end - begin))
+
+    analyze("sink %dMiB" % size_mb, speeds)
+
+def benchmark_source(device=None, size_mb=100):
+    if device == None:
+        device = adb.get_device()
+
+    speeds = list()
+    cmd = device.adb_cmd + ["raw", "source:%d" % (size_mb * 1024 * 1024)]
+
+    with open(os.devnull, 'w') as devnull:
+        for _ in range(0, 10):
+            begin = time.time()
+            subprocess.check_call(cmd, stdout=devnull)
+            end = time.time()
+            speeds.append(size_mb / float(end - begin))
+
+    analyze("source %dMiB" % size_mb, speeds)
+
 def benchmark_push(device=None, file_size_mb=100):
     if device == None:
         device = adb.get_device()
@@ -110,6 +147,8 @@
 def main():
     device = adb.get_device()
     unlock(device)
+    benchmark_sink(device)
+    benchmark_source(device)
     benchmark_push(device)
     benchmark_pull(device)
 
diff --git a/adb/client/adb_client.cpp b/adb/client/adb_client.cpp
index 0a09d1e..5a7bc8d 100644
--- a/adb/client/adb_client.cpp
+++ b/adb/client/adb_client.cpp
@@ -31,10 +31,12 @@
 
 #include <condition_variable>
 #include <mutex>
+#include <optional>
 #include <string>
 #include <thread>
 #include <vector>
 
+#include <android-base/file.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/thread_annotations.h>
@@ -70,46 +72,60 @@
     __adb_server_socket_spec = socket_spec;
 }
 
-static int switch_socket_transport(int fd, std::string* error) {
+static std::optional<TransportId> switch_socket_transport(int fd, std::string* error) {
+    TransportId result;
+    bool read_transport = true;
+
     std::string service;
     if (__adb_transport_id) {
+        read_transport = false;
         service += "host:transport-id:";
         service += std::to_string(__adb_transport_id);
+        result = __adb_transport_id;
     } else if (__adb_serial) {
-        service += "host:transport:";
+        service += "host:tport:serial:";
         service += __adb_serial;
     } else {
         const char* transport_type = "???";
         switch (__adb_transport) {
           case kTransportUsb:
-            transport_type = "transport-usb";
-            break;
+              transport_type = "usb";
+              break;
           case kTransportLocal:
-            transport_type = "transport-local";
-            break;
+              transport_type = "local";
+              break;
           case kTransportAny:
-            transport_type = "transport-any";
-            break;
+              transport_type = "any";
+              break;
           case kTransportHost:
             // no switch necessary
             return 0;
         }
-        service += "host:";
+        service += "host:tport:";
         service += transport_type;
     }
 
     if (!SendProtocolString(fd, service)) {
         *error = perror_str("write failure during connection");
-        return -1;
+        return std::nullopt;
     }
-    D("Switch transport in progress");
+
+    LOG(DEBUG) << "Switch transport in progress: " << service;
 
     if (!adb_status(fd, error)) {
         D("Switch transport failed: %s", error->c_str());
-        return -1;
+        return std::nullopt;
     }
+
+    if (read_transport) {
+        if (!ReadFdExactly(fd, &result, sizeof(result))) {
+            *error = "failed to read transport id from server";
+            return std::nullopt;
+        }
+    }
+
     D("Switch transport success");
-    return 0;
+    return result;
 }
 
 bool adb_status(int fd, std::string* error) {
@@ -133,11 +149,10 @@
     return false;
 }
 
-static int _adb_connect(const std::string& service, std::string* error) {
-    D("_adb_connect: %s", service.c_str());
+static int _adb_connect(std::string_view service, TransportId* transport, std::string* error) {
+    LOG(DEBUG) << "_adb_connect: " << service;
     if (service.empty() || service.size() > MAX_PAYLOAD) {
-        *error = android::base::StringPrintf("bad service name length (%zd)",
-                                             service.size());
+        *error = android::base::StringPrintf("bad service name length (%zd)", service.size());
         return -1;
     }
 
@@ -149,8 +164,15 @@
         return -2;
     }
 
-    if (memcmp(&service[0], "host", 4) != 0 && switch_socket_transport(fd.get(), error)) {
-        return -1;
+    if (!service.starts_with("host")) {
+        std::optional<TransportId> transport_result = switch_socket_transport(fd.get(), error);
+        if (!transport_result) {
+            return -1;
+        }
+
+        if (transport) {
+            *transport = *transport_result;
+        }
     }
 
     if (!SendProtocolString(fd.get(), service)) {
@@ -190,15 +212,30 @@
     return true;
 }
 
-int adb_connect(const std::string& service, std::string* error) {
-    // first query the adb server's version
-    unique_fd fd(_adb_connect("host:version", error));
+int adb_connect(std::string_view service, std::string* error) {
+    return adb_connect(nullptr, service, error);
+}
 
-    D("adb_connect: service %s", service.c_str());
-    if (fd == -2 && !is_local_socket_spec(__adb_server_socket_spec)) {
+#if defined(__linux__)
+std::optional<std::string> adb_get_server_executable_path() {
+    int port;
+    std::string error;
+    if (!parse_tcp_socket_spec(__adb_server_socket_spec, nullptr, &port, nullptr, &error)) {
+        LOG(FATAL) << "failed to parse server socket spec: " << error;
+    }
+
+    return adb_get_android_dir_path() + OS_PATH_SEPARATOR + "adb." + std::to_string(port);
+}
+#endif
+
+static bool __adb_check_server_version(std::string* error) {
+    unique_fd fd(_adb_connect("host:version", nullptr, error));
+
+    bool local = is_local_socket_spec(__adb_server_socket_spec);
+    if (fd == -2 && !local) {
         fprintf(stderr, "* cannot start server on remote host\n");
         // error is the original network connection error
-        return fd;
+        return false;
     } else if (fd == -2) {
         fprintf(stderr, "* daemon not running; starting now at %s\n", __adb_server_socket_spec);
     start_server:
@@ -208,7 +245,7 @@
             // return a generic error string about the overall adb_connect()
             // that the caller requested.
             *error = "cannot connect to daemon";
-            return -1;
+            return false;
         } else {
             fprintf(stderr, "* daemon started successfully\n");
         }
@@ -216,13 +253,13 @@
         // Fall through to _adb_connect.
     } else {
         // If a server is already running, check its version matches.
-        int version = ADB_SERVER_VERSION - 1;
+        int version = 0;
 
         // If we have a file descriptor, then parse version result.
         if (fd >= 0) {
             std::string version_string;
             if (!ReadProtocolString(fd, &version_string, error)) {
-                return -1;
+                return false;
             }
 
             ReadOrderlyShutdown(fd);
@@ -230,18 +267,39 @@
             if (sscanf(&version_string[0], "%04x", &version) != 1) {
                 *error = android::base::StringPrintf("cannot parse version string: %s",
                                                      version_string.c_str());
-                return -1;
+                return false;
             }
         } else {
             // If fd is -1 check for "unknown host service" which would
             // indicate a version of adb that does not support the
             // version command, in which case we should fall-through to kill it.
             if (*error != "unknown host service") {
-                return fd;
+                return false;
             }
         }
 
         if (version != ADB_SERVER_VERSION) {
+#if defined(__linux__)
+            if (version > ADB_SERVER_VERSION && local) {
+                // Try to re-exec the existing adb server's binary.
+                constexpr const char* adb_reexeced = "adb (re-execed)";
+                if (strcmp(adb_reexeced, *__adb_argv) != 0) {
+                    __adb_argv[0] = adb_reexeced;
+                    std::optional<std::string> server_path_path = adb_get_server_executable_path();
+                    std::string server_path;
+                    if (server_path_path &&
+                        android::base::ReadFileToString(*server_path_path, &server_path)) {
+                        if (execve(server_path.c_str(), const_cast<char**>(__adb_argv),
+                                   const_cast<char**>(__adb_envp)) == -1) {
+                            LOG(ERROR) << "failed to exec newer version at " << server_path;
+                        }
+
+                        // Fall-through to restarting the server.
+                    }
+                }
+            }
+#endif
+
             fprintf(stderr, "adb server version (%d) doesn't match this client (%d); killing...\n",
                     version, ADB_SERVER_VERSION);
             adb_kill_server();
@@ -249,12 +307,36 @@
         }
     }
 
+    return true;
+}
+
+bool adb_check_server_version(std::string* error) {
+    // Only check the version once per process, since this isn't atomic anyway.
+    static std::once_flag once;
+    static bool result;
+    static std::string* err;
+    std::call_once(once, []() {
+        err = new std::string();
+        result = __adb_check_server_version(err);
+    });
+    *error = *err;
+    return result;
+}
+
+int adb_connect(TransportId* transport, std::string_view service, std::string* error) {
+    LOG(DEBUG) << "adb_connect: service: " << service;
+
+    // Query the adb server's version.
+    if (!adb_check_server_version(error)) {
+        return -1;
+    }
+
     // if the command is start-server, we are done.
     if (service == "host:start-server") {
         return 0;
     }
 
-    fd.reset(_adb_connect(service, error));
+    unique_fd fd(_adb_connect(service, transport, error));
     if (fd == -1) {
         D("_adb_connect error: %s", error->c_str());
     } else if(fd == -2) {
@@ -265,7 +347,6 @@
     return fd.release();
 }
 
-
 bool adb_command(const std::string& service) {
     std::string error;
     unique_fd fd(adb_connect(service, &error));
diff --git a/adb/client/adb_client.h b/adb/client/adb_client.h
index d467539..8d32c93 100644
--- a/adb/client/adb_client.h
+++ b/adb/client/adb_client.h
@@ -20,11 +20,20 @@
 #include "sysdeps.h"
 #include "transport.h"
 
+#include <optional>
 #include <string>
 
+// Explicitly check the adb server version.
+// All of the commands below do this implicitly.
+// Only the first invocation of this function will check the server version.
+bool adb_check_server_version(std::string* _Nonnull error);
+
 // Connect to adb, connect to the named service, and return a valid fd for
 // interacting with that service upon success or a negative number on failure.
-int adb_connect(const std::string& service, std::string* _Nonnull error);
+int adb_connect(std::string_view service, std::string* _Nonnull error);
+
+// Same as above, except returning the TransportId for the service that we've connected to.
+int adb_connect(TransportId* _Nullable id, std::string_view service, std::string* _Nonnull error);
 
 // Kill the currently running adb server, if it exists.
 bool adb_kill_server();
@@ -62,3 +71,13 @@
 
 // Get the feature set of the current preferred transport.
 bool adb_get_feature_set(FeatureSet* _Nonnull feature_set, std::string* _Nonnull error);
+
+#if defined(__linux__)
+// Get the path of a file containing the path to the server executable, if the socket spec set via
+// adb_set_socket_spec is a local one.
+std::optional<std::string> adb_get_server_executable_path();
+#endif
+
+// Globally acccesible argv/envp, for the purpose of re-execing adb.
+extern const char* _Nullable * _Nullable __adb_argv;
+extern const char* _Nullable * _Nullable __adb_envp;
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index 2bf2924..16fa215 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -638,39 +638,43 @@
         fprintf(stdout, "Created child session ID %d.\n", session_id);
         session_ids.push_back(session_id);
 
-        struct stat sb;
-        if (stat(file, &sb) == -1) {
-            fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
-            goto finalize_multi_package_session;
+        // Support splitAPKs by allowing the notation split1.apk:split2.apk:split3.apk as argument.
+        std::vector<std::string> splits = android::base::Split(file, ":");
+
+        for (const std::string& split : splits) {
+            struct stat sb;
+            if (stat(split.c_str(), &sb) == -1) {
+                fprintf(stderr, "adb: failed to stat %s: %s\n", split.c_str(), strerror(errno));
+                goto finalize_multi_package_session;
+            }
+
+            std::string cmd = android::base::StringPrintf(
+                    "%s install-write -S %" PRIu64 " %d %d_%s -", install_cmd.c_str(),
+                    static_cast<uint64_t>(sb.st_size), session_id, i,
+                    android::base::Basename(split).c_str());
+
+            unique_fd local_fd(adb_open(split.c_str(), O_RDONLY | O_CLOEXEC));
+            if (local_fd < 0) {
+                fprintf(stderr, "adb: failed to open %s: %s\n", split.c_str(), strerror(errno));
+                goto finalize_multi_package_session;
+            }
+
+            std::string error;
+            unique_fd remote_fd(adb_connect(cmd, &error));
+            if (remote_fd < 0) {
+                fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
+                goto finalize_multi_package_session;
+            }
+
+            copy_to_file(local_fd.get(), remote_fd.get());
+            read_status_line(remote_fd.get(), buf, sizeof(buf));
+
+            if (strncmp("Success", buf, 7)) {
+                fprintf(stderr, "adb: failed to write %s\n", split.c_str());
+                fputs(buf, stderr);
+                goto finalize_multi_package_session;
+            }
         }
-
-        std::string cmd =
-                android::base::StringPrintf("%s install-write -S %" PRIu64 " %d %d_%s -",
-                                            install_cmd.c_str(), static_cast<uint64_t>(sb.st_size),
-                                            session_id, i, android::base::Basename(file).c_str());
-
-        unique_fd local_fd(adb_open(file, O_RDONLY | O_CLOEXEC));
-        if (local_fd < 0) {
-            fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
-            goto finalize_multi_package_session;
-        }
-
-        std::string error;
-        unique_fd remote_fd(adb_connect(cmd, &error));
-        if (remote_fd < 0) {
-            fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
-            goto finalize_multi_package_session;
-        }
-
-        copy_to_file(local_fd.get(), remote_fd.get());
-        read_status_line(remote_fd.get(), buf, sizeof(buf));
-
-        if (strncmp("Success", buf, 7)) {
-            fprintf(stderr, "adb: failed to write %s\n", file);
-            fputs(buf, stderr);
-            goto finalize_multi_package_session;
-        }
-
         all_session_ids += android::base::StringPrintf(" %d", session_id);
     }
 
@@ -718,6 +722,7 @@
         fputs(buf, stderr);
     }
 
+    session_ids.push_back(parent_session_id);
     // try to abandon all remaining sessions
     for (std::size_t i = 0; i < session_ids.size(); i++) {
         service = android::base::StringPrintf("%s install-abandon %d", install_cmd.c_str(),
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index f70b480..43a3e5e 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -190,8 +190,8 @@
         "scripting:\n"
         " wait-for[-TRANSPORT]-STATE\n"
         "     wait for device to be in the given state\n"
-        "     State: device, recovery, sideload, or bootloader\n"
-        "     Transport: usb, local, or any [default=any]\n"
+        "     STATE: device, recovery, sideload, bootloader, or disconnect\n"
+        "     TRANSPORT: usb, local, or any [default=any]\n"
         " get-state                print offline | bootloader | device\n"
         " get-serialno             print <serial-number>\n"
         " get-devpath              print <device-path>\n"
@@ -222,7 +222,9 @@
         "     all,adb,sockets,packets,rwx,usb,sync,sysdeps,transport,jdwp\n"
         " $ADB_VENDOR_KEYS         colon-separated list of keys (files or directories)\n"
         " $ANDROID_SERIAL          serial number to connect to (see -s)\n"
-        " $ANDROID_LOG_TAGS        tags to be used by logcat (see logcat --help)\n");
+        " $ANDROID_LOG_TAGS        tags to be used by logcat (see logcat --help)\n"
+        " $ADB_LOCAL_TRANSPORT_MAX_PORT max emulator scan port (default 5585, 16 emus)\n"
+    );
     // clang-format on
 }
 
@@ -293,7 +295,10 @@
                     callback->OnStderr(buffer_ptr, length);
                     break;
                 case ShellProtocol::kIdExit:
-                    exit_code = protocol->data()[0];
+                    // data() returns a char* which doesn't have defined signedness.
+                    // Cast to uint8_t to prevent 255 from being sign extended to INT_MIN,
+                    // which doesn't get truncated on Windows.
+                    exit_code = static_cast<uint8_t>(protocol->data()[0]);
                     continue;
                 default:
                     continue;
@@ -1003,7 +1008,8 @@
 #endif /* !defined(_WIN32) */
 }
 
-static bool wait_for_device(const char* service) {
+static bool wait_for_device(const char* service,
+                            std::optional<std::chrono::milliseconds> timeout = std::nullopt) {
     std::vector<std::string> components = android::base::Split(service, "-");
     if (components.size() < 3 || components.size() > 4) {
         fprintf(stderr, "adb: couldn't parse 'wait-for' command: %s\n", service);
@@ -1031,22 +1037,31 @@
     }
 
     if (components[3] != "any" && components[3] != "bootloader" && components[3] != "device" &&
-        components[3] != "recovery" && components[3] != "sideload") {
+        components[3] != "recovery" && components[3] != "sideload" &&
+        components[3] != "disconnect") {
         fprintf(stderr,
                 "adb: unknown state %s; "
-                "expected 'any', 'bootloader', 'device', 'recovery', or 'sideload'\n",
+                "expected 'any', 'bootloader', 'device', 'recovery', 'sideload', or 'disconnect'\n",
                 components[3].c_str());
         return false;
     }
 
     std::string cmd = format_host_command(android::base::Join(components, "-").c_str());
+    if (timeout) {
+        std::thread([timeout]() {
+            std::this_thread::sleep_for(*timeout);
+            fprintf(stderr, "timeout expired while waiting for device\n");
+            _exit(1);
+        }).detach();
+    }
     return adb_command(cmd);
 }
 
 static bool adb_root(const char* command) {
     std::string error;
 
-    unique_fd fd(adb_connect(android::base::StringPrintf("%s:", command), &error));
+    TransportId transport_id;
+    unique_fd fd(adb_connect(&transport_id, android::base::StringPrintf("%s:", command), &error));
     if (fd < 0) {
         fprintf(stderr, "adb: unable to connect for %s: %s\n", command, error.c_str());
         return false;
@@ -1079,9 +1094,22 @@
         return true;
     }
 
-    // Give adbd some time to kill itself and come back up.
-    // We can't use wait-for-device because devices (e.g. adb over network) might not come back.
-    std::this_thread::sleep_for(3s);
+    // Wait for the device to go away.
+    TransportType previous_type;
+    const char* previous_serial;
+    TransportId previous_id;
+    adb_get_transport(&previous_type, &previous_serial, &previous_id);
+
+    adb_set_transport(kTransportAny, nullptr, transport_id);
+    wait_for_device("wait-for-disconnect");
+
+    // Wait for the device to come back.
+    // If we were using a specific transport ID, there's nothing we can wait for.
+    if (previous_id == 0) {
+        adb_set_transport(previous_type, previous_serial, 0);
+        wait_for_device("wait-for-device", 3000ms);
+    }
+
     return true;
 }
 
@@ -1278,9 +1306,9 @@
     }
 }
 
-static int adb_connect_command(const std::string& command) {
+static int adb_connect_command(const std::string& command, TransportId* transport = nullptr) {
     std::string error;
-    unique_fd fd(adb_connect(command, &error));
+    unique_fd fd(adb_connect(transport, command, &error));
     if (fd < 0) {
         fprintf(stderr, "error: %s\n", error.c_str());
         return 1;
@@ -1369,9 +1397,9 @@
     TransportId transport_id = 0;
 
     while (argc > 0) {
-        if (!strcmp(argv[0],"server")) {
+        if (!strcmp(argv[0], "server")) {
             is_server = true;
-        } else if (!strcmp(argv[0],"nodaemon")) {
+        } else if (!strcmp(argv[0], "nodaemon")) {
             no_daemon = true;
         } else if (!strcmp(argv[0], "fork-server")) {
             /* this is a special flag used only when the ADB client launches the ADB Server */
@@ -1408,11 +1436,11 @@
             if (*id != '\0') {
                 error_exit("invalid transport id");
             }
-        } else if (!strcmp(argv[0],"-d")) {
+        } else if (!strcmp(argv[0], "-d")) {
             transport_type = kTransportUsb;
-        } else if (!strcmp(argv[0],"-e")) {
+        } else if (!strcmp(argv[0], "-e")) {
             transport_type = kTransportLocal;
-        } else if (!strcmp(argv[0],"-a")) {
+        } else if (!strcmp(argv[0], "-a")) {
             gListenAll = 1;
         } else if (!strncmp(argv[0], "-H", 2)) {
             if (argv[0][2] == '\0') {
@@ -1544,6 +1572,10 @@
         }
 
         std::string query = android::base::StringPrintf("host:%s%s", argv[0], listopt);
+        std::string error;
+        if (!adb_check_server_version(&error)) {
+            error_exit("failed to check server version: %s", error.c_str());
+        }
         printf("List of devices attached\n");
         return adb_query_command(query);
     }
diff --git a/adb/client/file_sync_client.cpp b/adb/client/file_sync_client.cpp
index b8827ef..7e470e1 100644
--- a/adb/client/file_sync_client.cpp
+++ b/adb/client/file_sync_client.cpp
@@ -681,9 +681,7 @@
     if (sync) {
         struct stat st;
         if (sync_lstat(sc, rpath, &st)) {
-            // For links, we cannot update the atime/mtime.
-            if ((S_ISREG(mode & st.st_mode) && st.st_mtime == static_cast<time_t>(mtime)) ||
-                (S_ISLNK(mode & st.st_mode) && st.st_mtime >= static_cast<time_t>(mtime))) {
+            if (st.st_mtime == static_cast<time_t>(mtime)) {
                 sc.RecordFilesSkipped(1);
                 return true;
             }
@@ -921,12 +919,8 @@
         for (copyinfo& ci : file_list) {
             struct stat st;
             if (sc.FinishStat(&st)) {
-                if (st.st_size == static_cast<off_t>(ci.size)) {
-                    // For links, we cannot update the atime/mtime.
-                    if ((S_ISREG(ci.mode & st.st_mode) && st.st_mtime == ci.time) ||
-                        (S_ISLNK(ci.mode & st.st_mode) && st.st_mtime >= ci.time)) {
-                        ci.skip = true;
-                    }
+                if (st.st_size == static_cast<off_t>(ci.size) && st.st_mtime == ci.time) {
+                    ci.skip = true;
                 }
             }
         }
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index 2ee81a9..0c5c28f 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -32,12 +32,16 @@
 
 #include "adb.h"
 #include "adb_auth.h"
+#include "adb_client.h"
 #include "adb_listeners.h"
 #include "adb_utils.h"
 #include "commandline.h"
 #include "sysdeps/chrono.h"
 #include "transport.h"
 
+const char** __adb_argv;
+const char** __adb_envp;
+
 static void setup_daemon_logging() {
     const std::string log_file_path(GetLogFilePath());
     int fd = unix_open(log_file_path, O_WRONLY | O_CREAT | O_APPEND, 0640);
@@ -191,13 +195,29 @@
         notify_thread.detach();
     }
 
+#if defined(__linux__)
+    // Write our location to .android/adb.$PORT, so that older clients can exec us.
+    std::string path;
+    if (!android::base::Readlink("/proc/self/exe", &path)) {
+        PLOG(ERROR) << "failed to readlink /proc/self/exe";
+    }
+
+    std::optional<std::string> server_executable_path = adb_get_server_executable_path();
+    if (server_executable_path) {
+      if (!android::base::WriteStringToFile(path, *server_executable_path)) {
+          PLOG(ERROR) << "failed to write server path to " << path;
+      }
+    }
+#endif
+
     D("Event loop starting");
     fdevent_loop();
-
     return 0;
 }
 
-int main(int argc, char** argv) {
+int main(int argc, char* argv[], char* envp[]) {
+    __adb_argv = const_cast<const char**>(argv);
+    __adb_envp = const_cast<const char**>(envp);
     adb_trace_init(argv);
     return adb_commandline(argc - 1, const_cast<const char**>(argv + 1));
 }
diff --git a/adb/daemon/abb.cpp b/adb/daemon/abb.cpp
index f69babe..eeac41a 100644
--- a/adb/daemon/abb.cpp
+++ b/adb/daemon/abb.cpp
@@ -14,14 +14,16 @@
  * limitations under the License.
  */
 
+#include <sys/wait.h>
+
+#include <android-base/cmsg.h>
+#include <cmd.h>
+
 #include "adb.h"
 #include "adb_io.h"
+#include "adb_utils.h"
 #include "shell_service.h"
 
-#include "cmd.h"
-
-#include <sys/wait.h>
-
 namespace {
 
 class AdbFdTextOutput : public android::TextOutput {
@@ -83,8 +85,18 @@
             break;
         }
 
-        auto result = StartCommandInProcess(std::move(data), &execCmd);
-        if (!SendFileDescriptor(fd, result)) {
+        std::string_view name = data;
+        auto protocol = SubprocessProtocol::kShell;
+        if (ConsumePrefix(&name, "abb:")) {
+            protocol = SubprocessProtocol::kShell;
+        } else if (ConsumePrefix(&name, "abb_exec:")) {
+            protocol = SubprocessProtocol::kNone;
+        } else {
+            LOG(FATAL) << "Unknown command prefix for abb: " << data;
+        }
+
+        unique_fd result = StartCommandInProcess(std::string(name), &execCmd, protocol);
+        if (android::base::SendFileDescriptors(fd, "", 1, result.get()) != 1) {
             PLOG(ERROR) << "Failed to send an inprocess fd for command: " << data;
             break;
         }
diff --git a/adb/daemon/abb_service.cpp b/adb/daemon/abb_service.cpp
index 817aea1..a435279 100644
--- a/adb/daemon/abb_service.cpp
+++ b/adb/daemon/abb_service.cpp
@@ -20,6 +20,8 @@
 #include "adb_utils.h"
 #include "shell_service.h"
 
+#include <android-base/cmsg.h>
+
 namespace {
 
 struct AbbProcess;
@@ -59,8 +61,9 @@
 
         unique_fd fd;
         std::string error;
-        if (!ReceiveFileDescriptor(socket_fd_, &fd, &error)) {
-            LOG(ERROR) << "failed to receive FD from abb: " << error;
+        char buf;
+        if (android::base::ReceiveFileDescriptors(socket_fd_, &buf, 1, &fd) != 1) {
+            PLOG(ERROR) << "failed to receive FD from abb";
             socket_fd_.reset();
             continue;
         }
@@ -83,6 +86,6 @@
 
 }  // namespace
 
-unique_fd execute_binder_command(std::string_view command) {
+unique_fd execute_abb_command(std::string_view command) {
     return abbp->sendCommand(command);
 }
diff --git a/adb/daemon/file_sync_service.cpp b/adb/daemon/file_sync_service.cpp
index 9e1760d..29bd798 100644
--- a/adb/daemon/file_sync_service.cpp
+++ b/adb/daemon/file_sync_service.cpp
@@ -25,8 +25,8 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/mount.h>
 #include <sys/stat.h>
+#include <sys/time.h>
 #include <sys/types.h>
 #include <unistd.h>
 #include <utime.h>
@@ -210,22 +210,6 @@
     return WriteFdExactly(s, &msg.dent, sizeof(msg.dent));
 }
 
-static bool is_mountpoint(const std::string& path, pid_t tid) {
-    const std::string mountinfo_path = "/proc/" + std::to_string(tid) + "/mountinfo";
-    std::string mountinfo;
-    if (!android::base::ReadFileToString(mountinfo_path, &mountinfo)) {
-        PLOG(ERROR) << "Failed to open " << mountinfo_path;
-        return false;
-    }
-    std::vector<std::string> lines = android::base::Split(mountinfo, "\n");
-    return std::find_if(lines.begin(), lines.end(), [&path](const auto& line) {
-               auto tokens = android::base::Split(line, " ");
-               // line format is ...
-               // mountid parentmountid major:minor sourcepath targetpath option ...
-               return tokens.size() >= 4 && tokens[4] == path;
-           }) != lines.end();
-}
-
 // Make sure that SendFail from adb_io.cpp isn't accidentally used in this file.
 #pragma GCC poison SendFail
 
@@ -242,10 +226,10 @@
     return SendSyncFail(fd, StringPrintf("%s: %s", reason.c_str(), strerror(errno)));
 }
 
-static bool handle_send_file(int s, const char* path, uid_t uid, gid_t gid, uint64_t capabilities,
-                             mode_t mode, std::vector<char>& buffer, bool do_unlink) {
+static bool handle_send_file(int s, const char* path, uint32_t* timestamp, uid_t uid, gid_t gid,
+                             uint64_t capabilities, mode_t mode, std::vector<char>& buffer,
+                             bool do_unlink) {
     syncmsg msg;
-    unsigned int timestamp = 0;
 
     __android_log_security_bswrite(SEC_TAG_ADB_SEND_FILE, path);
 
@@ -291,7 +275,7 @@
 
         if (msg.data.id != ID_DATA) {
             if (msg.data.id == ID_DONE) {
-                timestamp = msg.data.size;
+                *timestamp = msg.data.size;
                 break;
             }
             SendSyncFail(s, "invalid data message");
@@ -316,11 +300,6 @@
         goto fail;
     }
 
-    utimbuf u;
-    u.actime = timestamp;
-    u.modtime = timestamp;
-    utime(path, &u);
-
     msg.status.id = ID_OKAY;
     msg.status.msglen = 0;
     return WriteFdExactly(s, &msg.status, sizeof(msg.status));
@@ -360,9 +339,12 @@
 }
 
 #if defined(_WIN32)
-extern bool handle_send_link(int s, const std::string& path, std::vector<char>& buffer) __attribute__((error("no symlinks on Windows")));
+extern bool handle_send_link(int s, const std::string& path,
+                             uint32_t* timestamp, std::vector<char>& buffer)
+        __attribute__((error("no symlinks on Windows")));
 #else
-static bool handle_send_link(int s, const std::string& path, std::vector<char>& buffer) {
+static bool handle_send_link(int s, const std::string& path, uint32_t* timestamp,
+                             std::vector<char>& buffer) {
     syncmsg msg;
 
     if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) return false;
@@ -399,6 +381,7 @@
     if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) return false;
 
     if (msg.data.id == ID_DONE) {
+        *timestamp = msg.data.size;
         msg.status.id = ID_OKAY;
         msg.status.msglen = 0;
         if (!WriteFdExactly(s, &msg.status, sizeof(msg.status))) return false;
@@ -432,40 +415,44 @@
     struct stat st;
     bool do_unlink = (lstat(path.c_str(), &st) == -1) || S_ISREG(st.st_mode) ||
                      (S_ISLNK(st.st_mode) && !S_ISLNK(mode));
-
-    // If the path is a file that is a mount point, don't unlink it, but instead
-    // truncate to zero. If unlinked, existing mounts on the path is all
-    // unmounted
-    if (S_ISREG(st.st_mode) && is_mountpoint(path, getpid())) {
-        do_unlink = false;
-        if (truncate(path.c_str(), 0) == -1) {
-            SendSyncFail(s, "truncate to zero failed");
-            return false;
-        }
-    }
-
     if (do_unlink) {
         adb_unlink(path.c_str());
     }
 
+    bool result;
+    uint32_t timestamp;
     if (S_ISLNK(mode)) {
-        return handle_send_link(s, path.c_str(), buffer);
+        result = handle_send_link(s, path.c_str(), &timestamp, buffer);
+    } else {
+        // Copy user permission bits to "group" and "other" permissions.
+        mode &= 0777;
+        mode |= ((mode >> 3) & 0070);
+        mode |= ((mode >> 3) & 0007);
+
+        uid_t uid = -1;
+        gid_t gid = -1;
+        uint64_t capabilities = 0;
+        if (should_use_fs_config(path)) {
+            unsigned int broken_api_hack = mode;
+            fs_config(path.c_str(), 0, nullptr, &uid, &gid, &broken_api_hack, &capabilities);
+            mode = broken_api_hack;
+        }
+
+        result = handle_send_file(s, path.c_str(), &timestamp, uid, gid, capabilities, mode, buffer,
+                                  do_unlink);
     }
 
-    // Copy user permission bits to "group" and "other" permissions.
-    mode &= 0777;
-    mode |= ((mode >> 3) & 0070);
-    mode |= ((mode >> 3) & 0007);
-
-    uid_t uid = -1;
-    gid_t gid = -1;
-    uint64_t capabilities = 0;
-    if (should_use_fs_config(path)) {
-        unsigned int broken_api_hack = mode;
-        fs_config(path.c_str(), 0, nullptr, &uid, &gid, &broken_api_hack, &capabilities);
-        mode = broken_api_hack;
+    if (!result) {
+      return false;
     }
-    return handle_send_file(s, path.c_str(), uid, gid, capabilities, mode, buffer, do_unlink);
+
+    struct timeval tv[2];
+    tv[0].tv_sec = timestamp;
+    tv[0].tv_usec = 0;
+    tv[1].tv_sec = timestamp;
+    tv[1].tv_usec = 0;
+    lutimes(path.c_str(), tv);
+    return true;
 }
 
 static bool do_recv(int s, const char* path, std::vector<char>& buffer) {
@@ -575,64 +562,7 @@
     return true;
 }
 
-#if defined(__ANDROID__)
-class FileSyncPreparer {
-  public:
-    FileSyncPreparer() : saved_ns_fd_(-1), rooted_(getuid() == 0) {
-        const std::string namespace_path = "/proc/" + std::to_string(gettid()) + "/ns/mnt";
-        const int ns_fd = adb_open(namespace_path.c_str(), O_RDONLY | O_CLOEXEC);
-        if (ns_fd == -1) {
-            if (rooted_) PLOG(ERROR) << "Failed to save mount namespace";
-            return;
-        }
-        saved_ns_fd_.reset(ns_fd);
-
-        // Note: this is for the current thread only
-        if (unshare(CLONE_NEWNS) != 0) {
-            if (rooted_) PLOG(ERROR) << "Failed to clone mount namespace";
-            return;
-        }
-
-        // Set the propagation type of / to private so that unmount below is
-        // not propagated to other mount namespaces.
-        if (mount(nullptr, "/", nullptr, MS_PRIVATE | MS_REC, nullptr) == -1) {
-            if (rooted_) PLOG(ERROR) << "Could not change propagation type of / to MS_PRIVATE";
-            return;
-        }
-
-        // unmount /bionic which is bind-mount to itself by init. Under /bionic,
-        // there are other bind mounts for the bionic files. By unmounting this,
-        // we unmount them all thus revealing the raw file system that is the
-        // same as the local file system seen by the adb client.
-        if (umount2("/bionic", MNT_DETACH) == -1 && errno != ENOENT) {
-            if (rooted_) PLOG(ERROR) << "Could not unmount /bionic to reveal raw filesystem";
-            return;
-        }
-    }
-
-    ~FileSyncPreparer() {
-        if (saved_ns_fd_.get() != -1) {
-            // In fact, this is not strictly required because this thread for file
-            // sync service will be destroyed after the current transfer is all
-            // done. However, let's restore the ns in case the same thread is
-            // reused by multiple transfers in the future refactoring.
-            if (setns(saved_ns_fd_, CLONE_NEWNS) == -1) {
-                PLOG(ERROR) << "Failed to restore saved mount namespace";
-            }
-        }
-    }
-
-  private:
-    unique_fd saved_ns_fd_;
-    bool rooted_;
-};
-#endif
-
 void file_sync_service(unique_fd fd) {
-#if defined(__ANDROID__)
-    FileSyncPreparer preparer;
-#endif
-
     std::vector<char> buffer(SYNC_DATA_MAX);
 
     while (handle_sync_command(fd.get(), buffer)) {
diff --git a/adb/daemon/jdwp_service.cpp b/adb/daemon/jdwp_service.cpp
index 032ee42..66bfc0d 100644
--- a/adb/daemon/jdwp_service.cpp
+++ b/adb/daemon/jdwp_service.cpp
@@ -32,6 +32,8 @@
 #include <memory>
 #include <vector>
 
+#include <android-base/cmsg.h>
+
 #include "adb.h"
 #include "adb_io.h"
 #include "adb_unique_fd.h"
@@ -237,7 +239,7 @@
         CHECK(!proc->out_fds.empty());
 
         int fd = proc->out_fds.back().get();
-        if (!SendFileDescriptor(socket, fd)) {
+        if (android::base::SendFileDescriptors(socket, "", 1, fd) != 1) {
             D("sending new file descriptor to JDWP %d failed: %s", proc->pid, strerror(errno));
             goto CloseProcess;
         }
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index fce3a4f..e5a4917 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -58,17 +58,23 @@
 #if defined(__ANDROID__)
 static const char* root_seclabel = nullptr;
 
+static inline bool is_device_unlocked() {
+    return "orange" == android::base::GetProperty("ro.boot.verifiedbootstate", "");
+}
+
 static bool should_drop_capabilities_bounding_set() {
-#if defined(ALLOW_ADBD_ROOT)
-    if (__android_log_is_debuggable()) {
-        return false;
+    if (ALLOW_ADBD_ROOT || is_device_unlocked()) {
+        if (__android_log_is_debuggable()) {
+            return false;
+        }
     }
-#endif
     return true;
 }
 
 static bool should_drop_privileges() {
-#if defined(ALLOW_ADBD_ROOT)
+    // "adb root" not allowed, always drop privileges.
+    if (!ALLOW_ADBD_ROOT && !is_device_unlocked()) return true;
+
     // The properties that affect `adb root` and `adb unroot` are ro.secure and
     // ro.debuggable. In this context the names don't make the expected behavior
     // particularly obvious.
@@ -98,9 +104,6 @@
     }
 
     return drop;
-#else
-    return true; // "adb root" not allowed, always drop privileges.
-#endif // ALLOW_ADBD_ROOT
 }
 
 static void drop_privileges(int server_port) {
@@ -205,6 +208,10 @@
 #if defined(ALLOW_ADBD_NO_AUTH)
     // If ro.adb.secure is unset, default to no authentication required.
     auth_required = android::base::GetBoolProperty("ro.adb.secure", false);
+#elif defined(__ANDROID__)
+    if (is_device_unlocked()) {  // allows no authentication when the device is unlocked.
+        auth_required = android::base::GetBoolProperty("ro.adb.secure", false);
+    }
 #endif
 
     adbd_auth_init();
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index 5e6d416..ce494ee 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -14,339 +14,75 @@
  * limitations under the License.
  */
 
-#define TRACE_TAG ADB
-
-#include "sysdeps.h"
-
 #include <errno.h>
 #include <fcntl.h>
-#include <mntent.h>
-#include <spawn.h>
-#include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
-#include <sys/mount.h>
-#include <sys/statvfs.h>
-#include <sys/vfs.h>
+#include <sys/types.h>
+#include <sys/wait.h>
 #include <unistd.h>
 
-#include <memory>
-#include <set>
 #include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/properties.h>
-#include <bootloader_message/bootloader_message.h>
-#include <cutils/android_reboot.h>
-#include <fs_mgr.h>
-#include <fs_mgr_overlayfs.h>
 
 #include "adb.h"
 #include "adb_io.h"
 #include "adb_unique_fd.h"
-#include "adb_utils.h"
-#include "set_verity_enable_state_service.h"
 
-using android::base::Realpath;
-using android::fs_mgr::Fstab;
-using android::fs_mgr::ReadDefaultFstab;
+static constexpr char kRemountCmd[] = "/system/bin/remount";
 
-// Returns the last device used to mount a directory in /proc/mounts.
-// This will find overlayfs entry where upperdir=lowerdir, to make sure
-// remount is associated with the correct directory.
-static std::string find_proc_mount(const char* dir) {
-    std::unique_ptr<FILE, int(*)(FILE*)> fp(setmntent("/proc/mounts", "r"), endmntent);
-    std::string mnt_fsname;
-    if (!fp) return mnt_fsname;
-
-    // dir might be a symlink, e.g., /product -> /system/product in GSI.
-    std::string canonical_path;
-    if (!Realpath(dir, &canonical_path)) {
-        PLOG(ERROR) << "Realpath failed: " << dir;
-    }
-
-    mntent* e;
-    while ((e = getmntent(fp.get())) != nullptr) {
-        if (canonical_path == e->mnt_dir) {
-            mnt_fsname = e->mnt_fsname;
-        }
-    }
-    return mnt_fsname;
-}
-
-// Returns the device used to mount a directory in the fstab.
-static std::string find_fstab_mount(const char* dir) {
-    Fstab fstab;
-    if (!ReadDefaultFstab(&fstab)) {
-        return "";
-    }
-
-    auto entry = std::find_if(fstab.begin(), fstab.end(),
-                              [&dir](const auto& entry) { return entry.mount_point == dir; });
-    if (entry == fstab.end()) {
-        return "";
-    }
-    if (entry->fs_mgr_flags.logical) {
-        fs_mgr_update_logical_partition(&(*entry));
-    }
-    return entry->blk_device;
-}
-
-// The proc entry for / is full of lies, so check fstab instead.
-// /proc/mounts lists rootfs and /dev/root, neither of which is what we want.
-static std::string find_mount(const char* dir, bool is_root) {
-    if (is_root) {
-        return find_fstab_mount(dir);
-    } else {
-        return find_proc_mount(dir);
-    }
-}
-
-bool dev_is_overlayfs(const std::string& dev) {
-    return (dev == "overlay") || (dev == "overlayfs");
-}
-
-bool make_block_device_writable(const std::string& dev) {
-    if (dev_is_overlayfs(dev)) return true;
-    int fd = unix_open(dev, O_RDONLY | O_CLOEXEC);
-    if (fd == -1) {
+static bool do_remount(int fd, const std::string& cmd) {
+    if (getuid() != 0) {
+        WriteFdExactly(fd, "Not running as root. Try \"adb root\" first.\n");
         return false;
     }
 
-    int OFF = 0;
-    bool result = (ioctl(fd, BLKROSET, &OFF) != -1);
-    unix_close(fd);
-    return result;
-}
-
-static bool can_unshare_blocks(int fd, const char* dev) {
-    const char* E2FSCK_BIN = "/system/bin/e2fsck";
-    if (access(E2FSCK_BIN, X_OK)) {
-        WriteFdFmt(fd, "e2fsck is not available, cannot undo deduplication on %s\n", dev);
+    auto pid = fork();
+    if (pid < 0) {
+        WriteFdFmt(fd, "Failed to fork to %s: %s\n", kRemountCmd, strerror(errno));
         return false;
     }
 
-    pid_t child;
-    char* env[] = {nullptr};
-    const char* argv[] = {E2FSCK_BIN, "-n", "-E", "unshare_blocks", dev, nullptr};
-    if (posix_spawn(&child, E2FSCK_BIN, nullptr, nullptr, const_cast<char**>(argv), env)) {
-        WriteFdFmt(fd, "failed to e2fsck to check deduplication: %s\n", strerror(errno));
+    if (pid == 0) {
+        // child side of the fork
+        dup2(fd, STDIN_FILENO);
+        dup2(fd, STDOUT_FILENO);
+        dup2(fd, STDERR_FILENO);
+
+        execl(kRemountCmd, kRemountCmd, cmd.empty() ? nullptr : cmd.c_str(), nullptr);
+        _exit(errno);
+    }
+
+    int wstatus = 0;
+    auto ret = waitpid(pid, &wstatus, 0);
+
+    if (ret == -1) {
+        WriteFdFmt(fd, "Failed to wait for %s: %s\n", kRemountCmd, strerror(errno));
+        return false;
+    } else if (ret != pid) {
+        WriteFdFmt(fd, "pid %d and waitpid return %d do not match for %s\n",
+                   static_cast<int>(pid), static_cast<int>(ret), kRemountCmd);
         return false;
     }
-    int status = 0;
-    int ret = TEMP_FAILURE_RETRY(waitpid(child, &status, 0));
-    if (ret < 0) {
-        WriteFdFmt(fd, "failed to get e2fsck status: %s\n", strerror(errno));
+
+    if (WIFSIGNALED(wstatus)) {
+        WriteFdFmt(fd, "%s terminated with signal %s\n", kRemountCmd,
+                   strsignal(WTERMSIG(wstatus)));
         return false;
     }
-    if (!WIFEXITED(status)) {
-        WriteFdFmt(fd, "e2fsck exited abnormally with status %d\n", status);
+
+    if (!WIFEXITED(wstatus)) {
+        WriteFdFmt(fd, "%s stopped with status 0x%x\n", kRemountCmd, wstatus);
         return false;
     }
-    int rc = WEXITSTATUS(status);
-    if (rc != 0) {
-        WriteFdFmt(fd,
-                   "%s is deduplicated, and an e2fsck check failed. It might not "
-                   "have enough free-space to be remounted as writable.\n",
-                   dev);
+
+    if (WEXITSTATUS(wstatus)) {
+        WriteFdFmt(fd, "%s exited with status %d\n", kRemountCmd, WEXITSTATUS(wstatus));
         return false;
     }
+
     return true;
 }
 
-static unsigned long get_mount_flags(int fd, const char* dir) {
-    struct statvfs st_vfs;
-    if (statvfs(dir, &st_vfs) == -1) {
-        // Even though we could not get the original mount flags, assume that
-        // the mount was originally read-only.
-        WriteFdFmt(fd, "statvfs of the %s mount failed: %s.\n", dir, strerror(errno));
-        return MS_RDONLY;
-    }
-    return st_vfs.f_flag;
-}
-
-static bool remount_partition(int fd, const char* dir) {
-    if (!directory_exists(dir)) {
-        return true;
-    }
-    bool is_root = strcmp(dir, "/") == 0;
-    if (is_root && dev_is_overlayfs(find_mount("/system", false))) {
-        dir = "/system";
-        is_root = false;
-    }
-    std::string dev = find_mount(dir, is_root);
-    if (is_root && dev.empty()) {
-        // The fstab entry will be /system if the device switched roots during
-        // first-stage init.
-        dev = find_mount("/system", true);
-    }
-    // Even if the device for the root is not found, we still try to remount it
-    // as rw. This typically only happens when running Android in a container:
-    // the root will almost always be in a loop device, which is dynamic, so
-    // it's not convenient to put in the fstab.
-    if (dev.empty() && !is_root) {
-        return true;
-    }
-    if (!dev.empty() && !make_block_device_writable(dev)) {
-        WriteFdFmt(fd, "remount of %s failed; couldn't make block device %s writable: %s\n",
-                   dir, dev.c_str(), strerror(errno));
-        return false;
-    }
-
-    unsigned long remount_flags = get_mount_flags(fd, dir);
-    remount_flags &= ~MS_RDONLY;
-    remount_flags |= MS_REMOUNT;
-
-    if (mount(dev.c_str(), dir, "none", remount_flags | MS_BIND, nullptr) == -1) {
-        // This is useful for cases where the superblock is already marked as
-        // read-write, but the mount itself is read-only, such as containers
-        // where the remount with just MS_REMOUNT is forbidden by the kernel.
-        WriteFdFmt(fd, "remount of the %s mount failed: %s.\n", dir, strerror(errno));
-        return false;
-    }
-    if (mount(dev.c_str(), dir, "none", MS_REMOUNT, nullptr) == -1) {
-        WriteFdFmt(fd, "remount of the %s superblock failed: %s\n", dir, strerror(errno));
-        return false;
-    }
-    return true;
-}
-
-static void reboot_for_remount(int fd, bool need_fsck) {
-    std::string reboot_cmd = "reboot";
-    if (need_fsck) {
-        const std::vector<std::string> options = {"--fsck_unshare_blocks"};
-        std::string err;
-        if (!write_bootloader_message(options, &err)) {
-            WriteFdFmt(fd, "Failed to set bootloader message: %s\n", err.c_str());
-            return;
-        }
-
-        WriteFdExactly(fd,
-                       "The device will now reboot to recovery and attempt "
-                       "un-deduplication.\n");
-        reboot_cmd = "reboot,recovery";
-    }
-
-    sync();
-    android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_cmd.c_str());
-}
-
-static void try_unmount_bionic(int fd) {
-    static constexpr const char* kBionic = "/bionic";
-    struct statfs buf;
-    if (statfs(kBionic, &buf) == -1) {
-        WriteFdFmt(fd, "statfs of the %s mount failed: %s.\n", kBionic, strerror(errno));
-        return;
-    }
-    if (buf.f_flags & ST_RDONLY) {
-        // /bionic is on a read-only partition; can happen for
-        // non-system-as-root-devices. Don' try to unmount.
-        return;
-    }
-    // Success/Fail of the actual remount will be reported by the function.
-    remount_partition(fd, kBionic);
-    return;
-}
-
 void remount_service(unique_fd fd, const std::string& cmd) {
-    bool user_requested_reboot = cmd == "-R";
-
-    if (getuid() != 0) {
-        WriteFdExactly(fd.get(), "Not running as root. Try \"adb root\" first.\n");
-        return;
-    }
-
-    bool system_verified = !(android::base::GetProperty("partition.system.verified", "").empty());
-    bool vendor_verified = !(android::base::GetProperty("partition.vendor.verified", "").empty());
-
-    std::vector<std::string> partitions{"/",        "/odm",   "/oem", "/product_services",
-                                        "/product", "/vendor"};
-
-    if (system_verified || vendor_verified) {
-        // Disable verity automatically (reboot will be required).
-        set_verity_enabled_state_service(unique_fd(dup(fd.get())), false);
-
-        // If overlayfs is not supported, we try and remount or set up
-        // un-deduplication. If it is supported, we can go ahead and wait for
-        // a reboot.
-        if (fs_mgr_overlayfs_valid() != OverlayfsValidResult::kNotSupported) {
-            if (user_requested_reboot) {
-                if (android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot")) {
-                    WriteFdExactly(fd.get(), "rebooting device\n");
-                } else {
-                    WriteFdExactly(fd.get(), "reboot failed\n");
-                }
-            }
-            return;
-        }
-    } else if (fs_mgr_overlayfs_setup()) {
-        // If we can use overlayfs, lets get it in place first before we
-        // struggle with determining deduplication operations.
-        Fstab fstab;
-        if (ReadDefaultFstab(&fstab) && fs_mgr_overlayfs_mount_all(&fstab)) {
-            WriteFdExactly(fd.get(), "overlayfs mounted\n");
-        }
-    }
-
-    // If overlayfs is supported, we don't bother trying to un-deduplicate
-    // partitions.
-    std::set<std::string> dedup;
-    if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
-        // Find partitions that are deduplicated, and can be un-deduplicated.
-        for (const auto& part : partitions) {
-            auto partition = part;
-            if ((part == "/") && !find_mount("/system", false).empty()) partition = "/system";
-            std::string dev = find_mount(partition.c_str(), partition == "/");
-            if (dev.empty() || !fs_mgr_has_shared_blocks(partition, dev)) {
-                continue;
-            }
-            if (can_unshare_blocks(fd.get(), dev.c_str())) {
-                dedup.emplace(partition);
-            }
-        }
-
-        // Reboot now if the user requested it (and an operation needs a reboot).
-        if (user_requested_reboot) {
-            if (!dedup.empty()) {
-                reboot_for_remount(fd.get(), !dedup.empty());
-                return;
-            }
-            WriteFdExactly(fd.get(), "No reboot needed, skipping -R.\n");
-        }
-    }
-
-    bool success = true;
-    for (const auto& partition : partitions) {
-        // Don't try to remount partitions that need an fsck in recovery.
-        if (dedup.count(partition)) {
-            continue;
-        }
-        success &= remount_partition(fd.get(), partition.c_str());
-    }
-
-    if (!dedup.empty()) {
-        WriteFdExactly(fd.get(),
-                       "The following partitions are deduplicated and cannot "
-                       "yet be remounted:\n");
-        for (const std::string& name : dedup) {
-            WriteFdFmt(fd.get(), "  %s\n", name.c_str());
-        }
-
-        WriteFdExactly(fd.get(),
-                       "To reboot and un-deduplicate the listed partitions, "
-                       "please retry with adb remount -R.\n");
-        if (system_verified || vendor_verified) {
-            WriteFdExactly(fd.get(), "Note: verity will be automatically disabled after reboot.\n");
-        }
-        return;
-    }
-
-    try_unmount_bionic(fd.get());
-
-    if (!success) {
-        WriteFdExactly(fd.get(), "remount failed\n");
-    } else {
-        WriteFdExactly(fd.get(), "remount succeeded\n");
-    }
+    const char* success = do_remount(fd.get(), cmd) ? "succeeded" : "failed";
+    WriteFdFmt(fd.get(), "remount %s\n", success);
 }
diff --git a/adb/daemon/remount_service.h b/adb/daemon/remount_service.h
index c847403..522a5da 100644
--- a/adb/daemon/remount_service.h
+++ b/adb/daemon/remount_service.h
@@ -21,6 +21,5 @@
 #include "adb_unique_fd.h"
 
 #if defined(__ANDROID__)
-bool make_block_device_writable(const std::string&);
 void remount_service(unique_fd, const std::string&);
 #endif
diff --git a/adb/daemon/restart_service.cpp b/adb/daemon/restart_service.cpp
index 6803d93..16d2627 100644
--- a/adb/daemon/restart_service.cpp
+++ b/adb/daemon/restart_service.cpp
@@ -20,6 +20,7 @@
 
 #include <unistd.h>
 
+#include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <log/log_properties.h>
@@ -37,6 +38,7 @@
         return;
     }
 
+    LOG(INFO) << "adbd restarting as root";
     android::base::SetProperty("service.adb.root", "1");
     WriteFdExactly(fd.get(), "restarting adbd as root\n");
 }
@@ -46,6 +48,8 @@
         WriteFdExactly(fd.get(), "adbd not running as root\n");
         return;
     }
+
+    LOG(INFO) << "adbd restarting as nonroot";
     android::base::SetProperty("service.adb.root", "0");
     WriteFdExactly(fd.get(), "restarting adbd as non root\n");
 }
@@ -56,11 +60,13 @@
         return;
     }
 
+    LOG(INFO) << "adbd restarting in TCP mode (port = " << port << ")";
     android::base::SetProperty("service.adb.tcp.port", android::base::StringPrintf("%d", port));
     WriteFdFmt(fd.get(), "restarting in TCP mode port: %d\n", port);
 }
 
 void restart_usb_service(unique_fd fd) {
+    LOG(INFO) << "adbd restarting in USB mode";
     android::base::SetProperty("service.adb.tcp.port", "0");
     WriteFdExactly(fd.get(), "restarting in USB mode\n");
 }
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index d1f0345..b0cc450 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -223,17 +223,15 @@
         return create_jdwp_service_socket();
     } else if (name == "track-jdwp") {
         return create_jdwp_tracker_service_socket();
-    } else if (name.starts_with("sink:")) {
-        name.remove_prefix(strlen("sink:"));
+    } else if (ConsumePrefix(&name, "sink:")) {
         uint64_t byte_count = 0;
-        if (!android::base::ParseUint(name.data(), &byte_count)) {
+        if (!ParseUint(&byte_count, name)) {
             return nullptr;
         }
         return new SinkSocket(byte_count);
-    } else if (name.starts_with("source:")) {
-        name.remove_prefix(strlen("source:"));
+    } else if (ConsumePrefix(&name, "source:")) {
         uint64_t byte_count = 0;
-        if (!android::base::ParseUint(name.data(), &byte_count)) {
+        if (!ParseUint(&byte_count, name)) {
             return nullptr;
         }
         return new SourceSocket(byte_count);
@@ -244,29 +242,27 @@
 
 unique_fd daemon_service_to_fd(std::string_view name, atransport* transport) {
 #if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
-    if (name.starts_with("abb:")) {
-        name.remove_prefix(strlen("abb:"));
-        return execute_binder_command(name);
+    if (name.starts_with("abb:") || name.starts_with("abb_exec:")) {
+        return execute_abb_command(name);
     }
 #endif
 
 #if defined(__ANDROID__)
     if (name.starts_with("framebuffer:")) {
         return create_service_thread("fb", framebuffer_service);
-    } else if (name.starts_with("remount:")) {
-        std::string arg(name.begin() + strlen("remount:"), name.end());
+    } else if (ConsumePrefix(&name, "remount:")) {
+        std::string arg(name);
         return create_service_thread("remount",
                                      std::bind(remount_service, std::placeholders::_1, arg));
-    } else if (name.starts_with("reboot:")) {
-        std::string arg(name.begin() + strlen("reboot:"), name.end());
+    } else if (ConsumePrefix(&name, "reboot:")) {
+        std::string arg(name);
         return create_service_thread("reboot",
                                      std::bind(reboot_service, std::placeholders::_1, arg));
     } else if (name.starts_with("root:")) {
         return create_service_thread("root", restart_root_service);
     } else if (name.starts_with("unroot:")) {
         return create_service_thread("unroot", restart_unroot_service);
-    } else if (name.starts_with("backup:")) {
-        name.remove_prefix(strlen("backup:"));
+    } else if (ConsumePrefix(&name, "backup:")) {
         std::string cmd = "/system/bin/bu backup ";
         cmd += name;
         return StartSubprocess(cmd, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
@@ -279,8 +275,7 @@
     } else if (name.starts_with("enable-verity:")) {
         return create_service_thread("verity-off", std::bind(set_verity_enabled_state_service,
                                                              std::placeholders::_1, true));
-    } else if (name.starts_with("tcpip:")) {
-        name.remove_prefix(strlen("tcpip:"));
+    } else if (ConsumePrefix(&name, "tcpip:")) {
         std::string str(name);
 
         int port;
@@ -294,24 +289,22 @@
     }
 #endif
 
-    if (name.starts_with("dev:")) {
-        name.remove_prefix(strlen("dev:"));
+    if (ConsumePrefix(&name, "dev:")) {
         return unique_fd{unix_open(name, O_RDWR | O_CLOEXEC)};
-    } else if (name.starts_with("jdwp:")) {
-        name.remove_prefix(strlen("jdwp:"));
-        std::string str(name);
-        return create_jdwp_connection_fd(atoi(str.c_str()));
-    } else if (name.starts_with("shell")) {
-        name.remove_prefix(strlen("shell"));
+    } else if (ConsumePrefix(&name, "jdwp:")) {
+        pid_t pid;
+        if (!ParseUint(&pid, name)) {
+            return unique_fd{};
+        }
+        return create_jdwp_connection_fd(pid);
+    } else if (ConsumePrefix(&name, "shell")) {
         return ShellService(name, transport);
-    } else if (name.starts_with("exec:")) {
-        name.remove_prefix(strlen("exec:"));
+    } else if (ConsumePrefix(&name, "exec:")) {
         return StartSubprocess(std::string(name), nullptr, SubprocessType::kRaw,
                                SubprocessProtocol::kNone);
     } else if (name.starts_with("sync:")) {
         return create_service_thread("sync", file_sync_service);
-    } else if (name.starts_with("reverse:")) {
-        name.remove_prefix(strlen("reverse:"));
+    } else if (ConsumePrefix(&name, "reverse:")) {
         return reverse_service(name, transport);
     } else if (name == "reconnect") {
         return create_service_thread(
diff --git a/adb/daemon/set_verity_enable_state_service.cpp b/adb/daemon/set_verity_enable_state_service.cpp
index 92851c0..889229f 100644
--- a/adb/daemon/set_verity_enable_state_service.cpp
+++ b/adb/daemon/set_verity_enable_state_service.cpp
@@ -25,6 +25,7 @@
 #include <libavb_user/libavb_user.h>
 #include <stdarg.h>
 #include <stdio.h>
+#include <sys/mount.h>
 #include <sys/stat.h>
 
 #include <android-base/properties.h>
@@ -37,7 +38,6 @@
 #include "adb.h"
 #include "adb_io.h"
 #include "adb_unique_fd.h"
-#include "remount_service.h"
 
 #include "fec/io.h"
 
@@ -51,6 +51,17 @@
     if (getuid() != 0) WriteFdExactly(fd, "Maybe run adb root?\n");
 }
 
+static bool make_block_device_writable(const std::string& dev) {
+    unique_fd fd(unix_open(dev, O_RDONLY | O_CLOEXEC));
+    if (fd == -1) {
+        return false;
+    }
+
+    int OFF = 0;
+    bool result = (ioctl(fd, BLKROSET, &OFF) != -1);
+    return result;
+}
+
 /* Turn verity on/off */
 static bool set_verity_enabled_state(int fd, const char* block_device, const char* mount_point,
                                      bool enable) {
diff --git a/adb/daemon/shell_service.cpp b/adb/daemon/shell_service.cpp
index 0794bcd..e9d9c63 100644
--- a/adb/daemon/shell_service.cpp
+++ b/adb/daemon/shell_service.cpp
@@ -170,6 +170,8 @@
     // Opens the file at |pts_name|.
     int OpenPtyChildFd(const char* pts_name, unique_fd* error_sfd);
 
+    bool ConnectProtocolEndpoints(std::string* _Nonnull error);
+
     static void ThreadHandler(void* userdata);
     void PassDataStreams();
     void WaitForExit();
@@ -383,42 +385,9 @@
     }
 
     D("subprocess parent: exec completed");
-    if (protocol_ == SubprocessProtocol::kNone) {
-        // No protocol: all streams pass through the stdinout FD and hook
-        // directly into the local socket for raw data transfer.
-        local_socket_sfd_.reset(stdinout_sfd_.release());
-    } else {
-        // Shell protocol: create another socketpair to intercept data.
-        if (!CreateSocketpair(&protocol_sfd_, &local_socket_sfd_)) {
-            *error = android::base::StringPrintf(
-                "failed to create socketpair to intercept data: %s", strerror(errno));
-            kill(pid_, SIGKILL);
-            return false;
-        }
-        D("protocol FD = %d", protocol_sfd_.get());
-
-        input_ = std::make_unique<ShellProtocol>(protocol_sfd_);
-        output_ = std::make_unique<ShellProtocol>(protocol_sfd_);
-        if (!input_ || !output_) {
-            *error = "failed to allocate shell protocol objects";
-            kill(pid_, SIGKILL);
-            return false;
-        }
-
-        // Don't let reads/writes to the subprocess block our thread. This isn't
-        // likely but could happen under unusual circumstances, such as if we
-        // write a ton of data to stdin but the subprocess never reads it and
-        // the pipe fills up.
-        for (int fd : {stdinout_sfd_.get(), stderr_sfd_.get()}) {
-            if (fd >= 0) {
-                if (!set_file_block_mode(fd, false)) {
-                    *error = android::base::StringPrintf(
-                        "failed to set non-blocking mode for fd %d", fd);
-                    kill(pid_, SIGKILL);
-                    return false;
-                }
-            }
-        }
+    if (!ConnectProtocolEndpoints(error)) {
+        kill(pid_, SIGKILL);
+        return false;
     }
 
     D("subprocess parent: completed");
@@ -429,7 +398,6 @@
     unique_fd child_stdinout_sfd, child_stderr_sfd;
 
     CHECK(type_ == SubprocessType::kRaw);
-    CHECK(protocol_ == SubprocessProtocol::kShell);
 
     __android_log_security_bswrite(SEC_TAG_ADB_SHELL_CMD, command_.c_str());
 
@@ -448,34 +416,9 @@
     D("execinprocess: stdin/stdout FD = %d, stderr FD = %d", stdinout_sfd_.get(),
       stderr_sfd_.get());
 
-    // Required for shell protocol: create another socketpair to intercept data.
-    if (!CreateSocketpair(&protocol_sfd_, &local_socket_sfd_)) {
-        *error = android::base::StringPrintf("failed to create socketpair to intercept data: %s",
-                                             strerror(errno));
+    if (!ConnectProtocolEndpoints(error)) {
         return false;
     }
-    D("protocol FD = %d", protocol_sfd_.get());
-
-    input_ = std::make_unique<ShellProtocol>(protocol_sfd_);
-    output_ = std::make_unique<ShellProtocol>(protocol_sfd_);
-    if (!input_ || !output_) {
-        *error = "failed to allocate shell protocol objects";
-        return false;
-    }
-
-    // Don't let reads/writes to the subprocess block our thread. This isn't
-    // likely but could happen under unusual circumstances, such as if we
-    // write a ton of data to stdin but the subprocess never reads it and
-    // the pipe fills up.
-    for (int fd : {stdinout_sfd_.get(), stderr_sfd_.get()}) {
-        if (fd >= 0) {
-            if (!set_file_block_mode(fd, false)) {
-                *error = android::base::StringPrintf("failed to set non-blocking mode for fd %d",
-                                                     fd);
-                return false;
-            }
-        }
-    }
 
     std::thread([inout_sfd = std::move(child_stdinout_sfd), err_sfd = std::move(child_stderr_sfd),
                  command = std::move(command),
@@ -486,6 +429,45 @@
     return true;
 }
 
+bool Subprocess::ConnectProtocolEndpoints(std::string* _Nonnull error) {
+    if (protocol_ == SubprocessProtocol::kNone) {
+        // No protocol: all streams pass through the stdinout FD and hook
+        // directly into the local socket for raw data transfer.
+        local_socket_sfd_.reset(stdinout_sfd_.release());
+    } else {
+        // Required for shell protocol: create another socketpair to intercept data.
+        if (!CreateSocketpair(&protocol_sfd_, &local_socket_sfd_)) {
+            *error = android::base::StringPrintf(
+                    "failed to create socketpair to intercept data: %s", strerror(errno));
+            return false;
+        }
+        D("protocol FD = %d", protocol_sfd_.get());
+
+        input_ = std::make_unique<ShellProtocol>(protocol_sfd_);
+        output_ = std::make_unique<ShellProtocol>(protocol_sfd_);
+        if (!input_ || !output_) {
+            *error = "failed to allocate shell protocol objects";
+            return false;
+        }
+
+        // Don't let reads/writes to the subprocess block our thread. This isn't
+        // likely but could happen under unusual circumstances, such as if we
+        // write a ton of data to stdin but the subprocess never reads it and
+        // the pipe fills up.
+        for (int fd : {stdinout_sfd_.get(), stderr_sfd_.get()}) {
+            if (fd >= 0) {
+                if (!set_file_block_mode(fd, false)) {
+                    *error = android::base::StringPrintf(
+                            "failed to set non-blocking mode for fd %d", fd);
+                    return false;
+                }
+            }
+        }
+    }
+
+    return true;
+}
+
 bool Subprocess::StartThread(std::unique_ptr<Subprocess> subprocess, std::string* error) {
     Subprocess* raw = subprocess.release();
     std::thread(ThreadHandler, raw).detach();
@@ -863,12 +845,11 @@
     return local_socket;
 }
 
-unique_fd StartCommandInProcess(std::string name, Command command) {
+unique_fd StartCommandInProcess(std::string name, Command command, SubprocessProtocol protocol) {
     LOG(INFO) << "StartCommandInProcess(" << dump_hex(name.data(), name.size()) << ")";
 
     constexpr auto terminal_type = "";
     constexpr auto type = SubprocessType::kRaw;
-    constexpr auto protocol = SubprocessProtocol::kShell;
     constexpr auto make_pty_raw = false;
 
     auto subprocess = std::make_unique<Subprocess>(std::move(name), terminal_type, type, protocol,
diff --git a/adb/daemon/shell_service.h b/adb/daemon/shell_service.h
index fc66377..3abd958 100644
--- a/adb/daemon/shell_service.h
+++ b/adb/daemon/shell_service.h
@@ -49,7 +49,7 @@
 //
 // Returns an open FD connected to the thread or -1 on failure.
 using Command = int(std::string_view args, int in, int out, int err);
-unique_fd StartCommandInProcess(std::string name, Command command);
+unique_fd StartCommandInProcess(std::string name, Command command, SubprocessProtocol protocol);
 
 // Create a pipe containing the error.
 unique_fd ReportError(SubprocessProtocol protocol, const std::string& message);
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index f603d13..b42236e 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -29,6 +29,7 @@
 #include <linux/usb/functionfs.h>
 #include <sys/eventfd.h>
 
+#include <algorithm>
 #include <array>
 #include <future>
 #include <memory>
@@ -53,10 +54,14 @@
 
 using android::base::StringPrintf;
 
-static constexpr size_t kUsbReadQueueDepth = 16;
-static constexpr size_t kUsbReadSize = 16384;
+// We can't find out whether we have support for AIO on ffs endpoints until we submit a read.
+static std::optional<bool> gFfsAioSupported;
 
-static constexpr size_t kUsbWriteQueueDepth = 16;
+static constexpr size_t kUsbReadQueueDepth = 32;
+static constexpr size_t kUsbReadSize = 8 * PAGE_SIZE;
+
+static constexpr size_t kUsbWriteQueueDepth = 32;
+static constexpr size_t kUsbWriteSize = 8 * PAGE_SIZE;
 
 static const char* to_string(enum usb_functionfs_event_type type) {
     switch (type) {
@@ -110,9 +115,9 @@
 };
 
 struct IoBlock {
-    bool pending;
+    bool pending = false;
     struct iocb control;
-    Block payload;
+    std::shared_ptr<Block> payload;
 
     TransferId id() const { return TransferId::from_value(control.aio_data); }
 };
@@ -169,8 +174,13 @@
           read_fd_(std::move(read)),
           write_fd_(std::move(write)) {
         LOG(INFO) << "UsbFfsConnection constructed";
-        event_fd_.reset(eventfd(0, EFD_CLOEXEC));
-        if (event_fd_ == -1) {
+        worker_event_fd_.reset(eventfd(0, EFD_CLOEXEC));
+        if (worker_event_fd_ == -1) {
+            PLOG(FATAL) << "failed to create eventfd";
+        }
+
+        monitor_event_fd_.reset(eventfd(0, EFD_CLOEXEC));
+        if (monitor_event_fd_ == -1) {
             PLOG(FATAL) << "failed to create eventfd";
         }
 
@@ -181,6 +191,13 @@
         LOG(INFO) << "UsbFfsConnection being destroyed";
         Stop();
         monitor_thread_.join();
+
+        // We need to explicitly close our file descriptors before we notify our destruction,
+        // because the thread listening on the future will immediately try to reopen the endpoint.
+        control_fd_.reset();
+        read_fd_.reset();
+        write_fd_.reset();
+
         destruction_notifier_.set_value();
     }
 
@@ -192,8 +209,20 @@
         std::lock_guard<std::mutex> lock(write_mutex_);
         write_requests_.push_back(CreateWriteBlock(std::move(header), next_write_id_++));
         if (!packet->payload.empty()) {
-            write_requests_.push_back(
-                    CreateWriteBlock(std::move(packet->payload), next_write_id_++));
+            // The kernel attempts to allocate a contiguous block of memory for each write,
+            // which can fail if the write is large and the kernel heap is fragmented.
+            // Split large writes into smaller chunks to avoid this.
+            std::shared_ptr<Block> payload = std::make_shared<Block>(std::move(packet->payload));
+            size_t offset = 0;
+            size_t len = payload->size();
+
+            while (len > 0) {
+                size_t write_size = std::min(kUsbWriteSize, len);
+                write_requests_.push_back(
+                        CreateWriteBlock(payload, offset, write_size, next_write_id_++));
+                len -= write_size;
+                offset += write_size;
+            }
         }
         SubmitWrites();
         return true;
@@ -207,11 +236,18 @@
         }
         stopped_ = true;
         uint64_t notify = 1;
-        ssize_t rc = adb_write(event_fd_.get(), &notify, sizeof(notify));
+        ssize_t rc = adb_write(worker_event_fd_.get(), &notify, sizeof(notify));
         if (rc < 0) {
-            PLOG(FATAL) << "failed to notify eventfd to stop UsbFfsConnection";
+            PLOG(FATAL) << "failed to notify worker eventfd to stop UsbFfsConnection";
         }
         CHECK_EQ(static_cast<size_t>(rc), sizeof(notify));
+
+        rc = adb_write(monitor_event_fd_.get(), &notify, sizeof(notify));
+        if (rc < 0) {
+            PLOG(FATAL) << "failed to notify monitor eventfd to stop UsbFfsConnection";
+        }
+
+        CHECK_EQ(static_cast<size_t>(rc), sizeof(notify));
     }
 
   private:
@@ -224,7 +260,6 @@
         // until it dies, and then report failure to the transport via HandleError, which will
         // eventually result in the transport being destroyed, which will result in UsbFfsConnection
         // being destroyed, which unblocks the open thread and restarts this entire process.
-        static constexpr int kInterruptionSignal = SIGUSR1;
         static std::once_flag handler_once;
         std::call_once(handler_once, []() { signal(kInterruptionSignal, [](int) {}); });
 
@@ -235,22 +270,20 @@
             bool started = false;
             bool running = true;
             while (running) {
-                if (!bound || !started) {
-                    adb_pollfd pfd = {.fd = control_fd_.get(), .events = POLLIN, .revents = 0};
-                    int rc = TEMP_FAILURE_RETRY(adb_poll(&pfd, 1, 5000 /*ms*/));
-                    if (rc == -1) {
-                        PLOG(FATAL) << "poll on USB control fd failed";
-                    } else if (rc == 0) {
-                        // Something in the kernel presumably went wrong.
-                        // Close our endpoints, wait for a bit, and then try again.
-                        aio_context_.reset();
-                        read_fd_.reset();
-                        write_fd_.reset();
-                        control_fd_.reset();
-                        std::this_thread::sleep_for(5s);
-                        HandleError("didn't receive FUNCTIONFS_ENABLE, retrying");
-                        return;
-                    }
+                adb_pollfd pfd[2] = {
+                  { .fd = control_fd_.get(), .events = POLLIN, .revents = 0 },
+                  { .fd = monitor_event_fd_.get(), .events = POLLIN, .revents = 0 },
+                };
+                int rc = TEMP_FAILURE_RETRY(adb_poll(pfd, 2, -1));
+                if (rc == -1) {
+                    PLOG(FATAL) << "poll on USB control fd failed";
+                } else if (rc == 0) {
+                    LOG(FATAL) << "poll on USB control fd returned 0";
+                }
+
+                if (pfd[1].revents) {
+                    // We were told to die.
+                    break;
                 }
 
                 struct usb_functionfs_event event;
@@ -264,7 +297,7 @@
 
                 switch (event.type) {
                     case FUNCTIONFS_BIND:
-                        CHECK(!started) << "received FUNCTIONFS_ENABLE while already bound?";
+                        CHECK(!bound) << "received FUNCTIONFS_BIND while already bound?";
                         bound = true;
                         break;
 
@@ -280,28 +313,7 @@
                 }
             }
 
-            pthread_t worker_thread_handle = worker_thread_.native_handle();
-            while (true) {
-                int rc = pthread_kill(worker_thread_handle, kInterruptionSignal);
-                if (rc != 0) {
-                    LOG(ERROR) << "failed to send interruption signal to worker: " << strerror(rc);
-                    break;
-                }
-
-                std::this_thread::sleep_for(100ms);
-
-                rc = pthread_kill(worker_thread_handle, 0);
-                if (rc == 0) {
-                    continue;
-                } else if (rc == ESRCH) {
-                    break;
-                } else {
-                    LOG(ERROR) << "failed to send interruption signal to worker: " << strerror(rc);
-                }
-            }
-
-            worker_thread_.join();
-
+            StopWorker();
             aio_context_.reset();
             read_fd_.reset();
             write_fd_.reset();
@@ -313,12 +325,14 @@
             adb_thread_setname("UsbFfs-worker");
             for (size_t i = 0; i < kUsbReadQueueDepth; ++i) {
                 read_requests_[i] = CreateReadBlock(next_read_id_++);
-                SubmitRead(&read_requests_[i]);
+                if (!SubmitRead(&read_requests_[i])) {
+                    return;
+                }
             }
 
             while (!stopped_) {
                 uint64_t dummy;
-                ssize_t rc = adb_read(event_fd_.get(), &dummy, sizeof(dummy));
+                ssize_t rc = adb_read(worker_event_fd_.get(), &dummy, sizeof(dummy));
                 if (rc == -1) {
                     PLOG(FATAL) << "failed to read from eventfd";
                 } else if (rc == 0) {
@@ -330,12 +344,36 @@
         });
     }
 
+    void StopWorker() {
+        pthread_t worker_thread_handle = worker_thread_.native_handle();
+        while (true) {
+            int rc = pthread_kill(worker_thread_handle, kInterruptionSignal);
+            if (rc != 0) {
+                LOG(ERROR) << "failed to send interruption signal to worker: " << strerror(rc);
+                break;
+            }
+
+            std::this_thread::sleep_for(100ms);
+
+            rc = pthread_kill(worker_thread_handle, 0);
+            if (rc == 0) {
+                continue;
+            } else if (rc == ESRCH) {
+                break;
+            } else {
+                LOG(ERROR) << "failed to send interruption signal to worker: " << strerror(rc);
+            }
+        }
+
+        worker_thread_.join();
+    }
+
     void PrepareReadBlock(IoBlock* block, uint64_t id) {
         block->pending = false;
-        block->payload.resize(kUsbReadSize);
+        block->payload = std::make_shared<Block>(kUsbReadSize);
         block->control.aio_data = static_cast<uint64_t>(TransferId::read(id));
-        block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload.data());
-        block->control.aio_nbytes = block->payload.size();
+        block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload->data());
+        block->control.aio_nbytes = block->payload->size();
     }
 
     IoBlock CreateReadBlock(uint64_t id) {
@@ -347,7 +385,7 @@
         block.control.aio_fildes = read_fd_.get();
         block.control.aio_offset = 0;
         block.control.aio_flags = IOCB_FLAG_RESFD;
-        block.control.aio_resfd = event_fd_.get();
+        block.control.aio_resfd = worker_event_fd_.get();
         return block;
     }
 
@@ -386,7 +424,7 @@
         uint64_t read_idx = id.id % kUsbReadQueueDepth;
         IoBlock* block = &read_requests_[read_idx];
         block->pending = false;
-        block->payload.resize(size);
+        block->payload->resize(size);
 
         // Notification for completed reads can be received out of order.
         if (block->id().id != needed_read_id_) {
@@ -407,16 +445,16 @@
     }
 
     void ProcessRead(IoBlock* block) {
-        if (!block->payload.empty()) {
+        if (!block->payload->empty()) {
             if (!incoming_header_.has_value()) {
-                CHECK_EQ(sizeof(amessage), block->payload.size());
+                CHECK_EQ(sizeof(amessage), block->payload->size());
                 amessage msg;
-                memcpy(&msg, block->payload.data(), sizeof(amessage));
+                memcpy(&msg, block->payload->data(), sizeof(amessage));
                 LOG(DEBUG) << "USB read:" << dump_header(&msg);
                 incoming_header_ = msg;
             } else {
                 size_t bytes_left = incoming_header_->data_length - incoming_payload_.size();
-                Block payload = std::move(block->payload);
+                Block payload = std::move(*block->payload);
                 CHECK_LE(payload.size(), bytes_left);
                 incoming_payload_.append(std::make_unique<Block>(std::move(payload)));
             }
@@ -438,13 +476,22 @@
         SubmitRead(block);
     }
 
-    void SubmitRead(IoBlock* block) {
+    bool SubmitRead(IoBlock* block) {
         block->pending = true;
         struct iocb* iocb = &block->control;
         if (io_submit(aio_context_.get(), 1, &iocb) != 1) {
+            if (errno == EINVAL && !gFfsAioSupported.has_value()) {
+                HandleError("failed to submit first read, AIO on FFS not supported");
+                gFfsAioSupported = false;
+                return false;
+            }
+
             HandleError(StringPrintf("failed to submit read: %s", strerror(errno)));
-            return;
+            return false;
         }
+
+        gFfsAioSupported = true;
+        return true;
     }
 
     void HandleWrite(TransferId id) {
@@ -462,7 +509,8 @@
         SubmitWrites();
     }
 
-    std::unique_ptr<IoBlock> CreateWriteBlock(Block payload, uint64_t id) {
+    std::unique_ptr<IoBlock> CreateWriteBlock(std::shared_ptr<Block> payload, size_t offset,
+                                              size_t len, uint64_t id) {
         auto block = std::make_unique<IoBlock>();
         block->payload = std::move(payload);
         block->control.aio_data = static_cast<uint64_t>(TransferId::write(id));
@@ -470,14 +518,20 @@
         block->control.aio_lio_opcode = IOCB_CMD_PWRITE;
         block->control.aio_reqprio = 0;
         block->control.aio_fildes = write_fd_.get();
-        block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload.data());
-        block->control.aio_nbytes = block->payload.size();
+        block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload->data() + offset);
+        block->control.aio_nbytes = len;
         block->control.aio_offset = 0;
         block->control.aio_flags = IOCB_FLAG_RESFD;
-        block->control.aio_resfd = event_fd_.get();
+        block->control.aio_resfd = worker_event_fd_.get();
         return block;
     }
 
+    std::unique_ptr<IoBlock> CreateWriteBlock(Block payload, uint64_t id) {
+        std::shared_ptr<Block> block = std::make_shared<Block>(std::move(payload));
+        size_t len = block->size();
+        return CreateWriteBlock(std::move(block), 0, len, id);
+    }
+
     void SubmitWrites() REQUIRES(write_mutex_) {
         if (writes_submitted_ == kUsbWriteQueueDepth) {
             return;
@@ -526,7 +580,8 @@
     std::promise<void> destruction_notifier_;
     std::once_flag error_flag_;
 
-    unique_fd event_fd_;
+    unique_fd worker_event_fd_;
+    unique_fd monitor_event_fd_;
 
     ScopedAioContext aio_context_;
     unique_fd control_fd_;
@@ -549,12 +604,21 @@
     std::deque<std::unique_ptr<IoBlock>> write_requests_ GUARDED_BY(write_mutex_);
     size_t next_write_id_ GUARDED_BY(write_mutex_) = 0;
     size_t writes_submitted_ GUARDED_BY(write_mutex_) = 0;
+
+    static constexpr int kInterruptionSignal = SIGUSR1;
 };
 
+void usb_init_legacy();
+
 static void usb_ffs_open_thread() {
     adb_thread_setname("usb ffs open");
 
     while (true) {
+        if (gFfsAioSupported.has_value() && !gFfsAioSupported.value()) {
+            LOG(INFO) << "failed to use nonblocking ffs, falling back to legacy";
+            return usb_init_legacy();
+        }
+
         unique_fd control;
         unique_fd bulk_out;
         unique_fd bulk_in;
@@ -575,11 +639,14 @@
     }
 }
 
-void usb_init_legacy();
 void usb_init() {
-    if (!android::base::GetBoolProperty("persist.adb.nonblocking_ffs", false)) {
-        usb_init_legacy();
-    } else {
+    bool use_nonblocking = android::base::GetBoolProperty(
+            "persist.adb.nonblocking_ffs",
+            android::base::GetBoolProperty("ro.adb.nonblocking_ffs", true));
+
+    if (use_nonblocking) {
         std::thread(usb_ffs_open_thread).detach();
+    } else {
+        usb_init_legacy();
     }
 }
diff --git a/adb/services.cpp b/adb/services.cpp
index 0061f0e..80f9f79 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -25,6 +25,7 @@
 #include <string.h>
 
 #include <thread>
+
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <cutils/sockets.h>
@@ -63,11 +64,11 @@
         adb_setsockopt(s[0], SOL_SOCKET, SO_SNDBUF, &max_buf, sizeof(max_buf));
         adb_setsockopt(s[1], SOL_SOCKET, SO_SNDBUF, &max_buf, sizeof(max_buf));
     }
-#endif // !ADB_HOST
+#endif  // !ADB_HOST
 
     std::thread(service_bootstrap_func, service_name, func, unique_fd(s[1])).detach();
 
-    D("service thread started, %d:%d",s[0], s[1]);
+    D("service thread started, %d:%d", s[0], s[1]);
     return unique_fd(s[0]);
 }
 
@@ -108,12 +109,21 @@
         const char* serial = sinfo->serial.length() ? sinfo->serial.c_str() : nullptr;
         atransport* t = acquire_one_transport(sinfo->transport_type, serial, sinfo->transport_id,
                                               &is_ambiguous, &error);
-        if (t != nullptr && (sinfo->state == kCsAny || sinfo->state == t->GetConnectionState())) {
+        if (sinfo->state == kCsOffline) {
+            // wait-for-disconnect uses kCsOffline, we don't actually want to wait for 'offline'.
+            if (t == nullptr) {
+                SendOkay(fd);
+                break;
+            }
+        } else if (t != nullptr &&
+                   (sinfo->state == kCsAny || sinfo->state == t->GetConnectionState())) {
             SendOkay(fd);
             break;
-        } else if (!is_ambiguous) {
-            adb_pollfd pfd = {.fd = fd, .events = POLLIN };
-            int rc = adb_poll(&pfd, 1, 1000);
+        }
+
+        if (!is_ambiguous) {
+            adb_pollfd pfd = {.fd = fd, .events = POLLIN};
+            int rc = adb_poll(&pfd, 1, 100);
             if (rc < 0) {
                 SendFail(fd, error);
                 break;
@@ -187,46 +197,44 @@
 #endif
 
 #if ADB_HOST
-asocket* host_service_to_socket(const char* name, const char* serial, TransportId transport_id) {
-    if (!strcmp(name,"track-devices")) {
+asocket* host_service_to_socket(std::string_view name, std::string_view serial,
+                                TransportId transport_id) {
+    if (name == "track-devices") {
         return create_device_tracker(false);
-    } else if (!strcmp(name, "track-devices-l")) {
+    } else if (name == "track-devices-l") {
         return create_device_tracker(true);
-    } else if (android::base::StartsWith(name, "wait-for-")) {
-        name += strlen("wait-for-");
-
+    } else if (ConsumePrefix(&name, "wait-for-")) {
         std::shared_ptr<state_info> sinfo = std::make_shared<state_info>();
         if (sinfo == nullptr) {
             fprintf(stderr, "couldn't allocate state_info: %s", strerror(errno));
             return nullptr;
         }
 
-        if (serial) sinfo->serial = serial;
+        sinfo->serial = serial;
         sinfo->transport_id = transport_id;
 
-        if (android::base::StartsWith(name, "local")) {
-            name += strlen("local");
+        if (ConsumePrefix(&name, "local")) {
             sinfo->transport_type = kTransportLocal;
-        } else if (android::base::StartsWith(name, "usb")) {
-            name += strlen("usb");
+        } else if (ConsumePrefix(&name, "usb")) {
             sinfo->transport_type = kTransportUsb;
-        } else if (android::base::StartsWith(name, "any")) {
-            name += strlen("any");
+        } else if (ConsumePrefix(&name, "any")) {
             sinfo->transport_type = kTransportAny;
         } else {
             return nullptr;
         }
 
-        if (!strcmp(name, "-device")) {
+        if (name == "-device") {
             sinfo->state = kCsDevice;
-        } else if (!strcmp(name, "-recovery")) {
+        } else if (name == "-recovery") {
             sinfo->state = kCsRecovery;
-        } else if (!strcmp(name, "-sideload")) {
+        } else if (name == "-sideload") {
             sinfo->state = kCsSideload;
-        } else if (!strcmp(name, "-bootloader")) {
+        } else if (name == "-bootloader") {
             sinfo->state = kCsBootloader;
-        } else if (!strcmp(name, "-any")) {
+        } else if (name == "-any") {
             sinfo->state = kCsAny;
+        } else if (name == "-disconnect") {
+            sinfo->state = kCsOffline;
         } else {
             return nullptr;
         }
@@ -235,8 +243,8 @@
             wait_for_state(fd, sinfo.get());
         });
         return create_local_socket(std::move(fd));
-    } else if (!strncmp(name, "connect:", 8)) {
-        std::string host(name + strlen("connect:"));
+    } else if (ConsumePrefix(&name, "connect:")) {
+        std::string host(name);
         unique_fd fd = create_service_thread(
                 "connect", std::bind(connect_service, std::placeholders::_1, host));
         return create_local_socket(std::move(fd));
diff --git a/adb/socket_spec.h b/adb/socket_spec.h
index 687d751..7cc2fac 100644
--- a/adb/socket_spec.h
+++ b/adb/socket_spec.h
@@ -29,6 +29,5 @@
                          std::string* error);
 int socket_spec_listen(std::string_view spec, std::string* error, int* resolved_tcp_port = nullptr);
 
-// Exposed for testing.
 bool parse_tcp_socket_spec(std::string_view spec, std::string* hostname, int* port,
                            std::string* serial, std::string* error);
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 420a6d5..8a2bf9a 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -426,22 +426,6 @@
     return s;
 }
 
-#if ADB_HOST
-static asocket* create_host_service_socket(const char* name, const char* serial,
-                                           TransportId transport_id) {
-    asocket* s;
-
-    s = host_service_to_socket(name, serial, transport_id);
-
-    if (s != nullptr) {
-        D("LS(%d) bound to '%s'", s->id, name);
-        return s;
-    }
-
-    return s;
-}
-#endif /* ADB_HOST */
-
 static int remote_socket_enqueue(asocket* s, apacket::payload_type data) {
     D("entered remote_socket_enqueue RS(%d) WRITE fd=%d peer.fd=%d", s->id, s->fd, s->peer->fd);
     apacket* p = get_apacket();
@@ -771,34 +755,27 @@
 
 #if ADB_HOST
     service = std::string_view(s->smart_socket_data).substr(4);
-    if (service.starts_with("host-serial:")) {
-        service.remove_prefix(strlen("host-serial:"));
-
+    if (ConsumePrefix(&service, "host-serial:")) {
         // serial number should follow "host:" and could be a host:port string.
         if (!internal::parse_host_service(&serial, &service, service)) {
             LOG(ERROR) << "SS(" << s->id << "): failed to parse host service: " << service;
             goto fail;
         }
-    } else if (service.starts_with("host-transport-id:")) {
-        service.remove_prefix(strlen("host-transport-id:"));
+    } else if (ConsumePrefix(&service, "host-transport-id:")) {
         if (!ParseUint(&transport_id, service, &service)) {
             LOG(ERROR) << "SS(" << s->id << "): failed to parse host transport id: " << service;
             return -1;
         }
-        if (!service.starts_with(":")) {
+        if (!ConsumePrefix(&service, ":")) {
             LOG(ERROR) << "SS(" << s->id << "): host-transport-id without command";
             return -1;
         }
-        service.remove_prefix(1);
-    } else if (service.starts_with("host-usb:")) {
+    } else if (ConsumePrefix(&service, "host-usb:")) {
         type = kTransportUsb;
-        service.remove_prefix(strlen("host-usb:"));
-    } else if (service.starts_with("host-local:")) {
+    } else if (ConsumePrefix(&service, "host-local:")) {
         type = kTransportLocal;
-        service.remove_prefix(strlen("host-local:"));
-    } else if (service.starts_with("host:")) {
+    } else if (ConsumePrefix(&service, "host:")) {
         type = kTransportAny;
-        service.remove_prefix(strlen("host:"));
     } else {
         service = std::string_view{};
     }
@@ -808,17 +785,22 @@
 
         // Some requests are handled immediately -- in that case the handle_host_request() routine
         // has sent the OKAY or FAIL message and all we have to do is clean up.
-        // TODO: Convert to string_view.
-        if (handle_host_request(std::string(service).c_str(), type,
-                                serial.empty() ? nullptr : std::string(serial).c_str(),
-                                transport_id, s->peer->fd, s)) {
-            LOG(VERBOSE) << "SS(" << s->id << "): handled host service '" << service << "'";
-            goto fail;
-        }
-        if (service.starts_with("transport")) {
-            D("SS(%d): okay transport", s->id);
-            s->smart_socket_data.clear();
-            return 0;
+        auto host_request_result = handle_host_request(
+                service, type, serial.empty() ? nullptr : std::string(serial).c_str(), transport_id,
+                s->peer->fd, s);
+
+        switch (host_request_result) {
+            case HostRequestResult::Handled:
+                LOG(VERBOSE) << "SS(" << s->id << "): handled host service '" << service << "'";
+                goto fail;
+
+            case HostRequestResult::SwitchedTransport:
+                D("SS(%d): okay transport", s->id);
+                s->smart_socket_data.clear();
+                return 0;
+
+            case HostRequestResult::Unhandled:
+                break;
         }
 
         /* try to find a local service with this name.
@@ -826,8 +808,7 @@
         ** and tear down here.
         */
         // TODO: Convert to string_view.
-        s2 = create_host_service_socket(std::string(service).c_str(), std::string(serial).c_str(),
-                                        transport_id);
+        s2 = host_service_to_socket(service, serial, transport_id);
         if (s2 == nullptr) {
             LOG(VERBOSE) << "SS(" << s->id << "): couldn't create host service '" << service << "'";
             SendFail(s->peer->fd, "unknown host service");
diff --git a/adb/test_adb.py b/adb/test_adb.py
index 14e5071..8272722 100755
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -416,6 +416,7 @@
                     output.strip(),
                     "already connected to {}".format(serial).encode("utf8"))
 
+    @unittest.skip("Currently failing b/123247844")
     def test_reconnect(self):
         """Ensure that a disconnected device reconnects."""
 
diff --git a/adb/transport.cpp b/adb/transport.cpp
index ae53597..0b4e084 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -52,6 +52,8 @@
 #include "fdevent.h"
 #include "sysdeps/chrono.h"
 
+using android::base::ScopedLockAssertion;
+
 static void remove_transport(atransport* transport);
 static void transport_unref(atransport* transport);
 
@@ -69,20 +71,11 @@
 const char* const kFeatureApex = "apex";
 const char* const kFeatureFixedPushMkdir = "fixed_push_mkdir";
 const char* const kFeatureAbb = "abb";
+const char* const kFeatureFixedPushSymlinkTimestamp = "fixed_push_symlink_timestamp";
+const char* const kFeatureAbbExec = "abb_exec";
 
 namespace {
 
-// A class that helps the Clang Thread Safety Analysis deal with
-// std::unique_lock. Given that std::unique_lock is movable, and the analysis
-// can not currently perform alias analysis, it is not annotated. In order to
-// assert that the mutex is held, a ScopedAssumeLocked can be created just after
-// the std::unique_lock.
-class SCOPED_CAPABILITY ScopedAssumeLocked {
-  public:
-    ScopedAssumeLocked(std::mutex& mutex) ACQUIRE(mutex) {}
-    ~ScopedAssumeLocked() RELEASE() {}
-};
-
 #if ADB_HOST
 // Tracks and handles atransport*s that are attempting reconnection.
 class ReconnectHandler {
@@ -180,7 +173,7 @@
         ReconnectAttempt attempt;
         {
             std::unique_lock<std::mutex> lock(reconnect_mutex_);
-            ScopedAssumeLocked assume_lock(reconnect_mutex_);
+            ScopedLockAssertion assume_lock(reconnect_mutex_);
 
             if (!reconnect_queue_.empty()) {
                 // FIXME: libstdc++ (used on Windows) implements condition_variable with
@@ -296,7 +289,7 @@
         LOG(INFO) << this->transport_name_ << ": write thread spawning";
         while (true) {
             std::unique_lock<std::mutex> lock(mutex_);
-            ScopedAssumeLocked assume_locked(mutex_);
+            ScopedLockAssertion assume_locked(mutex_);
             cv_.wait(lock, [this]() REQUIRES(mutex_) {
                 return this->stopped_ || !this->write_queue_.empty();
             });
@@ -923,7 +916,7 @@
 
 bool ConnectionWaitable::WaitForConnection(std::chrono::milliseconds timeout) {
     std::unique_lock<std::mutex> lock(mutex_);
-    ScopedAssumeLocked assume_locked(mutex_);
+    ScopedLockAssertion assume_locked(mutex_);
     return cv_.wait_for(lock, timeout, [&]() REQUIRES(mutex_) {
         return connection_established_ready_;
     }) && connection_established_;
@@ -1014,8 +1007,14 @@
 const FeatureSet& supported_features() {
     // Local static allocation to avoid global non-POD variables.
     static const FeatureSet* features = new FeatureSet{
-            kFeatureShell2,         kFeatureCmd,  kFeatureStat2,
-            kFeatureFixedPushMkdir, kFeatureApex, kFeatureAbb,
+            kFeatureShell2,
+            kFeatureCmd,
+            kFeatureStat2,
+            kFeatureFixedPushMkdir,
+            kFeatureApex,
+            kFeatureAbb,
+            kFeatureFixedPushSymlinkTimestamp,
+            kFeatureAbbExec,
             // Increment ADB_SERVER_VERSION when adding a feature that adbd needs
             // to know about. Otherwise, the client can be stuck running an old
             // version of the server even after upgrading their copy of adb.
diff --git a/adb/transport.h b/adb/transport.h
index 065c81f..a0174b8 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -67,6 +67,8 @@
 extern const char* const kFeatureFixedPushMkdir;
 // adbd supports android binder bridge (abb).
 extern const char* const kFeatureAbb;
+// adbd properly updates symlink timestamps on push.
+extern const char* const kFeatureFixedPushSymlinkTimestamp;
 
 TransportId NextTransportId();
 
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 9a74fb3..b9f738d 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -53,12 +53,27 @@
 
 // Android Wear has been using port 5601 in all of its documentation/tooling,
 // but we search for emulators on ports [5554, 5555 + ADB_LOCAL_TRANSPORT_MAX].
-// Avoid stomping on their port by limiting the number of emulators that can be
-// connected.
-#define ADB_LOCAL_TRANSPORT_MAX 16
+// Avoid stomping on their port by restricting the active scanning range.
+// Once emulators self-(re-)register, they'll have to avoid 5601 in their own way.
+static int adb_local_transport_max_port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT + 16 * 2 - 1;
 
 static std::mutex& local_transports_lock = *new std::mutex();
 
+static void adb_local_transport_max_port_env_override() {
+    const char* env_max_s = getenv("ADB_LOCAL_TRANSPORT_MAX_PORT");
+    if (env_max_s != nullptr) {
+        size_t env_max;
+        if (ParseUint(&env_max, env_max_s, nullptr) && env_max < 65536) {
+            // < DEFAULT_ADB_LOCAL_TRANSPORT_PORT harmlessly mimics ADB_EMU=0
+            adb_local_transport_max_port = env_max;
+            D("transport: ADB_LOCAL_TRANSPORT_MAX_PORT read as %d", adb_local_transport_max_port);
+        } else {
+            D("transport: ADB_LOCAL_TRANSPORT_MAX_PORT '%s' invalid or >= 65536, so ignored",
+              env_max_s);
+        }
+    }
+}
+
 // We keep a map from emulator port to transport.
 // TODO: weak_ptr?
 static auto& local_transports GUARDED_BY(local_transports_lock) =
@@ -110,7 +125,6 @@
             D("reconnect failed: %s", response.c_str());
             return ReconnectResult::Retry;
         }
-
         // This invokes the part of register_socket_transport() that needs to be
         // invoked if the atransport* has already been setup. This eventually
         // calls atransport->SetConnection() with a newly created Connection*
@@ -168,12 +182,10 @@
 #if ADB_HOST
 
 static void PollAllLocalPortsForEmulator() {
-    int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
-    int count = ADB_LOCAL_TRANSPORT_MAX;
-
     // Try to connect to any number of running emulator instances.
-    for ( ; count > 0; count--, port += 2 ) {
-        local_connect(port);
+    for (int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT; port <= adb_local_transport_max_port;
+         port += 2) {
+        local_connect(port);  // Note, uses port and port-1, so '=max_port' is OK.
     }
 }
 
@@ -289,6 +301,7 @@
 #if ADB_HOST
     D("transport: local client init");
     std::thread(client_socket_thread, port).detach();
+    adb_local_transport_max_port_env_override();
 #elif !defined(__ANDROID__)
     // Host adbd.
     D("transport: local server init");
@@ -371,10 +384,6 @@
         if (existing_transport != nullptr) {
             D("local transport for port %d already registered (%p)?", adb_port, existing_transport);
             fail = -1;
-        } else if (local_transports.size() >= ADB_LOCAL_TRANSPORT_MAX) {
-            // Too many emulators.
-            D("cannot register more emulators. Maximum is %d", ADB_LOCAL_TRANSPORT_MAX);
-            fail = -1;
         } else {
             local_transports[adb_port] = t;
         }
diff --git a/base/Android.bp b/base/Android.bp
index b0181aa..38f301a 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -46,6 +46,7 @@
     defaults: ["libbase_cflags_defaults"],
     srcs: [
         "chrono_utils.cpp",
+        "cmsg.cpp",
         "file.cpp",
         "logging.cpp",
         "mapped_file.cpp",
@@ -85,6 +86,9 @@
                 "errors_windows.cpp",
                 "utf8.cpp",
             ],
+            exclude_srcs: [
+                "cmsg.cpp",
+            ],
             enabled: true,
         },
     },
@@ -121,6 +125,7 @@
     defaults: ["libbase_cflags_defaults"],
     host_supported: true,
     srcs: [
+        "cmsg_test.cpp",
         "endian_test.cpp",
         "errors_test.cpp",
         "file_test.cpp",
diff --git a/base/cmsg.cpp b/base/cmsg.cpp
new file mode 100644
index 0000000..42866f8
--- /dev/null
+++ b/base/cmsg.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2019 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 <android-base/cmsg.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/user.h>
+
+#include <memory>
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace base {
+
+ssize_t SendFileDescriptorVector(int sockfd, const void* data, size_t len,
+                                 const std::vector<int>& fds) {
+  size_t cmsg_space = CMSG_SPACE(sizeof(int) * fds.size());
+  size_t cmsg_len = CMSG_LEN(sizeof(int) * fds.size());
+  if (cmsg_space >= PAGE_SIZE) {
+    errno = ENOMEM;
+    return -1;
+  }
+
+  alignas(struct cmsghdr) char cmsg_buf[cmsg_space];
+  iovec iov = {.iov_base = const_cast<void*>(data), .iov_len = len};
+  msghdr msg = {
+      .msg_name = nullptr,
+      .msg_namelen = 0,
+      .msg_iov = &iov,
+      .msg_iovlen = 1,
+      .msg_control = cmsg_buf,
+      // We can't cast to the actual type of the field, because it's different across platforms.
+      .msg_controllen = static_cast<unsigned int>(cmsg_space),
+      .msg_flags = 0,
+  };
+
+  struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+  cmsg->cmsg_level = SOL_SOCKET;
+  cmsg->cmsg_type = SCM_RIGHTS;
+  cmsg->cmsg_len = cmsg_len;
+
+  int* cmsg_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+  for (size_t i = 0; i < fds.size(); ++i) {
+    cmsg_fds[i] = fds[i];
+  }
+
+#if defined(__linux__)
+  int flags = MSG_NOSIGNAL;
+#else
+  int flags = 0;
+#endif
+
+  return TEMP_FAILURE_RETRY(sendmsg(sockfd, &msg, flags));
+}
+
+ssize_t ReceiveFileDescriptorVector(int sockfd, void* data, size_t len, size_t max_fds,
+                                    std::vector<unique_fd>* fds) {
+  fds->clear();
+
+  size_t cmsg_space = CMSG_SPACE(sizeof(int) * max_fds);
+  if (cmsg_space >= PAGE_SIZE) {
+    errno = ENOMEM;
+    return -1;
+  }
+
+  alignas(struct cmsghdr) char cmsg_buf[cmsg_space];
+  iovec iov = {.iov_base = const_cast<void*>(data), .iov_len = len};
+  msghdr msg = {
+      .msg_name = nullptr,
+      .msg_namelen = 0,
+      .msg_iov = &iov,
+      .msg_iovlen = 1,
+      .msg_control = cmsg_buf,
+      // We can't cast to the actual type of the field, because it's different across platforms.
+      .msg_controllen = static_cast<unsigned int>(cmsg_space),
+      .msg_flags = 0,
+  };
+
+  int flags = MSG_TRUNC | MSG_CTRUNC;
+#if defined(__linux__)
+  flags |= MSG_CMSG_CLOEXEC | MSG_NOSIGNAL;
+#endif
+
+  ssize_t rc = TEMP_FAILURE_RETRY(recvmsg(sockfd, &msg, flags));
+
+  if (rc == -1) {
+    return -1;
+  }
+
+  int error = 0;
+  if ((msg.msg_flags & MSG_TRUNC)) {
+    LOG(ERROR) << "message was truncated when receiving file descriptors";
+    error = EMSGSIZE;
+  } else if ((msg.msg_flags & MSG_CTRUNC)) {
+    LOG(ERROR) << "control message was truncated when receiving file descriptors";
+    error = EMSGSIZE;
+  }
+
+  std::vector<unique_fd> received_fds;
+  struct cmsghdr* cmsg;
+  for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+    if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
+      LOG(ERROR) << "received unexpected cmsg: [" << cmsg->cmsg_level << ", " << cmsg->cmsg_type
+                 << "]";
+      error = EBADMSG;
+      continue;
+    }
+
+    // There isn't a macro that does the inverse of CMSG_LEN, so hack around it ourselves, with
+    // some asserts to ensure that CMSG_LEN behaves as we expect.
+#if defined(__linux__)
+#define CMSG_ASSERT static_assert
+#else
+// CMSG_LEN is somehow not constexpr on darwin.
+#define CMSG_ASSERT CHECK
+#endif
+    CMSG_ASSERT(CMSG_LEN(0) + 1 * sizeof(int) == CMSG_LEN(1 * sizeof(int)));
+    CMSG_ASSERT(CMSG_LEN(0) + 2 * sizeof(int) == CMSG_LEN(2 * sizeof(int)));
+    CMSG_ASSERT(CMSG_LEN(0) + 3 * sizeof(int) == CMSG_LEN(3 * sizeof(int)));
+    CMSG_ASSERT(CMSG_LEN(0) + 4 * sizeof(int) == CMSG_LEN(4 * sizeof(int)));
+
+    if (cmsg->cmsg_len % sizeof(int) != 0) {
+      LOG(FATAL) << "cmsg_len(" << cmsg->cmsg_len << ") not aligned to sizeof(int)";
+    } else if (cmsg->cmsg_len <= CMSG_LEN(0)) {
+      LOG(FATAL) << "cmsg_len(" << cmsg->cmsg_len << ") not long enough to hold any data";
+    }
+
+    int* cmsg_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+    size_t cmsg_fdcount = static_cast<size_t>(cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+    for (size_t i = 0; i < cmsg_fdcount; ++i) {
+#if !defined(__linux__)
+      // Linux uses MSG_CMSG_CLOEXEC instead of doing this manually.
+      fcntl(cmsg_fds[i], F_SETFD, FD_CLOEXEC);
+#endif
+      received_fds.emplace_back(cmsg_fds[i]);
+    }
+  }
+
+  if (error != 0) {
+    errno = error;
+    return -1;
+  }
+
+  if (received_fds.size() > max_fds) {
+    LOG(ERROR) << "received too many file descriptors, expected " << fds->size() << ", received "
+               << received_fds.size();
+    errno = EMSGSIZE;
+    return -1;
+  }
+
+  *fds = std::move(received_fds);
+  return rc;
+}
+
+}  // namespace base
+}  // namespace android
diff --git a/base/cmsg_test.cpp b/base/cmsg_test.cpp
new file mode 100644
index 0000000..9ee5c82
--- /dev/null
+++ b/base/cmsg_test.cpp
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2019 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 <android-base/cmsg.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+
+#if !defined(_WIN32)
+
+using android::base::ReceiveFileDescriptors;
+using android::base::SendFileDescriptors;
+using android::base::unique_fd;
+
+static ino_t GetInode(int fd) {
+  struct stat st;
+  if (fstat(fd, &st) != 0) {
+    PLOG(FATAL) << "fstat failed";
+  }
+
+  return st.st_ino;
+}
+
+struct CmsgTest : ::testing::TestWithParam<bool> {
+  bool Seqpacket() { return GetParam(); }
+
+  void SetUp() override {
+    ASSERT_TRUE(
+        android::base::Socketpair(Seqpacket() ? SOCK_SEQPACKET : SOCK_STREAM, &send, &recv));
+    int dup1 = dup(tmp1.fd);
+    ASSERT_NE(-1, dup1);
+    int dup2 = dup(tmp2.fd);
+    ASSERT_NE(-1, dup2);
+
+    fd1.reset(dup1);
+    fd2.reset(dup2);
+
+    ino1 = GetInode(dup1);
+    ino2 = GetInode(dup2);
+  }
+
+  unique_fd send;
+  unique_fd recv;
+
+  TemporaryFile tmp1;
+  TemporaryFile tmp2;
+
+  unique_fd fd1;
+  unique_fd fd2;
+
+  ino_t ino1;
+  ino_t ino2;
+};
+
+TEST_P(CmsgTest, smoke) {
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "x", 1, fd1.get()));
+
+  char buf[2];
+  unique_fd received;
+  ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 2, &received));
+  ASSERT_EQ('x', buf[0]);
+  ASSERT_NE(-1, received.get());
+
+  ASSERT_EQ(ino1, GetInode(received.get()));
+}
+
+TEST_P(CmsgTest, msg_trunc) {
+  ASSERT_EQ(2, SendFileDescriptors(send.get(), "ab", 2, fd1.get(), fd2.get()));
+
+  char buf[2];
+  unique_fd received1, received2;
+
+  ssize_t rc = ReceiveFileDescriptors(recv.get(), buf, 1, &received1, &received2);
+  if (Seqpacket()) {
+    ASSERT_EQ(-1, rc);
+    ASSERT_EQ(EMSGSIZE, errno);
+    ASSERT_EQ(-1, received1.get());
+    ASSERT_EQ(-1, received2.get());
+  } else {
+    ASSERT_EQ(1, rc);
+    ASSERT_NE(-1, received1.get());
+    ASSERT_NE(-1, received2.get());
+    ASSERT_EQ(ino1, GetInode(received1.get()));
+    ASSERT_EQ(ino2, GetInode(received2.get()));
+    ASSERT_EQ(1, read(recv.get(), buf, 2));
+  }
+}
+
+TEST_P(CmsgTest, msg_ctrunc) {
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get(), fd2.get()));
+
+  char buf[2];
+  unique_fd received;
+  ASSERT_EQ(-1, ReceiveFileDescriptors(recv.get(), buf, 1, &received));
+  ASSERT_EQ(EMSGSIZE, errno);
+  ASSERT_EQ(-1, received.get());
+}
+
+TEST_P(CmsgTest, peek) {
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get()));
+
+  char buf[2];
+  ASSERT_EQ(1, ::recv(recv.get(), buf, sizeof(buf), MSG_PEEK));
+  ASSERT_EQ('a', buf[0]);
+
+  unique_fd received;
+  ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received));
+  ASSERT_EQ(ino1, GetInode(received.get()));
+}
+
+TEST_P(CmsgTest, stream_fd_association) {
+  if (Seqpacket()) {
+    return;
+  }
+
+  // fds are associated with the first byte of the write.
+  ASSERT_EQ(1, TEMP_FAILURE_RETRY(write(send.get(), "a", 1)));
+  ASSERT_EQ(2, SendFileDescriptors(send.get(), "bc", 2, fd1.get()));
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "d", 1, fd2.get()));
+  char buf[2];
+  ASSERT_EQ(2, TEMP_FAILURE_RETRY(read(recv.get(), buf, 2)));
+  ASSERT_EQ(0, memcmp(buf, "ab", 2));
+
+  std::vector<unique_fd> received1;
+  ssize_t rc = ReceiveFileDescriptorVector(recv.get(), buf, 1, 1, &received1);
+  ASSERT_EQ(1, rc);
+  ASSERT_EQ('c', buf[0]);
+  ASSERT_TRUE(received1.empty());
+
+  unique_fd received2;
+  rc = ReceiveFileDescriptors(recv.get(), buf, 1, &received2);
+  ASSERT_EQ(1, rc);
+  ASSERT_EQ('d', buf[0]);
+  ASSERT_EQ(ino2, GetInode(received2.get()));
+}
+
+TEST_P(CmsgTest, multiple_fd_ordering) {
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get(), fd2.get()));
+
+  char buf[2];
+  unique_fd received1, received2;
+  ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received1, &received2));
+
+  ASSERT_NE(-1, received1.get());
+  ASSERT_NE(-1, received2.get());
+
+  ASSERT_EQ(ino1, GetInode(received1.get()));
+  ASSERT_EQ(ino2, GetInode(received2.get()));
+}
+
+TEST_P(CmsgTest, separate_fd_ordering) {
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get()));
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "b", 1, fd2.get()));
+
+  char buf[2];
+  unique_fd received1, received2;
+  ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received1));
+  ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received2));
+
+  ASSERT_NE(-1, received1.get());
+  ASSERT_NE(-1, received2.get());
+
+  ASSERT_EQ(ino1, GetInode(received1.get()));
+  ASSERT_EQ(ino2, GetInode(received2.get()));
+}
+
+TEST_P(CmsgTest, separate_fds_no_coalescing) {
+  unique_fd sent1(dup(tmp1.fd));
+  unique_fd sent2(dup(tmp2.fd));
+
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "", 1, fd1.get()));
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "", 1, fd2.get()));
+
+  char buf[2];
+  std::vector<unique_fd> received;
+  ASSERT_EQ(1, ReceiveFileDescriptorVector(recv.get(), buf, 2, 2, &received));
+  ASSERT_EQ(1U, received.size());
+  ASSERT_EQ(ino1, GetInode(received[0].get()));
+
+  ASSERT_EQ(1, ReceiveFileDescriptorVector(recv.get(), buf, 2, 2, &received));
+  ASSERT_EQ(1U, received.size());
+  ASSERT_EQ(ino2, GetInode(received[0].get()));
+}
+
+INSTANTIATE_TEST_CASE_P(CmsgTest, CmsgTest, testing::Bool());
+
+#endif
diff --git a/base/include/android-base/cmsg.h b/base/include/android-base/cmsg.h
new file mode 100644
index 0000000..7f93ddc
--- /dev/null
+++ b/base/include/android-base/cmsg.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <type_traits>
+#include <vector>
+
+#include <android-base/collections.h>
+#include <android-base/macros.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace base {
+
+#if !defined(_WIN32)
+
+// Helpers for sending and receiving file descriptors across Unix domain sockets.
+//
+// The cmsg(3) API is very hard to get right, with multiple landmines that can
+// lead to death. Almost all of the uses of cmsg in Android make at least one of
+// the following mistakes:
+//
+//   - not aligning the cmsg buffer
+//   - leaking fds if more fds are received than expected
+//   - blindly dereferencing CMSG_DATA without checking the header
+//   - using CMSG_SPACE instead of CMSG_LEN for .cmsg_len
+//   - using CMSG_LEN instead of CMSG_SPACE for .msg_controllen
+//   - using a length specified in number of fds instead of bytes
+//
+// These functions wrap the hard-to-use cmsg API with an easier to use abstraction.
+
+// Send file descriptors across a Unix domain socket.
+//
+// Note that the write can return short if the socket type is SOCK_STREAM. When
+// this happens, file descriptors are still sent to the other end, but with
+// truncated data. For this reason, using SOCK_SEQPACKET or SOCK_DGRAM is recommended.
+ssize_t SendFileDescriptorVector(int sock, const void* data, size_t len,
+                                 const std::vector<int>& fds);
+
+// Receive file descriptors from a Unix domain socket.
+//
+// If more FDs (or bytes, for datagram sockets) are received than expected,
+// -1 is returned with errno set to EMSGSIZE, and all received FDs are thrown away.
+ssize_t ReceiveFileDescriptorVector(int sock, void* data, size_t len, size_t max_fds,
+                                    std::vector<android::base::unique_fd>* fds);
+
+// Helper for SendFileDescriptorVector that constructs a std::vector for you, e.g.:
+//   SendFileDescriptors(sock, "foo", 3, std::move(fd1), std::move(fd2))
+template <typename... Args>
+ssize_t SendFileDescriptors(int sock, const void* data, size_t len, Args&&... sent_fds) {
+  // Do not allow implicit conversion to int: people might try to do something along the lines of:
+  //   SendFileDescriptors(..., std::move(a_unique_fd))
+  // and be surprised when the unique_fd isn't closed afterwards.
+  AssertType<int>(std::forward<Args>(sent_fds)...);
+  std::vector<int> fds;
+  Append(fds, std::forward<Args>(sent_fds)...);
+  return SendFileDescriptorVector(sock, data, len, fds);
+}
+
+// Helper for ReceiveFileDescriptorVector that receives an exact number of file descriptors.
+// If more file descriptors are received than requested, -1 is returned with errno set to EMSGSIZE.
+// If fewer file descriptors are received than requested, -1 is returned with errno set to ENOMSG.
+// In both cases, all arguments are cleared and any received FDs are thrown away.
+template <typename... Args>
+ssize_t ReceiveFileDescriptors(int sock, void* data, size_t len, Args&&... received_fds) {
+  std::vector<unique_fd*> fds;
+  Append(fds, std::forward<Args>(received_fds)...);
+
+  std::vector<unique_fd> result;
+  ssize_t rc = ReceiveFileDescriptorVector(sock, data, len, fds.size(), &result);
+  if (rc == -1 || result.size() != fds.size()) {
+    int err = rc == -1 ? errno : ENOMSG;
+    for (unique_fd* fd : fds) {
+      fd->reset();
+    }
+    errno = err;
+    return -1;
+  }
+
+  for (size_t i = 0; i < fds.size(); ++i) {
+    *fds[i] = std::move(result[i]);
+  }
+  return rc;
+}
+
+#endif
+
+}  // namespace base
+}  // namespace android
diff --git a/base/include/android-base/collections.h b/base/include/android-base/collections.h
new file mode 100644
index 0000000..be0683a
--- /dev/null
+++ b/base/include/android-base/collections.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utility>
+
+namespace android {
+namespace base {
+
+// Helpers for converting a variadic template parameter pack to a homogeneous collection.
+// Parameters must be implictly convertible to the contained type (including via move/copy ctors).
+//
+// Use as follows:
+//
+//   template <typename... Args>
+//   std::vector<int> CreateVector(Args&&... args) {
+//     std::vector<int> result;
+//     Append(result, std::forward<Args>(args)...);
+//     return result;
+//   }
+template <typename CollectionType, typename T>
+void Append(CollectionType& collection, T&& arg) {
+  collection.push_back(std::forward<T>(arg));
+}
+
+template <typename CollectionType, typename T, typename... Args>
+void Append(CollectionType& collection, T&& arg, Args&&... args) {
+  collection.push_back(std::forward<T>(arg));
+  return Append(collection, std::forward<Args>(args)...);
+}
+
+// Assert that all of the arguments in a variadic template parameter pack are of a given type
+// after std::decay.
+template <typename T, typename Arg, typename... Args>
+void AssertType(Arg&&) {
+  static_assert(std::is_same<T, typename std::decay<Arg>::type>::value);
+}
+
+template <typename T, typename Arg, typename... Args>
+void AssertType(Arg&&, Args&&... args) {
+  static_assert(std::is_same<T, typename std::decay<Arg>::type>::value);
+  AssertType<T>(std::forward<Args>(args)...);
+}
+
+}  // namespace base
+}  // namespace android
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
index fc5c1ce..8e9716f 100644
--- a/base/include/android-base/strings.h
+++ b/base/include/android-base/strings.h
@@ -56,23 +56,17 @@
 extern template std::string Join(const std::vector<const char*>&, const std::string&);
 
 // Tests whether 's' starts with 'prefix'.
-// TODO: string_view
-bool StartsWith(const std::string& s, const char* prefix);
-bool StartsWithIgnoreCase(const std::string& s, const char* prefix);
-bool StartsWith(const std::string& s, const std::string& prefix);
-bool StartsWithIgnoreCase(const std::string& s, const std::string& prefix);
-bool StartsWith(const std::string& s, char prefix);
+bool StartsWith(std::string_view s, std::string_view prefix);
+bool StartsWith(std::string_view s, char prefix);
+bool StartsWithIgnoreCase(std::string_view s, std::string_view prefix);
 
 // Tests whether 's' ends with 'suffix'.
-// TODO: string_view
-bool EndsWith(const std::string& s, const char* suffix);
-bool EndsWithIgnoreCase(const std::string& s, const char* suffix);
-bool EndsWith(const std::string& s, const std::string& suffix);
-bool EndsWithIgnoreCase(const std::string& s, const std::string& suffix);
-bool EndsWith(const std::string& s, char suffix);
+bool EndsWith(std::string_view s, std::string_view suffix);
+bool EndsWith(std::string_view s, char suffix);
+bool EndsWithIgnoreCase(std::string_view s, std::string_view suffix);
 
 // Tests whether 'lhs' equals 'rhs', ignoring case.
-bool EqualsIgnoreCase(const std::string& lhs, const std::string& rhs);
+bool EqualsIgnoreCase(std::string_view lhs, std::string_view rhs);
 
 }  // namespace base
 }  // namespace android
diff --git a/base/include/android-base/thread_annotations.h b/base/include/android-base/thread_annotations.h
index 5c55e63..53fe6da 100644
--- a/base/include/android-base/thread_annotations.h
+++ b/base/include/android-base/thread_annotations.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <mutex>
+
 #define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
 
 #define CAPABILITY(x) \
@@ -104,3 +106,39 @@
 
 #define NO_THREAD_SAFETY_ANALYSIS \
       THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
+
+namespace android {
+namespace base {
+
+// A class to help thread safety analysis deal with std::unique_lock and condition_variable.
+//
+// Clang's thread safety analysis currently doesn't perform alias analysis, so movable types
+// like std::unique_lock can't be marked with thread safety annotations. This helper allows
+// for manual assertion of lock state in a scope.
+//
+// For example:
+//
+//   std::mutex mutex;
+//   std::condition_variable cv;
+//   std::vector<int> vec GUARDED_BY(mutex);
+//
+//   int pop() {
+//     std::unique_lock lock(mutex);
+//     ScopedLockAssertion lock_assertion(mutex);
+//     cv.wait(lock, []() {
+//       ScopedLockAssertion lock_assertion(mutex);
+//       return !vec.empty();
+//     });
+//
+//     int result = vec.back();
+//     vec.pop_back();
+//     return result;
+//   }
+class SCOPED_CAPABILITY ScopedLockAssertion {
+ public:
+  ScopedLockAssertion(std::mutex& mutex) ACQUIRE(mutex) {}
+  ~ScopedLockAssertion() RELEASE() {}
+};
+
+}  // namespace base
+}  // namespace android
diff --git a/base/mapped_file.cpp b/base/mapped_file.cpp
index faa845d..7c65dc3 100644
--- a/base/mapped_file.cpp
+++ b/base/mapped_file.cpp
@@ -41,7 +41,14 @@
   HANDLE handle =
       CreateFileMapping(reinterpret_cast<HANDLE>(_get_osfhandle(fd)), nullptr,
                         (prot & PROT_WRITE) ? PAGE_READWRITE : PAGE_READONLY, 0, 0, nullptr);
-  if (handle == nullptr) return nullptr;
+  if (handle == nullptr) {
+    // http://b/119818070 "app crashes when reading asset of zero length".
+    // Return a MappedFile that's only valid for reading the size.
+    if (length == 0) {
+      return std::unique_ptr<MappedFile>(new MappedFile{nullptr, 0, 0, nullptr});
+    }
+    return nullptr;
+  }
   void* base = MapViewOfFile(handle, (prot & PROT_WRITE) ? FILE_MAP_ALL_ACCESS : FILE_MAP_READ, 0,
                              file_offset, file_length);
   if (base == nullptr) {
diff --git a/base/strings.cpp b/base/strings.cpp
index 2d6eef0..bb3167e 100644
--- a/base/strings.cpp
+++ b/base/strings.cpp
@@ -87,58 +87,33 @@
 template std::string Join(const std::vector<std::string>&, const std::string&);
 template std::string Join(const std::vector<const char*>&, const std::string&);
 
-bool StartsWith(const std::string& s, const char* prefix) {
-  return strncmp(s.c_str(), prefix, strlen(prefix)) == 0;
+bool StartsWith(std::string_view s, std::string_view prefix) {
+  return s.substr(0, prefix.size()) == prefix;
 }
 
-bool StartsWith(const std::string& s, const std::string& prefix) {
-  return strncmp(s.c_str(), prefix.c_str(), prefix.size()) == 0;
+bool StartsWith(std::string_view s, char prefix) {
+  return !s.empty() && s.front() == prefix;
 }
 
-bool StartsWith(const std::string& s, char prefix) {
-  return *s.c_str() == prefix;  // Use c_str() to guarantee there is at least a '\0'.
+bool StartsWithIgnoreCase(std::string_view s, std::string_view prefix) {
+  return s.size() >= prefix.size() && strncasecmp(s.data(), prefix.data(), prefix.size()) == 0;
 }
 
-bool StartsWithIgnoreCase(const std::string& s, const char* prefix) {
-  return strncasecmp(s.c_str(), prefix, strlen(prefix)) == 0;
+bool EndsWith(std::string_view s, std::string_view suffix) {
+  return s.size() >= suffix.size() && s.substr(s.size() - suffix.size(), suffix.size()) == suffix;
 }
 
-bool StartsWithIgnoreCase(const std::string& s, const std::string& prefix) {
-  return strncasecmp(s.c_str(), prefix.c_str(), prefix.size()) == 0;
+bool EndsWith(std::string_view s, char suffix) {
+  return !s.empty() && s.back() == suffix;
 }
 
-static bool EndsWith(const std::string& s, const char* suffix, size_t suffix_length,
-                     bool case_sensitive) {
-  size_t string_length = s.size();
-  if (suffix_length > string_length) {
-    return false;
-  }
-  size_t offset = string_length - suffix_length;
-  return (case_sensitive ? strncmp : strncasecmp)(s.c_str() + offset, suffix, suffix_length) == 0;
+bool EndsWithIgnoreCase(std::string_view s, std::string_view suffix) {
+  return s.size() >= suffix.size() &&
+         strncasecmp(s.data() + (s.size() - suffix.size()), suffix.data(), suffix.size()) == 0;
 }
 
-bool EndsWith(const std::string& s, const char* suffix) {
-  return EndsWith(s, suffix, strlen(suffix), true);
-}
-
-bool EndsWith(const std::string& s, const std::string& suffix) {
-  return EndsWith(s, suffix.c_str(), suffix.size(), true);
-}
-
-bool EndsWith(const std::string& s, char suffix) {
-  return EndsWith(s, &suffix, 1, true);
-}
-
-bool EndsWithIgnoreCase(const std::string& s, const char* suffix) {
-  return EndsWith(s, suffix, strlen(suffix), false);
-}
-
-bool EndsWithIgnoreCase(const std::string& s, const std::string& suffix) {
-  return EndsWith(s, suffix.c_str(), suffix.size(), false);
-}
-
-bool EqualsIgnoreCase(const std::string& lhs, const std::string& rhs) {
-  return strcasecmp(lhs.c_str(), rhs.c_str()) == 0;
+bool EqualsIgnoreCase(std::string_view lhs, std::string_view rhs) {
+  return lhs.size() == rhs.size() && strncasecmp(lhs.data(), rhs.data(), lhs.size()) == 0;
 }
 
 }  // namespace base
diff --git a/bootstat/OWNERS b/bootstat/OWNERS
index 7fe0443..50b2097 100644
--- a/bootstat/OWNERS
+++ b/bootstat/OWNERS
@@ -1 +1,2 @@
 jhawkins@google.com
+salyzyn@google.com
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index ed955ea..1ce0ec4 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -293,6 +293,12 @@
     {"kernel_panic,dsps", 166},
     {"kernel_panic,wcnss", 167},
     {"kernel_panic,_sde_encoder_phys_cmd_handle_ppdone_timeout", 168},
+    {"recovery,quiescent", 169},
+    {"reboot,quiescent", 170},
+    {"reboot,rtc", 171},
+    {"reboot,dm-verity_device_corrupted", 172},
+    {"reboot,dm-verity_enforcing", 173},
+    {"reboot,keys_clear", 174},
 };
 
 // Converts a string value representing the reason the system booted to an
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 76c5ade..0cf3378 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -218,13 +218,11 @@
         "liblog",
         "libminijail",
         "libnativehelper",
+        "libunwindstack",
     ],
 
     static_libs: [
         "libdebuggerd",
-        "libdexfile_external",  // libunwindstack dependency
-        "libdexfile_support",  // libunwindstack dependency
-        "libunwindstack",
     ],
 
     local_include_dirs: [
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 610b96b..60eb241 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -26,6 +26,7 @@
 
 #include <chrono>
 
+#include <android-base/cmsg.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
@@ -41,6 +42,7 @@
 
 using namespace std::chrono_literals;
 
+using android::base::SendFileDescriptors;
 using android::base::unique_fd;
 
 static bool send_signal(pid_t pid, const DebuggerdDumpType dump_type) {
@@ -146,15 +148,16 @@
     PLOG(ERROR) << "failed to set pipe buffer size";
   }
 
-  if (send_fd(set_timeout(sockfd), &req, sizeof(req), std::move(pipe_write)) != sizeof(req)) {
+  ssize_t rc = SendFileDescriptors(set_timeout(sockfd), &req, sizeof(req), pipe_write.get());
+  pipe_write.reset();
+  if (rc != sizeof(req)) {
     PLOG(ERROR) << "libdebuggerd_client: failed to send output fd to tombstoned";
     return false;
   }
 
   // Check to make sure we've successfully registered.
   InterceptResponse response;
-  ssize_t rc =
-      TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
+  rc = TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
   if (rc == 0) {
     LOG(ERROR) << "libdebuggerd_client: failed to read initial response from tombstoned: "
                << "timeout reached?";
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index bea8b43..64df53e 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -32,6 +32,7 @@
 #include <android/fdsan.h>
 #include <android/set_abort_message.h>
 
+#include <android-base/cmsg.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/macros.h>
@@ -53,6 +54,8 @@
 #include "util.h"
 
 using namespace std::chrono_literals;
+
+using android::base::SendFileDescriptors;
 using android::base::unique_fd;
 
 #if defined(__LP64__)
@@ -123,12 +126,14 @@
 
   ASSERT_GE(pipe_buffer_size, 1024 * 1024);
 
-  if (send_fd(intercept_fd->get(), &req, sizeof(req), std::move(output_pipe_write)) != sizeof(req)) {
+  ssize_t rc = SendFileDescriptors(intercept_fd->get(), &req, sizeof(req), output_pipe_write.get());
+  output_pipe_write.reset();
+  if (rc != sizeof(req)) {
     FAIL() << "failed to send output fd to tombstoned: " << strerror(errno);
   }
 
   InterceptResponse response;
-  ssize_t rc = TEMP_FAILURE_RETRY(read(intercept_fd->get(), &response, sizeof(response)));
+  rc = TEMP_FAILURE_RETRY(read(intercept_fd->get(), &response, sizeof(response)));
   if (rc == -1) {
     FAIL() << "failed to read response from tombstoned: " << strerror(errno);
   } else if (rc == 0) {
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index a064ca0..bca5e36 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -383,7 +383,9 @@
 
     execle(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, debuggerd_dump_type,
            nullptr, nullptr);
-    fatal_errno("exec failed");
+    async_safe_format_log(ANDROID_LOG_FATAL, "libc", "failed to exec crash_dump helper: %s",
+                          strerror(errno));
+    return 1;
   }
 
   input_write.reset();
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 8798ad3..c08afda 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -316,7 +316,7 @@
   std::shared_ptr<unwindstack::Memory>& process_memory = unwinder->GetProcessMemory();
 
   std::string line;
-  for (unwindstack::MapInfo* map_info : *maps) {
+  for (auto const& map_info : *maps) {
     line = "    ";
     if (print_fault_address_marker) {
       if (addr < map_info->start) {
diff --git a/debuggerd/tombstoned/intercept_manager.cpp b/debuggerd/tombstoned/intercept_manager.cpp
index c446dbb..7d25c50 100644
--- a/debuggerd/tombstoned/intercept_manager.cpp
+++ b/debuggerd/tombstoned/intercept_manager.cpp
@@ -24,6 +24,7 @@
 #include <event2/event.h>
 #include <event2/listener.h>
 
+#include <android-base/cmsg.h>
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
@@ -31,6 +32,7 @@
 #include "protocol.h"
 #include "util.h"
 
+using android::base::ReceiveFileDescriptors;
 using android::base::unique_fd;
 
 static void intercept_close_cb(evutil_socket_t sockfd, short event, void* arg) {
@@ -96,7 +98,8 @@
   {
     unique_fd rcv_fd;
     InterceptRequest intercept_request;
-    ssize_t result = recv_fd(sockfd, &intercept_request, sizeof(intercept_request), &rcv_fd);
+    ssize_t result =
+        ReceiveFileDescriptors(sockfd, &intercept_request, sizeof(intercept_request), &rcv_fd);
 
     if (result == -1) {
       PLOG(WARNING) << "failed to read from intercept socket";
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index ad92067..bbeb181 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -31,6 +31,7 @@
 #include <event2/listener.h>
 #include <event2/thread.h>
 
+#include <android-base/cmsg.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
@@ -45,6 +46,7 @@
 #include "intercept_manager.h"
 
 using android::base::GetIntProperty;
+using android::base::SendFileDescriptors;
 using android::base::StringPrintf;
 using android::base::unique_fd;
 
@@ -224,7 +226,10 @@
   TombstonedCrashPacket response = {
     .packet_type = CrashPacketType::kPerformDump
   };
-  ssize_t rc = send_fd(crash->crash_socket_fd, &response, sizeof(response), std::move(output_fd));
+  ssize_t rc =
+      SendFileDescriptors(crash->crash_socket_fd, &response, sizeof(response), output_fd.get());
+  output_fd.reset();
+
   if (rc == -1) {
     PLOG(WARNING) << "failed to send response to CrashRequest";
     goto fail;
diff --git a/debuggerd/tombstoned/tombstoned_client.cpp b/debuggerd/tombstoned/tombstoned_client.cpp
index bdb4c1a..2c23c98 100644
--- a/debuggerd/tombstoned/tombstoned_client.cpp
+++ b/debuggerd/tombstoned/tombstoned_client.cpp
@@ -21,6 +21,7 @@
 
 #include <utility>
 
+#include <android-base/cmsg.h>
 #include <android-base/unique_fd.h>
 #include <async_safe/log.h>
 #include <cutils/sockets.h>
@@ -28,6 +29,7 @@
 #include "protocol.h"
 #include "util.h"
 
+using android::base::ReceiveFileDescriptors;
 using android::base::unique_fd;
 
 bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* output_fd,
@@ -53,7 +55,7 @@
   }
 
   unique_fd tmp_output_fd;
-  ssize_t rc = recv_fd(sockfd, &packet, sizeof(packet), &tmp_output_fd);
+  ssize_t rc = ReceiveFileDescriptors(sockfd, &packet, sizeof(packet), &tmp_output_fd);
   if (rc == -1) {
     async_safe_format_log(ANDROID_LOG_ERROR, "libc",
                           "failed to read response to DumpRequest packet: %s", strerror(errno));
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
index 50c5efc..a37b3b9 100644
--- a/debuggerd/util.cpp
+++ b/debuggerd/util.cpp
@@ -24,73 +24,9 @@
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
 #include "protocol.h"
 
-using android::base::unique_fd;
-
-ssize_t send_fd(int sockfd, const void* data, size_t len, unique_fd fd) {
-  char cmsg_buf[CMSG_SPACE(sizeof(int))];
-
-  iovec iov = { .iov_base = const_cast<void*>(data), .iov_len = len };
-  msghdr msg = {
-    .msg_iov = &iov, .msg_iovlen = 1, .msg_control = cmsg_buf, .msg_controllen = sizeof(cmsg_buf),
-  };
-  auto cmsg = CMSG_FIRSTHDR(&msg);
-  cmsg->cmsg_level = SOL_SOCKET;
-  cmsg->cmsg_type = SCM_RIGHTS;
-  cmsg->cmsg_len = CMSG_LEN(sizeof(int));
-  *reinterpret_cast<int*>(CMSG_DATA(cmsg)) = fd.get();
-
-  return TEMP_FAILURE_RETRY(sendmsg(sockfd, &msg, 0));
-}
-
-ssize_t recv_fd(int sockfd, void* _Nonnull data, size_t len, unique_fd* _Nullable out_fd) {
-  char cmsg_buf[CMSG_SPACE(sizeof(int))];
-
-  iovec iov = { .iov_base = const_cast<void*>(data), .iov_len = len };
-  msghdr msg = {
-    .msg_iov = &iov,
-    .msg_iovlen = 1,
-    .msg_control = cmsg_buf,
-    .msg_controllen = sizeof(cmsg_buf),
-    .msg_flags = 0,
-  };
-  auto cmsg = CMSG_FIRSTHDR(&msg);
-  cmsg->cmsg_level = SOL_SOCKET;
-  cmsg->cmsg_type = SCM_RIGHTS;
-  cmsg->cmsg_len = CMSG_LEN(sizeof(int));
-
-  ssize_t result = TEMP_FAILURE_RETRY(recvmsg(sockfd, &msg, 0));
-  if (result == -1) {
-    return -1;
-  }
-
-  unique_fd fd;
-  bool received_fd = msg.msg_controllen == sizeof(cmsg_buf);
-  if (received_fd) {
-    fd.reset(*reinterpret_cast<int*>(CMSG_DATA(cmsg)));
-  }
-
-  if ((msg.msg_flags & MSG_TRUNC) != 0) {
-    errno = EFBIG;
-    return -1;
-  } else if ((msg.msg_flags & MSG_CTRUNC) != 0) {
-    errno = ERANGE;
-    return -1;
-  }
-
-  if (out_fd) {
-    *out_fd = std::move(fd);
-  } else if (received_fd) {
-    errno = ERANGE;
-    return -1;
-  }
-
-  return result;
-}
-
 std::string get_process_name(pid_t pid) {
   std::string result = "<unknown>";
   android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/cmdline", pid), &result);
diff --git a/debuggerd/util.h b/debuggerd/util.h
index 8260b44..e964423 100644
--- a/debuggerd/util.h
+++ b/debuggerd/util.h
@@ -21,29 +21,5 @@
 #include <sys/cdefs.h>
 #include <sys/types.h>
 
-#include <android-base/unique_fd.h>
-
-// *** WARNING ***
-// tombstoned's sockets are SOCK_SEQPACKET sockets.
-// Short reads are treated as errors and short writes are assumed to not happen.
-
-// Sends a packet with an attached fd.
-ssize_t send_fd(int sockfd, const void* _Nonnull data, size_t len, android::base::unique_fd fd);
-
-// Receives a packet and optionally, its attached fd.
-// If out_fd is non-null, packets can optionally have an attached fd.
-// If out_fd is null, received packets must not have an attached fd.
-//
-// Errors:
-//   EOVERFLOW: sockfd is SOCK_DGRAM or SOCK_SEQPACKET and buffer is too small.
-//              The first len bytes of the packet are stored in data, but the
-//              rest of the packet is dropped.
-//   ERANGE:    too many file descriptors were attached to the packet.
-//   ENOMSG:    not enough file descriptors were attached to the packet.
-//
-//   plus any errors returned by the underlying recvmsg.
-ssize_t recv_fd(int sockfd, void* _Nonnull data, size_t len,
-                android::base::unique_fd* _Nullable out_fd);
-
 std::string get_process_name(pid_t pid);
 std::string get_thread_name(pid_t tid);
diff --git a/adf/Android.bp b/deprecated-adf/Android.bp
similarity index 100%
rename from adf/Android.bp
rename to deprecated-adf/Android.bp
diff --git a/adf/OWNERS b/deprecated-adf/OWNERS
similarity index 100%
rename from adf/OWNERS
rename to deprecated-adf/OWNERS
diff --git a/adf/libadf/Android.bp b/deprecated-adf/libadf/Android.bp
similarity index 100%
rename from adf/libadf/Android.bp
rename to deprecated-adf/libadf/Android.bp
diff --git a/adf/libadf/adf.cpp b/deprecated-adf/libadf/adf.cpp
similarity index 100%
rename from adf/libadf/adf.cpp
rename to deprecated-adf/libadf/adf.cpp
diff --git a/adf/libadf/include/adf/adf.h b/deprecated-adf/libadf/include/adf/adf.h
similarity index 100%
rename from adf/libadf/include/adf/adf.h
rename to deprecated-adf/libadf/include/adf/adf.h
diff --git a/adf/libadf/include/video/adf.h b/deprecated-adf/libadf/include/video/adf.h
similarity index 100%
rename from adf/libadf/include/video/adf.h
rename to deprecated-adf/libadf/include/video/adf.h
diff --git a/adf/libadf/original-kernel-headers/video/adf.h b/deprecated-adf/libadf/original-kernel-headers/video/adf.h
similarity index 100%
rename from adf/libadf/original-kernel-headers/video/adf.h
rename to deprecated-adf/libadf/original-kernel-headers/video/adf.h
diff --git a/adf/libadf/tests/Android.bp b/deprecated-adf/libadf/tests/Android.bp
similarity index 100%
rename from adf/libadf/tests/Android.bp
rename to deprecated-adf/libadf/tests/Android.bp
diff --git a/adf/libadf/tests/adf_test.cpp b/deprecated-adf/libadf/tests/adf_test.cpp
similarity index 100%
rename from adf/libadf/tests/adf_test.cpp
rename to deprecated-adf/libadf/tests/adf_test.cpp
diff --git a/adf/libadfhwc/Android.bp b/deprecated-adf/libadfhwc/Android.bp
similarity index 100%
rename from adf/libadfhwc/Android.bp
rename to deprecated-adf/libadfhwc/Android.bp
diff --git a/adf/libadfhwc/adfhwc.cpp b/deprecated-adf/libadfhwc/adfhwc.cpp
similarity index 100%
rename from adf/libadfhwc/adfhwc.cpp
rename to deprecated-adf/libadfhwc/adfhwc.cpp
diff --git a/adf/libadfhwc/include/adfhwc/adfhwc.h b/deprecated-adf/libadfhwc/include/adfhwc/adfhwc.h
similarity index 100%
rename from adf/libadfhwc/include/adfhwc/adfhwc.h
rename to deprecated-adf/libadfhwc/include/adfhwc/adfhwc.h
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index 65a5247..fea0a77 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -403,7 +403,7 @@
 RetCode FastBootDriver::HandleResponse(std::string* response, std::vector<std::string>* info,
                                        int* dsize) {
     char status[FB_RESPONSE_SZ + 1];
-    auto start = std::chrono::system_clock::now();
+    auto start = std::chrono::steady_clock::now();
 
     auto set_response = [response](std::string s) {
         if (response) *response = std::move(s);
@@ -414,7 +414,7 @@
 
     // erase response
     set_response("");
-    while ((std::chrono::system_clock::now() - start) < std::chrono::seconds(RESP_TIMEOUT)) {
+    while ((std::chrono::steady_clock::now() - start) < std::chrono::seconds(RESP_TIMEOUT)) {
         int r = transport_->Read(status, FB_RESPONSE_SZ);
         if (r < 0) {
             error_ = ErrnoStr("Status read failed");
@@ -427,6 +427,11 @@
             std::string tmp = input.substr(strlen("INFO"));
             info_(tmp);
             add_info(std::move(tmp));
+            // We may receive one or more INFO packets during long operations,
+            // e.g. flash/erase if they are back by slow media like NAND/NOR
+            // flash. In that case, reset the timer since it's not a real
+            // timeout.
+            start = std::chrono::steady_clock::now();
         } else if (android::base::StartsWith(input, "OKAY")) {
             set_response(input.substr(strlen("OKAY")));
             return SUCCESS;
diff --git a/fastboot/fuzzy_fastboot/fixtures.cpp b/fastboot/fuzzy_fastboot/fixtures.cpp
index 2da452d..c23da01 100644
--- a/fastboot/fuzzy_fastboot/fixtures.cpp
+++ b/fastboot/fuzzy_fastboot/fixtures.cpp
@@ -55,6 +55,8 @@
 #include "test_utils.h"
 #include "usb_transport_sniffer.h"
 
+using namespace std::literals::chrono_literals;
+
 namespace fastboot {
 
 int FastBootTest::MatchFastboot(usb_ifc_info* info, const char* local_serial) {
@@ -163,6 +165,26 @@
     }
 }
 
+void FastBootTest::ReconnectFastbootDevice() {
+    fb.reset();
+    transport.reset();
+    while (UsbStillAvailible())
+        ;
+    printf("WAITING FOR DEVICE\n");
+    // Need to wait for device
+    const auto matcher = [](usb_ifc_info* info) -> int { return MatchFastboot(info, nullptr); };
+    while (!transport) {
+        std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
+        if (usb) {
+            transport = std::unique_ptr<UsbTransportSniffer>(
+                    new UsbTransportSniffer(std::move(usb), serial_port));
+        }
+        std::this_thread::sleep_for(1s);
+    }
+    device_path = cb_scratch;
+    fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
+}
+
 void FastBootTest::SetLockState(bool unlock, bool assert_change) {
     if (!fb) {
         return;
@@ -201,25 +223,8 @@
         std::string cmd = unlock ? "unlock" : "lock";
         ASSERT_EQ(fb->RawCommand("flashing " + cmd, &resp), SUCCESS)
                 << "Attempting to change locked state, but 'flashing" + cmd + "' command failed";
-        fb.reset();
-        transport.reset();
         printf("PLEASE RESPOND TO PROMPT FOR '%sing' BOOTLOADER ON DEVICE\n", cmd.c_str());
-        while (UsbStillAvailible())
-            ;  // Wait for disconnect
-        printf("WAITING FOR DEVICE");
-        // Need to wait for device
-        const auto matcher = [](usb_ifc_info* info) -> int { return MatchFastboot(info, nullptr); };
-        while (!transport) {
-            std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
-            if (usb) {
-                transport = std::unique_ptr<UsbTransportSniffer>(
-                        new UsbTransportSniffer(std::move(usb), serial_port));
-            }
-            std::this_thread::sleep_for(std::chrono::milliseconds(1000));
-            putchar('.');
-        }
-        device_path = cb_scratch;
-        fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
+        ReconnectFastbootDevice();
         if (assert_change) {
             ASSERT_EQ(fb->GetVar("unlocked", &resp), SUCCESS) << "getvar:unlocked failed";
             ASSERT_EQ(resp, unlock ? "yes" : "no")
diff --git a/fastboot/fuzzy_fastboot/fixtures.h b/fastboot/fuzzy_fastboot/fixtures.h
index 1f73d2d..7c8d54d 100644
--- a/fastboot/fuzzy_fastboot/fixtures.h
+++ b/fastboot/fuzzy_fastboot/fixtures.h
@@ -48,6 +48,7 @@
     static int MatchFastboot(usb_ifc_info* info, const char* local_serial = nullptr);
     bool UsbStillAvailible();
     bool UserSpaceFastboot();
+    void ReconnectFastbootDevice();
 
   protected:
     RetCode DownloadCommand(uint32_t size, std::string* response = nullptr,
@@ -87,6 +88,7 @@
 // differently
 class BasicFunctionality : public ModeTest<true> {};
 class Conformance : public ModeTest<true> {};
+class LogicalPartitionCompliance : public ModeTest<true> {};
 class UnlockPermissions : public ModeTest<true> {};
 class LockPermissions : public ModeTest<false> {};
 
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index ef34771..7ffc7d5 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -177,6 +177,93 @@
     }
 }
 
+// Test commands related to super partition
+TEST_F(LogicalPartitionCompliance, SuperPartition) {
+    ASSERT_TRUE(UserSpaceFastboot());
+    std::string partition_type;
+    // getvar partition-type:super must fail for retrofit devices because the
+    // partition does not exist.
+    if (fb->GetVar("partition-type:super", &partition_type) == SUCCESS) {
+        std::string is_logical;
+        EXPECT_EQ(fb->GetVar("is-logical:super", &is_logical), SUCCESS)
+                << "getvar is-logical:super failed";
+        EXPECT_EQ(is_logical, "no") << "super must not be a logical partition";
+        std::string super_name;
+        EXPECT_EQ(fb->GetVar("super-partition-name", &super_name), SUCCESS)
+                << "'getvar super-partition-name' failed";
+        EXPECT_EQ(super_name, "super") << "'getvar super-partition-name' must return 'super' for "
+                                          "device with a super partition";
+    }
+}
+
+// Test 'fastboot getvar is-logical'
+TEST_F(LogicalPartitionCompliance, GetVarIsLogical) {
+    ASSERT_TRUE(UserSpaceFastboot());
+    std::string has_slot;
+    EXPECT_EQ(fb->GetVar("has-slot:system", &has_slot), SUCCESS) << "getvar has-slot:system failed";
+    std::string is_logical_cmd;
+    if (has_slot == "yes") {
+        std::string current_slot;
+        EXPECT_EQ(fb->GetVar("current-slot", &current_slot), SUCCESS)
+                << "getvar current-slot failed";
+        is_logical_cmd = "is-logical:system_" + current_slot;
+    } else {
+        is_logical_cmd = "is-logical:system";
+    }
+    std::string is_logical;
+    EXPECT_EQ(fb->GetVar(is_logical_cmd, &is_logical), SUCCESS) << "getvar is-logical failed";
+    ASSERT_EQ(is_logical, "yes");
+}
+
+TEST_F(LogicalPartitionCompliance, FastbootRebootTest) {
+    ASSERT_TRUE(UserSpaceFastboot());
+    GTEST_LOG_(INFO) << "Rebooting to bootloader mode";
+    // Test 'fastboot reboot bootloader' from fastbootd
+    fb->RebootTo("bootloader");
+
+    // Test fastboot reboot fastboot from bootloader
+    ReconnectFastbootDevice();
+    ASSERT_FALSE(UserSpaceFastboot());
+    GTEST_LOG_(INFO) << "Rebooting back to fastbootd mode";
+    fb->RebootTo("fastboot");
+
+    ReconnectFastbootDevice();
+    ASSERT_TRUE(UserSpaceFastboot());
+}
+
+// Testing creation/resize/delete of logical partitions
+TEST_F(LogicalPartitionCompliance, CreateResizeDeleteLP) {
+    ASSERT_TRUE(UserSpaceFastboot());
+    GTEST_LOG_(INFO) << "Testing 'fastboot create-logical-partition' command";
+    EXPECT_EQ(fb->CreatePartition("test_partition_a", "0"), SUCCESS)
+            << "create-logical-partition failed";
+    GTEST_LOG_(INFO) << "Testing 'fastboot resize-logical-partition' command";
+    EXPECT_EQ(fb->ResizePartition("test_partition_a", "4096"), SUCCESS)
+            << "resize-logical-partition failed";
+    std::vector<char> buf(4096);
+
+    GTEST_LOG_(INFO) << "Flashing a logical partition..";
+    EXPECT_EQ(fb->FlashPartition("test_partition_a", buf), SUCCESS)
+            << "flash logical -partition failed";
+    GTEST_LOG_(INFO) << "Rebooting to bootloader mode";
+    // Reboot to bootloader mode and attempt to flash the logical partitions
+    fb->RebootTo("bootloader");
+
+    ReconnectFastbootDevice();
+    ASSERT_FALSE(UserSpaceFastboot());
+    GTEST_LOG_(INFO) << "Attempt to flash a logical partition..";
+    EXPECT_EQ(fb->FlashPartition("test_partition", buf), DEVICE_FAIL)
+            << "flash logical partition must fail in bootloader";
+    GTEST_LOG_(INFO) << "Rebooting back to fastbootd mode";
+    fb->RebootTo("fastboot");
+
+    ReconnectFastbootDevice();
+    ASSERT_TRUE(UserSpaceFastboot());
+    GTEST_LOG_(INFO) << "Testing 'fastboot delete-logical-partition' command";
+    EXPECT_EQ(fb->DeletePartition("test_partition_a"), SUCCESS)
+            << "delete logical-partition failed";
+}
+
 // Conformance tests
 TEST_F(Conformance, GetVar) {
     std::string product;
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 974e13e..4ee9624 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -111,3 +111,35 @@
         "libgsi_headers",
     ],
 }
+
+cc_binary {
+    name: "remount",
+    defaults: ["fs_mgr_defaults"],
+    static_libs: [
+        "libavb_user",
+    ],
+    shared_libs: [
+        "libbootloader_message",
+        "libbase",
+        "libcrypto",
+        "libfec",
+        "libfs_mgr",
+    ],
+    header_libs: [
+        "libcutils_headers",
+    ],
+    srcs: [
+        "fs_mgr_remount.cpp",
+    ],
+    cppflags: [
+        "-DALLOW_ADBD_DISABLE_VERITY=0",
+    ],
+    product_variables: {
+        debuggable: {
+            cppflags: [
+                "-UALLOW_ADBD_DISABLE_VERITY",
+                "-DALLOW_ADBD_DISABLE_VERITY=1",
+            ],
+        },
+    },
+}
diff --git a/fs_mgr/README.overlayfs.md b/fs_mgr/README.overlayfs.md
index 2aac260..f89e598 100644
--- a/fs_mgr/README.overlayfs.md
+++ b/fs_mgr/README.overlayfs.md
@@ -94,7 +94,7 @@
   and thus free dynamic partition space.
 - Kernel must have CONFIG_OVERLAY_FS=y and will need to be patched
   with "*overlayfs: override_creds=off option bypass creator_cred*"
-  if kernel is higher than 4.6.
+  if kernel is 4.4 or higher.
   The patch is available on the upstream mailing list and the latest as of
   Feb 8 2019 is https://lore.kernel.org/patchwork/patch/1009299/.
   This patch adds an override_creds _mount_ option to overlayfs that
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index b69e773..d26f2d5 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -307,7 +307,7 @@
         return false;
     }
 
-    if (pread(fd, sb, sizeof(*sb), 1024) != sizeof(*sb)) {
+    if (TEMP_FAILURE_RETRY(pread(fd, sb, sizeof(*sb), 1024)) != sizeof(*sb)) {
         PERROR << "Can't read '" << blk_device << "' superblock";
         return false;
     }
@@ -326,6 +326,17 @@
     return true;
 }
 
+// exported silent version of the above that just answer the question is_ext4
+bool fs_mgr_is_ext4(const std::string& blk_device) {
+    android::base::ErrnoRestorer restore;
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (fd < 0) return false;
+    ext4_super_block sb;
+    if (TEMP_FAILURE_RETRY(pread(fd, &sb, sizeof(sb), 1024)) != sizeof(sb)) return false;
+    if (!is_ext4_superblock_valid(&sb)) return false;
+    return true;
+}
+
 // Some system images do not have tune2fs for licensing reasons.
 // Detect these and skip running it.
 static bool tune2fs_available(void) {
@@ -494,11 +505,12 @@
         return false;
     }
 
-    if (pread(fd, &sb1, sizeof(sb1), F2FS_SUPER_OFFSET) != sizeof(sb1)) {
+    if (TEMP_FAILURE_RETRY(pread(fd, &sb1, sizeof(sb1), F2FS_SUPER_OFFSET)) != sizeof(sb1)) {
         PERROR << "Can't read '" << blk_device << "' superblock1";
         return false;
     }
-    if (pread(fd, &sb2, sizeof(sb2), F2FS_BLKSIZE + F2FS_SUPER_OFFSET) != sizeof(sb2)) {
+    if (TEMP_FAILURE_RETRY(pread(fd, &sb2, sizeof(sb2), F2FS_BLKSIZE + F2FS_SUPER_OFFSET)) !=
+        sizeof(sb2)) {
         PERROR << "Can't read '" << blk_device << "' superblock2";
         return false;
     }
@@ -511,6 +523,23 @@
     return true;
 }
 
+// exported silent version of the above that just answer the question is_f2fs
+bool fs_mgr_is_f2fs(const std::string& blk_device) {
+    android::base::ErrnoRestorer restore;
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (fd < 0) return false;
+    __le32 sb;
+    if (TEMP_FAILURE_RETRY(pread(fd, &sb, sizeof(sb), F2FS_SUPER_OFFSET)) != sizeof(sb)) {
+        return false;
+    }
+    if (sb == cpu_to_le32(F2FS_SUPER_MAGIC)) return true;
+    if (TEMP_FAILURE_RETRY(pread(fd, &sb, sizeof(sb), F2FS_BLKSIZE + F2FS_SUPER_OFFSET)) !=
+        sizeof(sb)) {
+        return false;
+    }
+    return sb == cpu_to_le32(F2FS_SUPER_MAGIC);
+}
+
 //
 // Prepare the filesystem on the given block device to be mounted.
 //
@@ -1089,7 +1118,7 @@
                 // Skips mounting the device.
                 continue;
             }
-        } else if (!current_entry.avb_key.empty()) {
+        } else if (!current_entry.avb_keys.empty()) {
             if (AvbHandle::SetUpStandaloneAvbHashtree(&current_entry) == AvbHashtreeResult::kFail) {
                 LERROR << "Failed to set up AVB on standalone partition: "
                        << current_entry.mount_point << ", skipping!";
@@ -1098,9 +1127,7 @@
             }
         } else if ((current_entry.fs_mgr_flags.verify)) {
             int rc = fs_mgr_setup_verity(&current_entry, true);
-            if (__android_log_is_debuggable() &&
-                    (rc == FS_MGR_SETUP_VERITY_DISABLED ||
-                     rc == FS_MGR_SETUP_VERITY_SKIPPED)) {
+            if (rc == FS_MGR_SETUP_VERITY_DISABLED || rc == FS_MGR_SETUP_VERITY_SKIPPED) {
                 LINFO << "Verity disabled";
             } else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
                 LERROR << "Could not set up verified partition, skipping!";
@@ -1322,7 +1349,7 @@
                 // Skips mounting the device.
                 continue;
             }
-        } else if (!fstab_entry.avb_key.empty()) {
+        } else if (!fstab_entry.avb_keys.empty()) {
             if (AvbHandle::SetUpStandaloneAvbHashtree(&fstab_entry) == AvbHashtreeResult::kFail) {
                 LERROR << "Failed to set up AVB on standalone partition: "
                        << fstab_entry.mount_point << ", skipping!";
@@ -1331,9 +1358,7 @@
             }
         } else if (fstab_entry.fs_mgr_flags.verify) {
             int rc = fs_mgr_setup_verity(&fstab_entry, true);
-            if (__android_log_is_debuggable() &&
-                    (rc == FS_MGR_SETUP_VERITY_DISABLED ||
-                     rc == FS_MGR_SETUP_VERITY_SKIPPED)) {
+            if (rc == FS_MGR_SETUP_VERITY_DISABLED || rc == FS_MGR_SETUP_VERITY_SKIPPED) {
                 LINFO << "Verity disabled";
             } else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
                 LERROR << "Could not set up verified partition, skipping!";
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 4659add..2f1e41f 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -307,6 +307,8 @@
             } else {
                 entry->logical_blk_size = val;
             }
+        } else if (StartsWith(flag, "avb_keys=")) {  // must before the following "avb"
+            entry->avb_keys = arg;
         } else if (StartsWith(flag, "avb")) {
             entry->fs_mgr_flags.avb = true;
             entry->vbmeta_partition = arg;
@@ -325,8 +327,6 @@
             }
         } else if (StartsWith(flag, "zram_backing_dev_path=")) {
             entry->zram_backing_dev_path = arg;
-        } else if (StartsWith(flag, "avb_key=")) {
-            entry->avb_key = arg;
         } else {
             LWARNING << "Warning: unknown flag: " << flag;
         }
@@ -607,10 +607,14 @@
     return userdata;
 }
 
-void EraseFstabEntry(Fstab* fstab, const std::string& mount_point) {
+bool EraseFstabEntry(Fstab* fstab, const std::string& mount_point) {
     auto iter = std::remove_if(fstab->begin(), fstab->end(),
                                [&](const auto& entry) { return entry.mount_point == mount_point; });
-    fstab->erase(iter, fstab->end());
+    if (iter != fstab->end()) {
+        fstab->erase(iter, fstab->end());
+        return true;
+    }
+    return false;
 }
 
 void TransformFstabForGsi(Fstab* fstab) {
@@ -628,11 +632,13 @@
         userdata = BuildGsiUserdataFstabEntry();
     }
 
-    EraseFstabEntry(fstab, "/system");
-    EraseFstabEntry(fstab, "/data");
+    if (EraseFstabEntry(fstab, "/system")) {
+        fstab->emplace_back(BuildGsiSystemFstabEntry());
+    }
 
-    fstab->emplace_back(BuildGsiSystemFstabEntry());
-    fstab->emplace_back(userdata);
+    if (EraseFstabEntry(fstab, "/data")) {
+        fstab->emplace_back(userdata);
+    }
 }
 
 }  // namespace
@@ -753,7 +759,8 @@
                          .fs_type = "ext4",
                          .flags = MS_RDONLY,
                          .fs_options = "barrier=1",
-                         .avb_key = "/gsi.avbpubkey",
+                         // could add more keys separated by ':'.
+                         .avb_keys = "/avb/gsi.avbpubkey:",
                          .logical_partition_name = "system"};
     system.fs_mgr_flags.wait = true;
     system.fs_mgr_flags.logical = true;
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 9364b2d..dea4844 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -157,7 +157,12 @@
         fs_mgr_update_logical_partition(entry);
     }
     auto save_errno = errno;
+    errno = 0;
     auto has_shared_blocks = fs_mgr_has_shared_blocks(entry->mount_point, entry->blk_device);
+    // special case for first stage init for system as root (taimen)
+    if (!has_shared_blocks && (errno == ENOENT) && (entry->blk_device == "/dev/root")) {
+        has_shared_blocks = true;
+    }
     errno = save_errno;
     return has_shared_blocks;
 }
@@ -538,6 +543,10 @@
         if (!fs_mgr_rw_access(device_path)) return false;
     }
 
+    auto f2fs = fs_mgr_is_f2fs(device_path);
+    auto ext4 = fs_mgr_is_ext4(device_path);
+    if (!f2fs && !ext4) return false;
+
     if (setfscreatecon(kOverlayfsFileContext)) {
         PERROR << "setfscreatecon " << kOverlayfsFileContext;
     }
@@ -549,6 +558,8 @@
     entry.blk_device = device_path;
     entry.mount_point = kScratchMountPoint;
     entry.fs_type = mnt_type;
+    if ((mnt_type == "f2fs") && !f2fs) entry.fs_type = "ext4";
+    if ((mnt_type == "ext4") && !ext4) entry.fs_type = "f2fs";
     entry.flags = MS_RELATIME;
     if (readonly) {
         entry.flags |= MS_RDONLY;
@@ -558,12 +569,13 @@
     auto save_errno = errno;
     auto mounted = fs_mgr_do_mount_one(entry) == 0;
     if (!mounted) {
-        if (mnt_type == "f2fs") {
+        if ((entry.fs_type == "f2fs") && ext4) {
             entry.fs_type = "ext4";
-        } else {
+            mounted = fs_mgr_do_mount_one(entry) == 0;
+        } else if ((entry.fs_type == "ext4") && f2fs) {
             entry.fs_type = "f2fs";
+            mounted = fs_mgr_do_mount_one(entry) == 0;
         }
-        mounted = fs_mgr_do_mount_one(entry) == 0;
         if (!mounted) save_errno = errno;
     }
     setfscreatecon(nullptr);
@@ -764,9 +776,10 @@
 
     // in recovery, fastbootd, or gsi mode, not allowed!
     if (fs_mgr_access("/system/bin/recovery")) return true;
-    if (android::gsi::IsGsiRunning()) return true;
-
-    return false;
+    auto save_errno = errno;
+    auto ret = android::gsi::IsGsiRunning();
+    errno = save_errno;
+    return ret;
 }
 
 }  // namespace
@@ -1026,7 +1039,7 @@
     if (major > 4) {
         return OverlayfsValidResult::kNotSupported;
     }
-    if (minor > 6) {
+    if (minor > 3) {
         return OverlayfsValidResult::kNotSupported;
     }
     return OverlayfsValidResult::kOk;
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 166c32b..11602ea 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -100,3 +100,6 @@
 const std::string& get_android_dt_dir();
 bool is_dt_compatible();
 int load_verity_state(const android::fs_mgr::FstabEntry& entry, int* mode);
+
+bool fs_mgr_is_ext4(const std::string& blk_device);
+bool fs_mgr_is_f2fs(const std::string& blk_device);
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
new file mode 100644
index 0000000..24044d8
--- /dev/null
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <libavb_user/libavb_user.h>
+#include <stdio.h>
+#include <sys/mount.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+#include <unistd.h>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <bootloader_message/bootloader_message.h>
+#include <cutils/android_reboot.h>
+#include <fec/io.h>
+#include <fs_mgr_overlayfs.h>
+#include <fs_mgr_priv.h>
+#include <fstab/fstab.h>
+
+namespace {
+
+[[noreturn]] void usage(int exit_status) {
+    LOG(INFO) << getprogname()
+              << " [-h] [-R] [-T fstab_file] [partition]...\n"
+                 "\t-h --help\tthis help\n"
+                 "\t-R --reboot\tdisable verity & reboot to facilitate remount\n"
+                 "\t-T --fstab\tcustom fstab file location\n"
+                 "\tpartition\tspecific partition(s) (empty does all)\n"
+                 "\n"
+                 "Remount specified partition(s) read-write, by name or mount point.\n"
+                 "-R notwithstanding, verity must be disabled on partition(s).";
+
+    ::exit(exit_status);
+}
+
+bool remountable_partition(const android::fs_mgr::FstabEntry& entry) {
+    if (entry.fs_mgr_flags.vold_managed) return false;
+    if (entry.fs_mgr_flags.recovery_only) return false;
+    if (entry.fs_mgr_flags.slot_select_other) return false;
+    if (!(entry.flags & MS_RDONLY)) return false;
+    if (entry.fs_type == "vfat") return false;
+    return true;
+}
+
+const std::string system_mount_point(const android::fs_mgr::FstabEntry& entry) {
+    if (entry.mount_point == "/") return "/system";
+    return entry.mount_point;
+}
+
+const android::fs_mgr::FstabEntry* is_wrapped(const android::fs_mgr::Fstab& overlayfs_candidates,
+                                              const android::fs_mgr::FstabEntry& entry) {
+    auto mount_point = system_mount_point(entry);
+    auto it = std::find_if(overlayfs_candidates.begin(), overlayfs_candidates.end(),
+                           [&mount_point](const auto& entry) {
+                               return android::base::StartsWith(mount_point,
+                                                                system_mount_point(entry) + "/");
+                           });
+    if (it == overlayfs_candidates.end()) return nullptr;
+    return &(*it);
+}
+
+void MyLogger(android::base::LogId id, android::base::LogSeverity severity, const char* tag,
+              const char* file, unsigned int line, const char* message) {
+    static const char log_characters[] = "VD\0WEFF";
+    if (severity < sizeof(log_characters)) {
+        auto severity_char = log_characters[severity];
+        if (severity_char) fprintf(stderr, "%c ", severity_char);
+    }
+    fprintf(stderr, "%s\n", message);
+
+    static auto logd = android::base::LogdLogger();
+    logd(id, severity, tag, file, line, message);
+}
+
+[[noreturn]] void reboot(bool dedupe) {
+    if (dedupe) {
+        LOG(INFO) << "The device will now reboot to recovery and attempt un-deduplication.";
+    } else {
+        LOG(INFO) << "Successfully disabled verity\nrebooting device";
+    }
+    ::sync();
+    android::base::SetProperty(ANDROID_RB_PROPERTY, dedupe ? "reboot,recovery" : "reboot,remount");
+    ::sleep(60);
+    ::exit(0);  // SUCCESS
+}
+
+}  // namespace
+
+int main(int argc, char* argv[]) {
+    android::base::InitLogging(argv, MyLogger);
+
+    enum {
+        SUCCESS,
+        NOT_USERDEBUG,
+        BADARG,
+        NOT_ROOT,
+        NO_FSTAB,
+        UNKNOWN_PARTITION,
+        INVALID_PARTITION,
+        VERITY_PARTITION,
+        BAD_OVERLAY,
+        NO_MOUNTS,
+        REMOUNT_FAILED,
+    } retval = SUCCESS;
+
+    // If somehow this executable is delivered on a "user" build, it can
+    // not function, so providing a clear message to the caller rather than
+    // letting if fall through and provide a lot of confusing failure messages.
+    if (!ALLOW_ADBD_DISABLE_VERITY || (android::base::GetProperty("ro.debuggable", "0") != "1")) {
+        LOG(ERROR) << "only functions on userdebug or eng builds";
+        return NOT_USERDEBUG;
+    }
+
+    const char* fstab_file = nullptr;
+    auto can_reboot = false;
+
+    struct option longopts[] = {
+            {"fstab", required_argument, nullptr, 'T'},
+            {"help", no_argument, nullptr, 'h'},
+            {"reboot", no_argument, nullptr, 'R'},
+            {0, 0, nullptr, 0},
+    };
+    for (int opt; (opt = ::getopt_long(argc, argv, "hRT:", longopts, nullptr)) != -1;) {
+        switch (opt) {
+            case 'R':
+                can_reboot = true;
+                break;
+            case 'T':
+                if (fstab_file) {
+                    LOG(ERROR) << "Cannot supply two fstabs: -T " << fstab_file << " -T" << optarg;
+                    usage(BADARG);
+                }
+                fstab_file = optarg;
+                break;
+            default:
+                LOG(ERROR) << "Bad Argument -" << char(opt);
+                usage(BADARG);
+                break;
+            case 'h':
+                usage(SUCCESS);
+                break;
+        }
+    }
+
+    // Make sure we are root.
+    if (::getuid() != 0) {
+        LOG(ERROR) << "must be run as root";
+        return NOT_ROOT;
+    }
+
+    // Read the selected fstab.
+    android::fs_mgr::Fstab fstab;
+    auto fstab_read = false;
+    if (fstab_file) {
+        fstab_read = android::fs_mgr::ReadFstabFromFile(fstab_file, &fstab);
+    } else {
+        fstab_read = android::fs_mgr::ReadDefaultFstab(&fstab);
+        // Manufacture a / entry from /proc/mounts if missing.
+        if (!GetEntryForMountPoint(&fstab, "/system") && !GetEntryForMountPoint(&fstab, "/")) {
+            android::fs_mgr::Fstab mounts;
+            if (android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts)) {
+                if (auto entry = GetEntryForMountPoint(&mounts, "/")) {
+                    if (entry->fs_type != "rootfs") fstab.emplace_back(*entry);
+                }
+            }
+        }
+    }
+    if (!fstab_read || fstab.empty()) {
+        PLOG(ERROR) << "Failed to read fstab";
+        return NO_FSTAB;
+    }
+
+    // Generate the list of supported overlayfs mount points.
+    auto overlayfs_candidates = fs_mgr_overlayfs_candidate_list(fstab);
+
+    // Generate the all remountable partitions sub-list
+    android::fs_mgr::Fstab all;
+    for (auto const& entry : fstab) {
+        if (!remountable_partition(entry)) continue;
+        if (overlayfs_candidates.empty() ||
+            GetEntryForMountPoint(&overlayfs_candidates, entry.mount_point) ||
+            (is_wrapped(overlayfs_candidates, entry) == nullptr)) {
+            all.emplace_back(entry);
+        }
+    }
+
+    // Parse the unique list of valid partition arguments.
+    android::fs_mgr::Fstab partitions;
+    for (; argc > optind; ++optind) {
+        auto partition = std::string(argv[optind]);
+        if (partition.empty()) continue;
+        if (partition == "/") partition = "/system";
+        auto find_part = [&partition](const auto& entry) {
+            const auto mount_point = system_mount_point(entry);
+            if (partition == mount_point) return true;
+            if (partition == android::base::Basename(mount_point)) return true;
+            return false;
+        };
+        // Do we know about the partition?
+        auto it = std::find_if(fstab.begin(), fstab.end(), find_part);
+        if (it == fstab.end()) {
+            LOG(ERROR) << "Unknown partition " << argv[optind] << ", skipping";
+            retval = UNKNOWN_PARTITION;
+            continue;
+        }
+        // Is that one covered by an existing overlayfs?
+        auto wrap = is_wrapped(overlayfs_candidates, *it);
+        if (wrap) {
+            LOG(INFO) << "partition " << argv[optind] << " covered by overlayfs for "
+                      << wrap->mount_point << ", switching";
+            partition = system_mount_point(*wrap);
+        }
+        // Is it a remountable partition?
+        it = std::find_if(all.begin(), all.end(), find_part);
+        if (it == all.end()) {
+            LOG(ERROR) << "Invalid partition " << argv[optind] << ", skipping";
+            retval = INVALID_PARTITION;
+            continue;
+        }
+        if (GetEntryForMountPoint(&partitions, it->mount_point) == nullptr) {
+            partitions.emplace_back(*it);
+        }
+    }
+
+    if (partitions.empty() && !retval) {
+        partitions = all;
+    }
+
+    // Check verity and optionally setup overlayfs backing.
+    auto reboot_later = false;
+    auto uses_overlayfs = fs_mgr_overlayfs_valid() != OverlayfsValidResult::kNotSupported;
+    for (auto it = partitions.begin(); it != partitions.end();) {
+        auto& entry = *it;
+        auto& mount_point = entry.mount_point;
+        if (fs_mgr_is_verity_enabled(entry)) {
+            retval = VERITY_PARTITION;
+            if (android::base::GetProperty("ro.boot.vbmeta.devices_state", "") != "locked") {
+                if (AvbOps* ops = avb_ops_user_new()) {
+                    auto ret = avb_user_verity_set(
+                            ops, android::base::GetProperty("ro.boot.slot_suffix", "").c_str(),
+                            false);
+                    avb_ops_user_free(ops);
+                    if (ret) {
+                        LOG(WARNING) << "Disable verity for " << mount_point;
+                        reboot_later = can_reboot;
+                        if (reboot_later) {
+                            // w/o overlayfs available, also check for dedupe
+                            if (!uses_overlayfs) {
+                                ++it;
+                                continue;
+                            }
+                            reboot(false);
+                        }
+                    } else if (fs_mgr_set_blk_ro(entry.blk_device, false)) {
+                        fec::io fh(entry.blk_device.c_str(), O_RDWR);
+                        if (fh && fh.set_verity_status(false)) {
+                            LOG(WARNING) << "Disable verity for " << mount_point;
+                            reboot_later = can_reboot;
+                            if (reboot_later && !uses_overlayfs) {
+                                ++it;
+                                continue;
+                            }
+                        }
+                    }
+                }
+            }
+            LOG(ERROR) << "Skipping " << mount_point;
+            it = partitions.erase(it);
+            continue;
+        }
+
+        auto change = false;
+        errno = 0;
+        if (fs_mgr_overlayfs_setup(nullptr, mount_point.c_str(), &change)) {
+            if (change) {
+                LOG(INFO) << "Using overlayfs for " << mount_point;
+            }
+        } else if (errno) {
+            PLOG(ERROR) << "Overlayfs setup for " << mount_point << " failed, skipping";
+            retval = BAD_OVERLAY;
+            it = partitions.erase(it);
+            continue;
+        }
+        ++it;
+    }
+
+    if (partitions.empty()) {
+        if (reboot_later) reboot(false);
+        LOG(WARNING) << "No partitions to remount";
+        return retval;
+    }
+
+    // Mount overlayfs.
+    errno = 0;
+    if (!fs_mgr_overlayfs_mount_all(&partitions) && errno) {
+        retval = BAD_OVERLAY;
+        PLOG(ERROR) << "Can not mount overlayfs for partitions";
+    }
+
+    // Get actual mounts _after_ overlayfs has been added.
+    android::fs_mgr::Fstab mounts;
+    if (!android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts) || mounts.empty()) {
+        PLOG(ERROR) << "Failed to read /proc/mounts";
+        retval = NO_MOUNTS;
+    }
+
+    // Remount selected partitions.
+    for (auto& entry : partitions) {
+        // unlock the r/o key for the mount point device
+        if (entry.fs_mgr_flags.logical) {
+            fs_mgr_update_logical_partition(&entry);
+        }
+        auto blk_device = entry.blk_device;
+        auto mount_point = entry.mount_point;
+
+        for (auto it = mounts.rbegin(); it != mounts.rend(); ++it) {
+            auto& rentry = *it;
+            if (mount_point == rentry.mount_point) {
+                blk_device = rentry.blk_device;
+                break;
+            }
+            if ((mount_point == "/") && (rentry.mount_point == "/system")) {
+                blk_device = rentry.blk_device;
+                mount_point = "/system";
+                break;
+            }
+        }
+        if (blk_device == "/dev/root") {
+            auto from_fstab = GetEntryForMountPoint(&fstab, mount_point);
+            if (from_fstab) blk_device = from_fstab->blk_device;
+        }
+        fs_mgr_set_blk_ro(blk_device, false);
+
+        // Now remount!
+        if (::mount(blk_device.c_str(), mount_point.c_str(), entry.fs_type.c_str(), MS_REMOUNT,
+                    nullptr) == 0) {
+            continue;
+        }
+        if ((errno == EINVAL) && (mount_point != entry.mount_point)) {
+            mount_point = entry.mount_point;
+            if (::mount(blk_device.c_str(), mount_point.c_str(), entry.fs_type.c_str(), MS_REMOUNT,
+                        nullptr) == 0) {
+                continue;
+            }
+        }
+        PLOG(WARNING) << "failed to remount partition dev:" << blk_device << " mnt:" << mount_point;
+        // If errno = EROFS at this point, we are dealing with r/o
+        // filesystem types like squashfs, erofs or ext4 dedupe. We will
+        // consider such a device that does not have CONFIG_OVERLAY_FS
+        // in the kernel as a misconfigured; except for ext4 dedupe.
+        if ((errno == EROFS) && can_reboot) {
+            const std::vector<std::string> msg = {"--fsck_unshare_blocks"};
+            std::string err;
+            if (write_bootloader_message(msg, &err)) reboot(true);
+            LOG(ERROR) << "Failed to set bootloader message: " << err;
+            errno = EROFS;
+        }
+        retval = REMOUNT_FAILED;
+    }
+
+    if (reboot_later) reboot(false);
+
+    return retval;
+}
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index a3d9fdd..e811447 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -55,7 +55,7 @@
     std::string zram_loopback_path;
     uint64_t zram_loopback_size = 512 * 1024 * 1024;  // 512MB by default;
     std::string zram_backing_dev_path;
-    std::string avb_key;
+    std::string avb_keys;
 
     struct FsMgrFlags {
         bool wait : 1;
diff --git a/fs_mgr/libfiemap_writer/Android.bp b/fs_mgr/libfiemap_writer/Android.bp
index 33c3cad..32fc3d2 100644
--- a/fs_mgr/libfiemap_writer/Android.bp
+++ b/fs_mgr/libfiemap_writer/Android.bp
@@ -20,13 +20,17 @@
     recovery_available: true,
     export_include_dirs: ["include"],
     cflags: [
-        // TODO(b/121211685): Allows us to create a skeleton of required classes
-        "-Wno-unused-private-field",
-        "-Wno-unused-parameter",
+        "-D_FILE_OFFSET_BITS=64",
     ],
 
     srcs: [
         "fiemap_writer.cpp",
+        "split_fiemap_writer.cpp",
+        "utility.cpp",
+    ],
+
+    static_libs: [
+        "libext4_utils",
     ],
 
     header_libs: [
@@ -37,6 +41,9 @@
 
 cc_test {
     name: "fiemap_writer_test",
+    cflags: [
+        "-D_FILE_OFFSET_BITS=64",
+    ],
     static_libs: [
         "libbase",
         "libdm",
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer.cpp b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
index b9b75f8..85589cc 100644
--- a/fs_mgr/libfiemap_writer/fiemap_writer.cpp
+++ b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
@@ -27,6 +27,7 @@
 #include <sys/vfs.h>
 #include <unistd.h>
 
+#include <limits>
 #include <string>
 #include <utility>
 #include <vector>
@@ -50,6 +51,9 @@
         FIEMAP_EXTENT_NOT_ALIGNED | FIEMAP_EXTENT_DATA_INLINE | FIEMAP_EXTENT_DATA_TAIL |
         FIEMAP_EXTENT_UNWRITTEN | FIEMAP_EXTENT_SHARED | FIEMAP_EXTENT_MERGED;
 
+// Large file support must be enabled.
+static_assert(sizeof(off_t) == sizeof(uint64_t));
+
 static inline void cleanup(const std::string& file_path, bool created) {
     if (created) {
         unlink(file_path.c_str());
@@ -126,7 +130,8 @@
     return DeviceMapperStackPop(bdev_next, bdev_raw);
 }
 
-static bool FileToBlockDevicePath(const std::string& file_path, std::string* bdev_path) {
+bool FiemapWriter::GetBlockDeviceForFile(const std::string& file_path, std::string* bdev_path,
+                                         bool* uses_dm) {
     struct stat sb;
     if (stat(file_path.c_str(), &sb)) {
         PLOG(ERROR) << "Failed to get stat for: " << file_path;
@@ -146,6 +151,10 @@
         return false;
     }
 
+    if (uses_dm) {
+        *uses_dm = (bdev_raw != bdev);
+    }
+
     LOG(DEBUG) << "Popped device (" << bdev_raw << ") from device mapper stack starting with ("
                << bdev << ")";
 
@@ -195,17 +204,21 @@
         return false;
     }
 
-    if (file_size % sfs.f_bsize) {
-        LOG(ERROR) << "File size " << file_size << " is not aligned to optimal block size "
-                   << sfs.f_bsize << " for file " << file_path;
+    if (!sfs.f_bsize) {
+        LOG(ERROR) << "Unsupported block size: " << sfs.f_bsize;
         return false;
     }
 
     // Check if the filesystem is of supported types.
-    // Only ext4 and f2fs are tested and supported.
-    if ((sfs.f_type != EXT4_SUPER_MAGIC) && (sfs.f_type != F2FS_SUPER_MAGIC)) {
-        LOG(ERROR) << "Unsupported file system type: 0x" << std::hex << sfs.f_type;
-        return false;
+    // Only ext4, f2fs, and vfat are tested and supported.
+    switch (sfs.f_type) {
+        case EXT4_SUPER_MAGIC:
+        case F2FS_SUPER_MAGIC:
+        case MSDOS_SUPER_MAGIC:
+            break;
+        default:
+            LOG(ERROR) << "Unsupported file system type: 0x" << std::hex << sfs.f_type;
+            return false;
     }
 
     uint64_t available_bytes = sfs.f_bsize * sfs.f_bavail;
@@ -219,15 +232,65 @@
     return true;
 }
 
+static bool FallocateFallback(int file_fd, uint64_t block_size, uint64_t file_size,
+                              const std::string& file_path,
+                              const std::function<bool(uint64_t, uint64_t)>& on_progress) {
+    // Even though this is much faster than writing zeroes, it is still slow
+    // enough that we need to fire the progress callback periodically. To
+    // easily achieve this, we seek in chunks. We use 1000 chunks since
+    // normally we only fire the callback on 1/1000th increments.
+    uint64_t bytes_per_chunk = std::max(file_size / 1000, block_size);
+
+    // Seek just to the end of each chunk and write a single byte, causing
+    // the filesystem to allocate blocks.
+    off_t cursor = 0;
+    off_t end = static_cast<off_t>(file_size);
+    while (cursor < end) {
+        cursor = std::min(static_cast<off_t>(cursor + bytes_per_chunk), end);
+        auto rv = TEMP_FAILURE_RETRY(lseek(file_fd, cursor - 1, SEEK_SET));
+        if (rv < 0) {
+            PLOG(ERROR) << "Failed to lseek " << file_path;
+            return false;
+        }
+        if (rv != cursor - 1) {
+            LOG(ERROR) << "Seek returned wrong offset " << rv << " for file " << file_path;
+            return false;
+        }
+        char buffer[] = {0};
+        if (!android::base::WriteFully(file_fd, buffer, 1)) {
+            PLOG(ERROR) << "Write failed: " << file_path;
+            return false;
+        }
+        if (on_progress && !on_progress(cursor, file_size)) {
+            return false;
+        }
+    }
+    return true;
+}
+
 static bool AllocateFile(int file_fd, const std::string& file_path, uint64_t blocksz,
-                         uint64_t file_size, std::function<bool(uint64_t, uint64_t)> on_progress) {
+                         uint64_t file_size, unsigned int fs_type,
+                         std::function<bool(uint64_t, uint64_t)> on_progress) {
     // Reserve space for the file on the file system and write it out to make sure the extents
     // don't come back unwritten. Return from this function with the kernel file offset set to 0.
     // If the filesystem is f2fs, then we also PIN the file on disk to make sure the blocks
     // aren't moved around.
-    if (fallocate(file_fd, FALLOC_FL_ZERO_RANGE, 0, file_size)) {
-        PLOG(ERROR) << "Failed to allocate space for file: " << file_path << " size: " << file_size;
-        return false;
+    switch (fs_type) {
+        case EXT4_SUPER_MAGIC:
+        case F2FS_SUPER_MAGIC:
+            if (fallocate(file_fd, FALLOC_FL_ZERO_RANGE, 0, file_size)) {
+                PLOG(ERROR) << "Failed to allocate space for file: " << file_path
+                            << " size: " << file_size;
+                return false;
+            }
+            break;
+        case MSDOS_SUPER_MAGIC:
+            // fallocate() is not supported, and not needed, since VFAT does not support holes.
+            // Instead we can perform a much faster allocation.
+            return FallocateFallback(file_fd, blocksz, file_size, file_path, on_progress);
+        default:
+            LOG(ERROR) << "Missing fallocate() support for file system " << fs_type;
+            return false;
     }
 
     // write zeroes in 'blocksz' byte increments until we reach file_size to make sure the data
@@ -246,16 +309,19 @@
     }
 
     int permille = -1;
-    for (; offset < file_size; offset += blocksz) {
+    while (offset < file_size) {
         if (!::android::base::WriteFully(file_fd, buffer.get(), blocksz)) {
             PLOG(ERROR) << "Failed to write" << blocksz << " bytes at offset" << offset
                         << " in file " << file_path;
             return false;
         }
+
+        offset += blocksz;
+
         // Don't invoke the callback every iteration - wait until a significant
         // chunk (here, 1/1000th) of the data has been processed.
         int new_permille = (static_cast<uint64_t>(offset) * 1000) / file_size;
-        if (new_permille != permille) {
+        if (new_permille != permille && static_cast<uint64_t>(offset) != file_size) {
             if (on_progress && !on_progress(offset, file_size)) {
                 return false;
             }
@@ -282,9 +348,9 @@
 }
 
 static bool PinFile(int file_fd, const std::string& file_path, uint32_t fs_type) {
-    if (fs_type == EXT4_SUPER_MAGIC) {
-        // No pinning necessary for ext4. The blocks, once allocated, are expected
-        // to be fixed.
+    if (fs_type != F2FS_SUPER_MAGIC) {
+        // No pinning necessary for ext4/msdos. The blocks, once allocated, are
+        // expected to be fixed.
         return true;
     }
 
@@ -319,9 +385,9 @@
 }
 
 static bool IsFilePinned(int file_fd, const std::string& file_path, uint32_t fs_type) {
-    if (fs_type == EXT4_SUPER_MAGIC) {
-        // No pinning necessary for ext4. The blocks, once allocated, are expected
-        // to be fixed.
+    if (fs_type != F2FS_SUPER_MAGIC) {
+        // No pinning necessary for ext4 or vfat. The blocks, once allocated,
+        // are expected to be fixed.
         return true;
     }
 
@@ -433,6 +499,54 @@
     return last_extent_seen;
 }
 
+static bool ReadFibmap(int file_fd, const std::string& file_path,
+                       std::vector<struct fiemap_extent>* extents) {
+    struct stat s;
+    if (fstat(file_fd, &s)) {
+        PLOG(ERROR) << "Failed to stat " << file_path;
+        return false;
+    }
+
+    unsigned int blksize;
+    if (ioctl(file_fd, FIGETBSZ, &blksize) < 0) {
+        PLOG(ERROR) << "Failed to get FIGETBSZ for " << file_path;
+        return false;
+    }
+    if (!blksize) {
+        LOG(ERROR) << "Invalid filesystem block size: " << blksize;
+        return false;
+    }
+
+    uint64_t num_blocks = (s.st_size + blksize - 1) / blksize;
+    if (num_blocks > std::numeric_limits<uint32_t>::max()) {
+        LOG(ERROR) << "Too many blocks for FIBMAP (" << num_blocks << ")";
+        return false;
+    }
+
+    for (uint32_t last_block, block_number = 0; block_number < num_blocks; block_number++) {
+        uint32_t block = block_number;
+        if (ioctl(file_fd, FIBMAP, &block)) {
+            PLOG(ERROR) << "Failed to get FIBMAP for file " << file_path;
+            return false;
+        }
+        if (!block) {
+            LOG(ERROR) << "Logical block " << block_number << " is a hole, which is not supported";
+            return false;
+        }
+
+        if (!extents->empty() && block == last_block + 1) {
+            extents->back().fe_length += blksize;
+        } else {
+            extents->push_back(fiemap_extent{.fe_logical = block_number,
+                                             .fe_physical = static_cast<uint64_t>(block) * blksize,
+                                             .fe_length = static_cast<uint64_t>(blksize),
+                                             .fe_flags = 0});
+        }
+        last_block = block;
+    }
+    return true;
+}
+
 FiemapUniquePtr FiemapWriter::Open(const std::string& file_path, uint64_t file_size, bool create,
                                    std::function<bool(uint64_t, uint64_t)> progress) {
     // if 'create' is false, open an existing file and do not truncate.
@@ -458,7 +572,7 @@
     }
 
     std::string bdev_path;
-    if (!FileToBlockDevicePath(abs_path, &bdev_path)) {
+    if (!GetBlockDeviceForFile(abs_path, &bdev_path)) {
         LOG(ERROR) << "Failed to get block dev path for file: " << file_path;
         cleanup(abs_path, create);
         return nullptr;
@@ -495,8 +609,13 @@
         return nullptr;
     }
 
+    // Align up to the nearest block size.
+    if (file_size % blocksz) {
+        file_size += blocksz - (file_size % blocksz);
+    }
+
     if (create) {
-        if (!AllocateFile(file_fd, abs_path, blocksz, file_size, std::move(progress))) {
+        if (!AllocateFile(file_fd, abs_path, blocksz, file_size, fs_type, std::move(progress))) {
             LOG(ERROR) << "Failed to allocate file: " << abs_path << " of size: " << file_size
                        << " bytes";
             cleanup(abs_path, create);
@@ -513,28 +632,35 @@
 
     // now allocate the FiemapWriter and start setting it up
     FiemapUniquePtr fmap(new FiemapWriter());
-    if (!ReadFiemap(file_fd, abs_path, &fmap->extents_)) {
-        LOG(ERROR) << "Failed to read fiemap of file: " << abs_path;
-        cleanup(abs_path, create);
-        return nullptr;
+    switch (fs_type) {
+        case EXT4_SUPER_MAGIC:
+        case F2FS_SUPER_MAGIC:
+            if (!ReadFiemap(file_fd, abs_path, &fmap->extents_)) {
+                LOG(ERROR) << "Failed to read fiemap of file: " << abs_path;
+                cleanup(abs_path, create);
+                return nullptr;
+            }
+            break;
+        case MSDOS_SUPER_MAGIC:
+            if (!ReadFibmap(file_fd, abs_path, &fmap->extents_)) {
+                LOG(ERROR) << "Failed to read fibmap of file: " << abs_path;
+                cleanup(abs_path, create);
+                return nullptr;
+            }
+            break;
     }
 
     fmap->file_path_ = abs_path;
     fmap->bdev_path_ = bdev_path;
-    fmap->file_fd_ = std::move(file_fd);
     fmap->file_size_ = file_size;
     fmap->bdev_size_ = bdevsz;
     fmap->fs_type_ = fs_type;
     fmap->block_size_ = blocksz;
 
-    LOG(INFO) << "Successfully created FiemapWriter for file " << abs_path << " on block device "
-              << bdev_path;
+    LOG(VERBOSE) << "Successfully created FiemapWriter for file " << abs_path << " on block device "
+                 << bdev_path;
     return fmap;
 }
 
-bool FiemapWriter::Read(off64_t off, uint8_t* buffer, uint64_t size) {
-    return false;
-}
-
 }  // namespace fiemap_writer
 }  // namespace android
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
index 41fa959..ca51689 100644
--- a/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
+++ b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
@@ -22,6 +22,7 @@
 #include <sys/mount.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <sys/vfs.h>
 #include <unistd.h>
 
 #include <string>
@@ -32,23 +33,29 @@
 #include <android-base/unique_fd.h>
 #include <gtest/gtest.h>
 #include <libdm/loop_control.h>
-
 #include <libfiemap_writer/fiemap_writer.h>
+#include <libfiemap_writer/split_fiemap_writer.h>
+
+#include "utility.h"
+
+namespace android {
+namespace fiemap_writer {
 
 using namespace std;
+using namespace std::string_literals;
 using namespace android::fiemap_writer;
 using unique_fd = android::base::unique_fd;
 using LoopDevice = android::dm::LoopDevice;
 
-std::string testbdev = "";
+std::string gTestDir;
 uint64_t testfile_size = 536870912;  // default of 512MiB
+size_t gBlockSize = 0;
 
 class FiemapWriterTest : public ::testing::Test {
   protected:
     void SetUp() override {
         const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
-        std::string exec_dir = ::android::base::GetExecutableDirectory();
-        testfile = ::android::base::StringPrintf("%s/testdata/%s", exec_dir.c_str(), tinfo->name());
+        testfile = gTestDir + "/"s + tinfo->name();
     }
 
     void TearDown() override { unlink(testfile.c_str()); }
@@ -57,6 +64,24 @@
     std::string testfile;
 };
 
+class SplitFiemapTest : public ::testing::Test {
+  protected:
+    void SetUp() override {
+        const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
+        testfile = gTestDir + "/"s + tinfo->name();
+    }
+
+    void TearDown() override {
+        std::string message;
+        if (!SplitFiemap::RemoveSplitFiles(testfile, &message)) {
+            cerr << "Could not remove all split files: " << message;
+        }
+    }
+
+    // name of the file we use for testing
+    std::string testfile;
+};
+
 TEST_F(FiemapWriterTest, CreateImpossiblyLargeFile) {
     // Try creating a file of size ~100TB but aligned to
     // 512 byte to make sure block alignment tests don't
@@ -69,39 +94,48 @@
 
 TEST_F(FiemapWriterTest, CreateUnalignedFile) {
     // Try creating a file of size 4097 bytes which is guaranteed
-    // to be unaligned to all known block sizes. The creation must
-    // fail.
-    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 4097);
-    EXPECT_EQ(fptr, nullptr);
-    EXPECT_EQ(access(testfile.c_str(), F_OK), -1);
-    EXPECT_EQ(errno, ENOENT);
+    // to be unaligned to all known block sizes.
+    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize + 1);
+    ASSERT_NE(fptr, nullptr);
+    ASSERT_EQ(fptr->size(), gBlockSize * 2);
 }
 
 TEST_F(FiemapWriterTest, CheckFilePath) {
-    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 4096);
+    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
     ASSERT_NE(fptr, nullptr);
-    EXPECT_EQ(fptr->size(), 4096);
+    EXPECT_EQ(fptr->size(), gBlockSize);
     EXPECT_EQ(fptr->file_path(), testfile);
     EXPECT_EQ(access(testfile.c_str(), F_OK), 0);
 }
 
+TEST_F(FiemapWriterTest, CheckFileSize) {
+    // Create a large-ish file and test that the expected size matches.
+    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 1024 * 1024 * 16);
+    ASSERT_NE(fptr, nullptr);
+
+    struct stat s;
+    ASSERT_EQ(stat(testfile.c_str(), &s), 0);
+    EXPECT_EQ(static_cast<uint64_t>(s.st_size), fptr->size());
+}
+
 TEST_F(FiemapWriterTest, CheckProgress) {
-    std::vector<uint64_t> expected{
-            0,
-            4096,
-    };
+    std::vector<uint64_t> expected;
     size_t invocations = 0;
     auto callback = [&](uint64_t done, uint64_t total) -> bool {
-        EXPECT_LT(invocations, expected.size());
+        if (invocations >= expected.size()) {
+            return false;
+        }
         EXPECT_EQ(done, expected[invocations]);
-        EXPECT_EQ(total, 4096);
+        EXPECT_EQ(total, gBlockSize);
         invocations++;
         return true;
     };
 
-    auto ptr = FiemapWriter::Open(testfile, 4096, true, std::move(callback));
+    expected.push_back(gBlockSize);
+
+    auto ptr = FiemapWriter::Open(testfile, gBlockSize, true, std::move(callback));
     EXPECT_NE(ptr, nullptr);
-    EXPECT_EQ(invocations, 2);
+    EXPECT_EQ(invocations, expected.size());
 }
 
 TEST_F(FiemapWriterTest, CheckPinning) {
@@ -111,9 +145,10 @@
 }
 
 TEST_F(FiemapWriterTest, CheckBlockDevicePath) {
-    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 4096);
-    EXPECT_EQ(fptr->size(), 4096);
-    EXPECT_EQ(fptr->bdev_path(), testbdev);
+    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
+    EXPECT_EQ(fptr->size(), gBlockSize);
+    EXPECT_EQ(fptr->bdev_path().find("/dev/block/"), size_t(0));
+    EXPECT_EQ(fptr->bdev_path().find("/dev/block/dm-"), string::npos);
 }
 
 TEST_F(FiemapWriterTest, CheckFileCreated) {
@@ -129,7 +164,7 @@
 
     struct stat sb;
     ASSERT_EQ(stat(testfile.c_str(), &sb), 0);
-    EXPECT_EQ(sb.st_size, testfile_size);
+    EXPECT_GE(sb.st_size, testfile_size);
 }
 
 TEST_F(FiemapWriterTest, CheckFileExtents) {
@@ -138,45 +173,223 @@
     EXPECT_GT(fptr->extents().size(), 0);
 }
 
-class TestExistingFile : public ::testing::Test {
-  protected:
-    void SetUp() override {
-        std::string exec_dir = ::android::base::GetExecutableDirectory();
-        unaligned_file_ = exec_dir + "/testdata/unaligned_file";
-        file_4k_ = exec_dir + "/testdata/file_4k";
-        file_32k_ = exec_dir + "/testdata/file_32k";
+TEST_F(FiemapWriterTest, ExistingFile) {
+    // Create the file.
+    { ASSERT_NE(FiemapWriter::Open(testfile, gBlockSize), nullptr); }
+    // Test that we can still open it.
+    {
+        auto ptr = FiemapWriter::Open(testfile, 0, false);
+        ASSERT_NE(ptr, nullptr);
+        EXPECT_GT(ptr->extents().size(), 0);
+    }
+}
 
-        CleanupFiles();
-        fptr_unaligned = FiemapWriter::Open(unaligned_file_, 4097);
-        fptr_4k = FiemapWriter::Open(file_4k_, 4096);
-        fptr_32k = FiemapWriter::Open(file_32k_, 32768);
+TEST_F(FiemapWriterTest, FileDeletedOnError) {
+    auto callback = [](uint64_t, uint64_t) -> bool { return false; };
+    auto ptr = FiemapWriter::Open(testfile, gBlockSize, true, std::move(callback));
+    EXPECT_EQ(ptr, nullptr);
+    EXPECT_EQ(access(testfile.c_str(), F_OK), -1);
+    EXPECT_EQ(errno, ENOENT);
+}
+
+TEST_F(FiemapWriterTest, MaxBlockSize) {
+    ASSERT_GT(DetermineMaximumFileSize(testfile), 0);
+}
+
+TEST_F(FiemapWriterTest, FibmapBlockAddressing) {
+    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
+    ASSERT_NE(fptr, nullptr);
+
+    switch (fptr->fs_type()) {
+        case F2FS_SUPER_MAGIC:
+        case EXT4_SUPER_MAGIC:
+            // Skip the test for FIEMAP supported filesystems. This is really
+            // because f2fs/ext4 have caches that seem to defeat reading back
+            // directly from the block device, and writing directly is too
+            // dangerous.
+            std::cout << "Skipping test, filesystem does not use FIBMAP\n";
+            return;
     }
 
-    void TearDown() { CleanupFiles(); }
+    bool uses_dm;
+    std::string bdev_path;
+    ASSERT_TRUE(FiemapWriter::GetBlockDeviceForFile(testfile, &bdev_path, &uses_dm));
 
-    void CleanupFiles() {
-        unlink(unaligned_file_.c_str());
-        unlink(file_4k_.c_str());
-        unlink(file_32k_.c_str());
+    if (uses_dm) {
+        // We could use a device-mapper wrapper here to bypass encryption, but
+        // really this test is for FIBMAP correctness on VFAT (where encryption
+        // is never used), so we don't bother.
+        std::cout << "Skipping test, block device is metadata encrypted\n";
+        return;
     }
 
-    std::string unaligned_file_;
-    std::string file_4k_;
-    std::string file_32k_;
-    FiemapUniquePtr fptr_unaligned;
-    FiemapUniquePtr fptr_4k;
-    FiemapUniquePtr fptr_32k;
-};
+    std::string data(fptr->size(), '\0');
+    for (size_t i = 0; i < data.size(); i++) {
+        data[i] = 'A' + static_cast<char>(data.size() % 26);
+    }
 
-TEST_F(TestExistingFile, ErrorChecks) {
-    EXPECT_EQ(fptr_unaligned, nullptr);
-    EXPECT_NE(fptr_4k, nullptr);
-    EXPECT_NE(fptr_32k, nullptr);
+    {
+        unique_fd fd(open(testfile.c_str(), O_WRONLY | O_CLOEXEC));
+        ASSERT_GE(fd, 0);
+        ASSERT_TRUE(android::base::WriteFully(fd, data.data(), data.size()));
+        ASSERT_EQ(fsync(fd), 0);
+    }
 
-    EXPECT_EQ(fptr_4k->size(), 4096);
-    EXPECT_EQ(fptr_32k->size(), 32768);
-    EXPECT_GT(fptr_4k->extents().size(), 0);
-    EXPECT_GT(fptr_32k->extents().size(), 0);
+    ASSERT_FALSE(fptr->extents().empty());
+    const auto& first_extent = fptr->extents()[0];
+
+    unique_fd bdev(open(fptr->bdev_path().c_str(), O_RDONLY | O_CLOEXEC));
+    ASSERT_GE(bdev, 0);
+
+    off_t where = first_extent.fe_physical;
+    ASSERT_EQ(lseek(bdev, where, SEEK_SET), where);
+
+    // Note: this will fail on encrypted folders.
+    std::string actual(data.size(), '\0');
+    ASSERT_GE(first_extent.fe_length, data.size());
+    ASSERT_TRUE(android::base::ReadFully(bdev, actual.data(), actual.size()));
+    EXPECT_EQ(memcmp(actual.data(), data.data(), data.size()), 0);
+}
+
+TEST_F(SplitFiemapTest, Create) {
+    auto ptr = SplitFiemap::Create(testfile, 1024 * 768, 1024 * 32);
+    ASSERT_NE(ptr, nullptr);
+
+    auto extents = ptr->extents();
+
+    // Destroy the fiemap, closing file handles. This should not delete them.
+    ptr = nullptr;
+
+    std::vector<std::string> files;
+    ASSERT_TRUE(SplitFiemap::GetSplitFileList(testfile, &files));
+    for (const auto& path : files) {
+        EXPECT_EQ(access(path.c_str(), F_OK), 0);
+    }
+
+    ASSERT_GE(extents.size(), files.size());
+}
+
+TEST_F(SplitFiemapTest, Open) {
+    {
+        auto ptr = SplitFiemap::Create(testfile, 1024 * 768, 1024 * 32);
+        ASSERT_NE(ptr, nullptr);
+    }
+
+    auto ptr = SplitFiemap::Open(testfile);
+    ASSERT_NE(ptr, nullptr);
+
+    auto extents = ptr->extents();
+    ASSERT_GE(extents.size(), 24);
+}
+
+TEST_F(SplitFiemapTest, DeleteOnFail) {
+    auto ptr = SplitFiemap::Create(testfile, 1024 * 1024 * 100, 1);
+    ASSERT_EQ(ptr, nullptr);
+
+    std::string first_file = testfile + ".0001";
+    ASSERT_NE(access(first_file.c_str(), F_OK), 0);
+    ASSERT_EQ(errno, ENOENT);
+    ASSERT_NE(access(testfile.c_str(), F_OK), 0);
+    ASSERT_EQ(errno, ENOENT);
+}
+
+static string ReadSplitFiles(const std::string& base_path, size_t num_files) {
+    std::string result;
+    for (int i = 0; i < num_files; i++) {
+        std::string path = base_path + android::base::StringPrintf(".%04d", i);
+        std::string data;
+        if (!android::base::ReadFileToString(path, &data)) {
+            return {};
+        }
+        result += data;
+    }
+    return result;
+}
+
+TEST_F(SplitFiemapTest, WriteWholeFile) {
+    static constexpr size_t kChunkSize = 32768;
+    static constexpr size_t kSize = kChunkSize * 3;
+    auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
+    ASSERT_NE(ptr, nullptr);
+
+    auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
+    for (size_t i = 0; i < kSize / sizeof(int); i++) {
+        buffer[i] = i;
+    }
+    ASSERT_TRUE(ptr->Write(buffer.get(), kSize));
+
+    std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
+    auto actual = ReadSplitFiles(testfile, 3);
+    ASSERT_EQ(expected.size(), actual.size());
+    EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
+}
+
+TEST_F(SplitFiemapTest, WriteFileInChunks1) {
+    static constexpr size_t kChunkSize = 32768;
+    static constexpr size_t kSize = kChunkSize * 3;
+    auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
+    ASSERT_NE(ptr, nullptr);
+
+    auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
+    for (size_t i = 0; i < kSize / sizeof(int); i++) {
+        buffer[i] = i;
+    }
+
+    // Write in chunks of 1000 (so some writes straddle the boundary of two
+    // files).
+    size_t bytes_written = 0;
+    while (bytes_written < kSize) {
+        size_t to_write = std::min(kSize - bytes_written, (size_t)1000);
+        char* data = reinterpret_cast<char*>(buffer.get()) + bytes_written;
+        ASSERT_TRUE(ptr->Write(data, to_write));
+        bytes_written += to_write;
+    }
+
+    std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
+    auto actual = ReadSplitFiles(testfile, 3);
+    ASSERT_EQ(expected.size(), actual.size());
+    EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
+}
+
+TEST_F(SplitFiemapTest, WriteFileInChunks2) {
+    static constexpr size_t kChunkSize = 32768;
+    static constexpr size_t kSize = kChunkSize * 3;
+    auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
+    ASSERT_NE(ptr, nullptr);
+
+    auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
+    for (size_t i = 0; i < kSize / sizeof(int); i++) {
+        buffer[i] = i;
+    }
+
+    // Write in chunks of 32KiB so every write is exactly at the end of the
+    // current file.
+    size_t bytes_written = 0;
+    while (bytes_written < kSize) {
+        size_t to_write = std::min(kSize - bytes_written, kChunkSize);
+        char* data = reinterpret_cast<char*>(buffer.get()) + bytes_written;
+        ASSERT_TRUE(ptr->Write(data, to_write));
+        bytes_written += to_write;
+    }
+
+    std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
+    auto actual = ReadSplitFiles(testfile, 3);
+    ASSERT_EQ(expected.size(), actual.size());
+    EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
+}
+
+TEST_F(SplitFiemapTest, WritePastEnd) {
+    static constexpr size_t kChunkSize = 32768;
+    static constexpr size_t kSize = kChunkSize * 3;
+    auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
+    ASSERT_NE(ptr, nullptr);
+
+    auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
+    for (size_t i = 0; i < kSize / sizeof(int); i++) {
+        buffer[i] = i;
+    }
+    ASSERT_TRUE(ptr->Write(buffer.get(), kSize));
+    ASSERT_FALSE(ptr->Write(buffer.get(), kSize));
 }
 
 class VerifyBlockWritesExt4 : public ::testing::Test {
@@ -263,17 +476,46 @@
     std::string fs_path;
 };
 
+bool DetermineBlockSize() {
+    struct statfs s;
+    if (statfs(gTestDir.c_str(), &s)) {
+        std::cerr << "Could not call statfs: " << strerror(errno) << "\n";
+        return false;
+    }
+    if (!s.f_bsize) {
+        std::cerr << "Invalid block size: " << s.f_bsize << "\n";
+        return false;
+    }
+
+    gBlockSize = s.f_bsize;
+    return true;
+}
+
+}  // namespace fiemap_writer
+}  // namespace android
+
+using namespace android::fiemap_writer;
+
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
     if (argc <= 1) {
-        cerr << "Filepath with its bdev path must be provided as follows:" << endl;
-        cerr << "  $ fiemap_writer_test </dev/block/XXXX" << endl;
-        cerr << "  where, /dev/block/XXX is the block device where the file resides" << endl;
+        cerr << "Usage: <test_dir> [file_size]\n";
+        cerr << "\n";
+        cerr << "Note: test_dir must be a writable, unencrypted directory.\n";
         exit(EXIT_FAILURE);
     }
     ::android::base::InitLogging(argv, ::android::base::StderrLogger);
 
-    testbdev = argv[1];
+    std::string tempdir = argv[1] + "/XXXXXX"s;
+    if (!mkdtemp(tempdir.data())) {
+        cerr << "unable to create tempdir on " << argv[1] << "\n";
+        exit(EXIT_FAILURE);
+    }
+    if (!android::base::Realpath(tempdir, &gTestDir)) {
+        cerr << "unable to find realpath for " << tempdir;
+        exit(EXIT_FAILURE);
+    }
+
     if (argc > 2) {
         testfile_size = strtoull(argv[2], NULL, 0);
         if (testfile_size == ULLONG_MAX) {
@@ -281,5 +523,14 @@
         }
     }
 
-    return RUN_ALL_TESTS();
+    if (!DetermineBlockSize()) {
+        exit(EXIT_FAILURE);
+    }
+
+    auto result = RUN_ALL_TESTS();
+
+    std::string cmd = "rm -rf " + gTestDir;
+    system(cmd.c_str());
+
+    return result;
 }
diff --git a/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h b/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
index edbae77..9486122 100644
--- a/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
+++ b/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
@@ -41,6 +41,9 @@
     // invoked, if create is true, while the file is being initialized. It receives the bytes
     // written and the number of total bytes. If the callback returns false, the operation will
     // fail.
+    //
+    // Note: when create is true, the file size will be aligned up to the nearest file system
+    // block.
     static FiemapUniquePtr Open(const std::string& file_path, uint64_t file_size,
                                 bool create = true,
                                 std::function<bool(uint64_t, uint64_t)> progress = {});
@@ -57,10 +60,12 @@
     // FiemapWriter::Open).
     static bool HasPinnedExtents(const std::string& file_path);
 
-    // The counter part of Write(). It is an error for the offset to be unaligned with
-    // the block device's block size.
-    // In case of error, the contents of buffer MUST be discarded.
-    bool Read(off64_t off, uint8_t* buffer, uint64_t size);
+    // Returns the underlying block device of a file. This will look past device-mapper layers.
+    // If an intermediate device-mapper layer would not maintain a 1:1 mapping (i.e. is a non-
+    // trivial dm-linear), then this will fail. If device-mapper nodes are encountered, then
+    // |uses_dm| will be set to true.
+    static bool GetBlockDeviceForFile(const std::string& file_path, std::string* bdev_path,
+                                      bool* uses_dm = nullptr);
 
     ~FiemapWriter() = default;
 
@@ -69,6 +74,7 @@
     const std::string& bdev_path() const { return bdev_path_; };
     uint64_t block_size() const { return block_size_; };
     const std::vector<struct fiemap_extent>& extents() { return extents_; };
+    uint32_t fs_type() const { return fs_type_; }
 
     // Non-copyable & Non-movable
     FiemapWriter(const FiemapWriter&) = delete;
@@ -82,9 +88,6 @@
     // Block device on which we have created the file.
     std::string bdev_path_;
 
-    // File descriptors for the file and block device
-    ::android::base::unique_fd file_fd_;
-
     // Size in bytes of the file this class is writing
     uint64_t file_size_;
 
diff --git a/fs_mgr/libfiemap_writer/include/libfiemap_writer/split_fiemap_writer.h b/fs_mgr/libfiemap_writer/include/libfiemap_writer/split_fiemap_writer.h
new file mode 100644
index 0000000..7b977e1
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/include/libfiemap_writer/split_fiemap_writer.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+#include "fiemap_writer.h"
+
+namespace android {
+namespace fiemap_writer {
+
+// Wrapper around FiemapWriter that is able to split images across files if
+// necessary.
+class SplitFiemap final {
+  public:
+    using ProgressCallback = std::function<bool(uint64_t, uint64_t)>;
+
+    // Create a new split fiemap file. If |max_piece_size| is 0, the number of
+    // pieces will be determined automatically by detecting the filesystem.
+    // Otherwise, the file will be split evenly (with the remainder in the
+    // final file).
+    static std::unique_ptr<SplitFiemap> Create(const std::string& file_path, uint64_t file_size,
+                                               uint64_t max_piece_size,
+                                               ProgressCallback progress = {});
+
+    // Open an existing split fiemap file.
+    static std::unique_ptr<SplitFiemap> Open(const std::string& file_path);
+
+    ~SplitFiemap();
+
+    // Return a list of all files created for a split file.
+    static bool GetSplitFileList(const std::string& file_path, std::vector<std::string>* list);
+
+    // Destroy all components of a split file. If the root file does not exist,
+    // this returns true and does not report an error.
+    static bool RemoveSplitFiles(const std::string& file_path, std::string* message = nullptr);
+
+    // Return whether all components of a split file still have pinned extents.
+    bool HasPinnedExtents() const;
+
+    // Helper method for writing data that spans files. Note there is no seek
+    // method (yet); this starts at 0 and increments the position by |bytes|.
+    bool Write(const void* data, uint64_t bytes);
+
+    // Flush all writes to all split files.
+    bool Flush();
+
+    const std::vector<struct fiemap_extent>& extents();
+    uint32_t block_size() const;
+    uint64_t size() const { return total_size_; }
+    const std::string& bdev_path() const;
+
+    // Non-copyable & Non-movable
+    SplitFiemap(const SplitFiemap&) = delete;
+    SplitFiemap& operator=(const SplitFiemap&) = delete;
+    SplitFiemap& operator=(SplitFiemap&&) = delete;
+    SplitFiemap(SplitFiemap&&) = delete;
+
+  private:
+    SplitFiemap() = default;
+    void AddFile(FiemapUniquePtr&& file);
+
+    bool creating_ = false;
+    std::string list_file_;
+    std::vector<FiemapUniquePtr> files_;
+    std::vector<struct fiemap_extent> extents_;
+    uint64_t total_size_ = 0;
+
+    // Most recently open file and position for Write().
+    size_t cursor_index_ = 0;
+    uint64_t cursor_file_pos_ = 0;
+    android::base::unique_fd cursor_fd_;
+};
+
+}  // namespace fiemap_writer
+}  // namespace android
diff --git a/fs_mgr/libfiemap_writer/split_fiemap_writer.cpp b/fs_mgr/libfiemap_writer/split_fiemap_writer.cpp
new file mode 100644
index 0000000..a0ccc10
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/split_fiemap_writer.cpp
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2019 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 <libfiemap_writer/split_fiemap_writer.h>
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+
+#include "utility.h"
+
+namespace android {
+namespace fiemap_writer {
+
+using android::base::unique_fd;
+
+// We use a four-digit suffix at the end of filenames.
+static const size_t kMaxFilePieces = 500;
+
+std::unique_ptr<SplitFiemap> SplitFiemap::Create(const std::string& file_path, uint64_t file_size,
+                                                 uint64_t max_piece_size,
+                                                 ProgressCallback progress) {
+    if (!file_size) {
+        LOG(ERROR) << "Cannot create a fiemap for a 0-length file: " << file_path;
+        return nullptr;
+    }
+
+    if (!max_piece_size) {
+        max_piece_size = DetermineMaximumFileSize(file_path);
+        if (!max_piece_size) {
+            LOG(ERROR) << "Could not determine maximum file size for " << file_path;
+            return nullptr;
+        }
+    }
+
+    // Call |progress| only when the total percentage would significantly change.
+    int permille = -1;
+    uint64_t total_bytes_written = 0;
+    auto on_progress = [&](uint64_t written, uint64_t) -> bool {
+        uint64_t actual_written = total_bytes_written + written;
+        int new_permille = (actual_written * 1000) / file_size;
+        if (new_permille != permille && actual_written < file_size) {
+            if (progress && !progress(actual_written, file_size)) {
+                return false;
+            }
+            permille = new_permille;
+        }
+        return true;
+    };
+
+    std::unique_ptr<SplitFiemap> out(new SplitFiemap());
+    out->creating_ = true;
+    out->list_file_ = file_path;
+
+    // Create the split files.
+    uint64_t remaining_bytes = file_size;
+    while (remaining_bytes) {
+        if (out->files_.size() >= kMaxFilePieces) {
+            LOG(ERROR) << "Requested size " << file_size << " created too many split files";
+            return nullptr;
+        }
+        std::string chunk_path =
+                android::base::StringPrintf("%s.%04d", file_path.c_str(), (int)out->files_.size());
+        uint64_t chunk_size = std::min(max_piece_size, remaining_bytes);
+        auto writer = FiemapWriter::Open(chunk_path, chunk_size, true, on_progress);
+        if (!writer) {
+            return nullptr;
+        }
+
+        // To make sure the alignment doesn't create too much inconsistency, we
+        // account the *actual* size, not the requested size.
+        total_bytes_written += writer->size();
+        remaining_bytes -= writer->size();
+
+        out->AddFile(std::move(writer));
+    }
+
+    // Create the split file list.
+    unique_fd fd(open(out->list_file_.c_str(), O_CREAT | O_WRONLY | O_CLOEXEC, 0660));
+    if (fd < 0) {
+        PLOG(ERROR) << "Failed to open " << file_path;
+        return nullptr;
+    }
+
+    for (const auto& writer : out->files_) {
+        std::string line = android::base::Basename(writer->file_path()) + "\n";
+        if (!android::base::WriteFully(fd, line.data(), line.size())) {
+            PLOG(ERROR) << "Write failed " << file_path;
+            return nullptr;
+        }
+    }
+
+    // Unset this bit, so we don't unlink on destruction.
+    out->creating_ = false;
+    return out;
+}
+
+std::unique_ptr<SplitFiemap> SplitFiemap::Open(const std::string& file_path) {
+    std::vector<std::string> files;
+    if (!GetSplitFileList(file_path, &files)) {
+        return nullptr;
+    }
+
+    std::unique_ptr<SplitFiemap> out(new SplitFiemap());
+    out->list_file_ = file_path;
+
+    for (const auto& file : files) {
+        auto writer = FiemapWriter::Open(file, 0, false);
+        if (!writer) {
+            // Error was logged in Open().
+            return nullptr;
+        }
+        out->AddFile(std::move(writer));
+    }
+    return out;
+}
+
+bool SplitFiemap::GetSplitFileList(const std::string& file_path, std::vector<std::string>* list) {
+    // This is not the most efficient thing, but it is simple and recovering
+    // the fiemap/fibmap is much more expensive.
+    std::string contents;
+    if (!android::base::ReadFileToString(file_path, &contents, true)) {
+        PLOG(ERROR) << "Error reading file: " << file_path;
+        return false;
+    }
+
+    std::vector<std::string> names = android::base::Split(contents, "\n");
+    std::string dir = android::base::Dirname(file_path);
+    for (const auto& name : names) {
+        if (!name.empty()) {
+            list->emplace_back(dir + "/" + name);
+        }
+    }
+    return true;
+}
+
+bool SplitFiemap::RemoveSplitFiles(const std::string& file_path, std::string* message) {
+    // Early exit if this does not exist, and do not report an error.
+    if (access(file_path.c_str(), F_OK) && errno == ENOENT) {
+        return true;
+    }
+
+    bool ok = true;
+    std::vector<std::string> files;
+    if (GetSplitFileList(file_path, &files)) {
+        for (const auto& file : files) {
+            ok &= android::base::RemoveFileIfExists(file, message);
+        }
+    }
+    ok &= android::base::RemoveFileIfExists(file_path, message);
+    return ok;
+}
+
+bool SplitFiemap::HasPinnedExtents() const {
+    for (const auto& file : files_) {
+        if (!FiemapWriter::HasPinnedExtents(file->file_path())) {
+            return false;
+        }
+    }
+    return true;
+}
+
+const std::vector<struct fiemap_extent>& SplitFiemap::extents() {
+    if (extents_.empty()) {
+        for (const auto& file : files_) {
+            const auto& extents = file->extents();
+            extents_.insert(extents_.end(), extents.begin(), extents.end());
+        }
+    }
+    return extents_;
+}
+
+bool SplitFiemap::Write(const void* data, uint64_t bytes) {
+    // Open the current file.
+    FiemapWriter* file = files_[cursor_index_].get();
+
+    const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(data);
+    uint64_t bytes_remaining = bytes;
+    while (bytes_remaining) {
+        // How many bytes can we write into the current file?
+        uint64_t file_bytes_left = file->size() - cursor_file_pos_;
+        if (!file_bytes_left) {
+            if (cursor_index_ == files_.size() - 1) {
+                LOG(ERROR) << "write past end of file requested";
+                return false;
+            }
+
+            // No space left in the current file, but we have more files to
+            // use, so prep the next one.
+            cursor_fd_ = {};
+            cursor_file_pos_ = 0;
+            file = files_[++cursor_index_].get();
+            file_bytes_left = file->size();
+        }
+
+        // Open the current file if it's not open.
+        if (cursor_fd_ < 0) {
+            cursor_fd_.reset(open(file->file_path().c_str(), O_CLOEXEC | O_WRONLY));
+            if (cursor_fd_ < 0) {
+                PLOG(ERROR) << "open failed: " << file->file_path();
+                return false;
+            }
+            CHECK(cursor_file_pos_ == 0);
+        }
+
+        if (!FiemapWriter::HasPinnedExtents(file->file_path())) {
+            LOG(ERROR) << "file is no longer pinned: " << file->file_path();
+            return false;
+        }
+
+        uint64_t bytes_to_write = std::min(file_bytes_left, bytes_remaining);
+        if (!android::base::WriteFully(cursor_fd_, data_ptr, bytes_to_write)) {
+            PLOG(ERROR) << "write failed: " << file->file_path();
+            return false;
+        }
+        data_ptr += bytes_to_write;
+        bytes_remaining -= bytes_to_write;
+        cursor_file_pos_ += bytes_to_write;
+    }
+
+    // If we've reached the end of the current file, close it for sanity.
+    if (cursor_file_pos_ == file->size()) {
+        cursor_fd_ = {};
+    }
+    return true;
+}
+
+bool SplitFiemap::Flush() {
+    for (const auto& file : files_) {
+        unique_fd fd(open(file->file_path().c_str(), O_RDONLY | O_CLOEXEC));
+        if (fd < 0) {
+            PLOG(ERROR) << "open failed: " << file->file_path();
+            return false;
+        }
+        if (fsync(fd)) {
+            PLOG(ERROR) << "fsync failed: " << file->file_path();
+            return false;
+        }
+    }
+    return true;
+}
+
+SplitFiemap::~SplitFiemap() {
+    if (!creating_) {
+        return;
+    }
+
+    // We failed to finish creating, so unlink everything.
+    unlink(list_file_.c_str());
+    for (auto&& file : files_) {
+        std::string path = file->file_path();
+        file = nullptr;
+
+        unlink(path.c_str());
+    }
+}
+
+void SplitFiemap::AddFile(FiemapUniquePtr&& file) {
+    total_size_ += file->size();
+    files_.emplace_back(std::move(file));
+}
+
+uint32_t SplitFiemap::block_size() const {
+    return files_[0]->block_size();
+}
+
+const std::string& SplitFiemap::bdev_path() const {
+    return files_[0]->bdev_path();
+}
+
+}  // namespace fiemap_writer
+}  // namespace android
diff --git a/fs_mgr/libfiemap_writer/utility.cpp b/fs_mgr/libfiemap_writer/utility.cpp
new file mode 100644
index 0000000..192ec16
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/utility.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 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 "utility.h"
+
+#include <stdint.h>
+#include <sys/vfs.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <libfiemap_writer/fiemap_writer.h>
+
+namespace android {
+namespace fiemap_writer {
+
+uint64_t DetermineMaximumFileSize(const std::string& file_path) {
+    // Create the smallest file possible (one block).
+    auto writer = FiemapWriter::Open(file_path, 1);
+    if (!writer) {
+        return 0;
+    }
+
+    uint64_t result = 0;
+    switch (writer->fs_type()) {
+        case EXT4_SUPER_MAGIC:
+            // The minimum is 16GiB, so just report that. If we wanted we could parse the
+            // superblock and figure out if 64-bit support is enabled.
+            result = 17179869184ULL;
+            break;
+        case F2FS_SUPER_MAGIC:
+            // Formula is from https://www.kernel.org/doc/Documentation/filesystems/f2fs.txt
+            // 4KB * (923 + 2 * 1018 + 2 * 1018 * 1018 + 1018 * 1018 * 1018) := 3.94TB.
+            result = 4329690886144ULL;
+            break;
+        case MSDOS_SUPER_MAGIC:
+            // 4GB-1, which we want aligned to the block size.
+            result = 4294967295;
+            result -= (result % writer->block_size());
+            break;
+        default:
+            LOG(ERROR) << "Unknown file system type: " << writer->fs_type();
+            break;
+    }
+
+    // Close and delete the temporary file.
+    writer = nullptr;
+    unlink(file_path.c_str());
+
+    return result;
+}
+
+}  // namespace fiemap_writer
+}  // namespace android
diff --git a/fs_mgr/libfiemap_writer/utility.h b/fs_mgr/libfiemap_writer/utility.h
new file mode 100644
index 0000000..2d418da
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/utility.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+namespace android {
+namespace fiemap_writer {
+
+// Given a file that will be created, determine the maximum size its containing
+// filesystem allows. Note this is a theoretical maximum size; free space is
+// ignored entirely.
+uint64_t DetermineMaximumFileSize(const std::string& file_path);
+
+}  // namespace fiemap_writer
+}  // namespace android
diff --git a/fs_mgr/libfs_avb/avb_util.cpp b/fs_mgr/libfs_avb/avb_util.cpp
index fa9080e..f4e4d4e 100644
--- a/fs_mgr/libfs_avb/avb_util.cpp
+++ b/fs_mgr/libfs_avb/avb_util.cpp
@@ -28,12 +28,26 @@
 #include "util.h"
 
 using android::base::Basename;
+using android::base::ReadFileToString;
 using android::base::StartsWith;
 using android::base::unique_fd;
 
 namespace android {
 namespace fs_mgr {
 
+std::string GetAvbPropertyDescriptor(const std::string& key,
+                                     const std::vector<VBMetaData>& vbmeta_images) {
+    size_t value_size;
+    for (const auto& vbmeta : vbmeta_images) {
+        const char* value = avb_property_lookup(vbmeta.data(), vbmeta.size(), key.data(),
+                                                key.size(), &value_size);
+        if (value != nullptr) {
+            return {value, value_size};
+        }
+    }
+    return "";
+}
+
 // Constructs dm-verity arguments for sending DM_TABLE_LOAD ioctl to kernel.
 // See the following link for more details:
 // https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity
@@ -298,7 +312,8 @@
     return footer;
 }
 
-bool VerifyPublicKeyBlob(const uint8_t* key, size_t length, const std::string& expected_key_blob) {
+bool ValidatePublicKeyBlob(const uint8_t* key, size_t length,
+                           const std::string& expected_key_blob) {
     if (expected_key_blob.empty()) {  // no expectation of the key, return true.
         return true;
     }
@@ -311,6 +326,21 @@
     return false;
 }
 
+bool ValidatePublicKeyBlob(const std::string& key_blob_to_validate,
+                           const std::vector<std::string>& allowed_key_paths) {
+    std::string allowed_key_blob;
+    if (key_blob_to_validate.empty()) {
+        LWARNING << "Failed to validate an empty key";
+        return false;
+    }
+    for (const auto& path : allowed_key_paths) {
+        if (ReadFileToString(path, &allowed_key_blob)) {
+            if (key_blob_to_validate == allowed_key_blob) return true;
+        }
+    }
+    return false;
+}
+
 VBMetaVerifyResult VerifyVBMetaSignature(const VBMetaData& vbmeta,
                                          const std::string& expected_public_key_blob,
                                          std::string* out_public_key_data) {
@@ -334,7 +364,7 @@
                        << ": Error verifying vbmeta image: failed to get public key";
                 return VBMetaVerifyResult::kError;
             }
-            if (!VerifyPublicKeyBlob(pk_data, pk_len, expected_public_key_blob)) {
+            if (!ValidatePublicKeyBlob(pk_data, pk_len, expected_public_key_blob)) {
                 LERROR << vbmeta.partition() << ": Error verifying vbmeta image: public key used to"
                        << " sign data does not match key in chain descriptor";
                 return VBMetaVerifyResult::kErrorVerification;
diff --git a/fs_mgr/libfs_avb/avb_util.h b/fs_mgr/libfs_avb/avb_util.h
index 9babd88..09c786a 100644
--- a/fs_mgr/libfs_avb/avb_util.h
+++ b/fs_mgr/libfs_avb/avb_util.h
@@ -37,6 +37,9 @@
         : partition_name(chain_partition_name), public_key_blob(chain_public_key_blob) {}
 };
 
+std::string GetAvbPropertyDescriptor(const std::string& key,
+                                     const std::vector<VBMetaData>& vbmeta_images);
+
 // AvbHashtreeDescriptor to dm-verity table setup.
 std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
         const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images);
@@ -75,7 +78,10 @@
                                          const std::string& expected_public_key_blob,
                                          std::string* out_public_key_data);
 
-bool VerifyPublicKeyBlob(const uint8_t* key, size_t length, const std::string& expected_key_blob);
+bool ValidatePublicKeyBlob(const uint8_t* key, size_t length, const std::string& expected_key_blob);
+
+bool ValidatePublicKeyBlob(const std::string& key_blob_to_validate,
+                           const std::vector<std::string>& expected_key_paths);
 
 // Detects if whether a partition contains a rollback image.
 bool RollbackDetected(const std::string& partition_name, uint64_t rollback_index);
diff --git a/fs_mgr/libfs_avb/fs_avb.cpp b/fs_mgr/libfs_avb/fs_avb.cpp
index 938e149..f0767dc 100644
--- a/fs_mgr/libfs_avb/fs_avb.cpp
+++ b/fs_mgr/libfs_avb/fs_avb.cpp
@@ -29,6 +29,7 @@
 #include <android-base/file.h>
 #include <android-base/parseint.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <libavb/libavb.h>
 #include <libdm/dm.h>
 
@@ -40,6 +41,7 @@
 using android::base::Basename;
 using android::base::ParseUint;
 using android::base::ReadFileToString;
+using android::base::Split;
 using android::base::StringPrintf;
 
 namespace android {
@@ -263,6 +265,68 @@
     return avb_handle;
 }
 
+AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(const FstabEntry& fstab_entry) {
+    if (fstab_entry.avb_keys.empty()) {
+        LERROR << "avb_keys=/path/to/key(s) is missing for " << fstab_entry.mount_point;
+        return nullptr;
+    }
+
+    // Binds allow_verification_error and rollback_protection to device unlock state.
+    bool allow_verification_error = IsDeviceUnlocked();
+    bool rollback_protection = !allow_verification_error;
+
+    std::string public_key_data;
+    bool verification_disabled = false;
+    VBMetaVerifyResult verify_result = VBMetaVerifyResult::kError;
+    std::unique_ptr<VBMetaData> vbmeta = LoadAndVerifyVbmetaByPath(
+            fstab_entry.blk_device, "" /* partition_name, no need for a standalone path */,
+            "" /* expected_public_key_blob, */, allow_verification_error, rollback_protection,
+            false /* not is_chained_vbmeta */, &public_key_data, &verification_disabled,
+            &verify_result);
+
+    if (!vbmeta) {
+        LERROR << "Failed to load vbmeta: " << fstab_entry.blk_device;
+        return nullptr;
+    }
+
+    AvbUniquePtr avb_handle(new AvbHandle());
+    if (!avb_handle) {
+        LERROR << "Failed to allocate AvbHandle";
+        return nullptr;
+    }
+    avb_handle->vbmeta_images_.emplace_back(std::move(*vbmeta));
+
+    switch (verify_result) {
+        case VBMetaVerifyResult::kSuccess:
+            avb_handle->status_ = AvbHandleStatus::kSuccess;
+            break;
+        case VBMetaVerifyResult::kErrorVerification:
+            avb_handle->status_ = AvbHandleStatus::kVerificationError;
+            break;
+        default:
+            LERROR << "LoadAndVerifyVbmetaByPath failed, result: " << verify_result;
+            return nullptr;
+    }
+
+    if (!ValidatePublicKeyBlob(public_key_data, Split(fstab_entry.avb_keys, ":"))) {
+        avb_handle->status_ = AvbHandleStatus::kVerificationError;
+        LWARNING << "Found unknown public key used to sign " << fstab_entry.mount_point;
+        if (!allow_verification_error) {
+            LERROR << "Unknown public key is not allowed";
+            return nullptr;
+        }
+    }
+
+    if (verification_disabled) {
+        LINFO << "AVB verification disabled on: " << fstab_entry.mount_point;
+        avb_handle->status_ = AvbHandleStatus::kVerificationDisabled;
+    }
+
+    LINFO << "Returning avb_handle for '" << fstab_entry.mount_point
+          << "' with status: " << avb_handle->status_;
+    return avb_handle;
+}
+
 AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta() {
     // Loads inline vbmeta images, starting from /vbmeta.
     return LoadAndVerifyVbmeta("vbmeta", fs_mgr_get_slot_suffix(), fs_mgr_get_other_slot_suffix(),
@@ -358,52 +422,12 @@
 
 AvbHashtreeResult AvbHandle::SetUpStandaloneAvbHashtree(FstabEntry* fstab_entry,
                                                         bool wait_for_verity_dev) {
-    if (fstab_entry->avb_key.empty()) {
-        LERROR << "avb_key=/path/to/key is missing for " << fstab_entry->mount_point;
+    auto avb_handle = LoadAndVerifyVbmeta(*fstab_entry);
+    if (!avb_handle) {
         return AvbHashtreeResult::kFail;
     }
 
-    // Binds allow_verification_error and rollback_protection to device unlock state.
-    bool allow_verification_error = IsDeviceUnlocked();
-    bool rollback_protection = !allow_verification_error;
-
-    std::string expected_key_blob;
-    if (!ReadFileToString(fstab_entry->avb_key, &expected_key_blob)) {
-        if (!allow_verification_error) {
-            LERROR << "Failed to load avb_key: " << fstab_entry->avb_key
-                   << " for mount point: " << fstab_entry->mount_point;
-            return AvbHashtreeResult::kFail;
-        }
-        LWARNING << "Allowing no expected key blob when verification error is permitted";
-        expected_key_blob.clear();
-    }
-
-    bool verification_disabled = false;
-    std::unique_ptr<VBMetaData> vbmeta = LoadAndVerifyVbmetaByPath(
-            fstab_entry->blk_device, "" /* partition_name, no need for a standalone path */,
-            expected_key_blob, allow_verification_error, rollback_protection,
-            false /* not is_chained_vbmeta */, nullptr /* out_public_key_data */,
-            &verification_disabled, nullptr /* out_verify_result */);
-
-    if (!vbmeta) {
-        LERROR << "Failed to load vbmeta: " << fstab_entry->blk_device;
-        return AvbHashtreeResult::kFail;
-    }
-
-    if (verification_disabled) {
-        LINFO << "AVB verification disabled on: " << fstab_entry->mount_point;
-        return AvbHashtreeResult::kDisabled;
-    }
-
-    // Puts the vbmeta into a vector, for LoadAvbHashtreeToEnableVerity() to use.
-    std::vector<VBMetaData> vbmeta_images;
-    vbmeta_images.emplace_back(std::move(*vbmeta));
-    if (!LoadAvbHashtreeToEnableVerity(fstab_entry, wait_for_verity_dev, vbmeta_images,
-                                       fs_mgr_get_slot_suffix(), fs_mgr_get_other_slot_suffix())) {
-        return AvbHashtreeResult::kFail;
-    }
-
-    return AvbHashtreeResult::kSuccess;
+    return avb_handle->SetUpAvbHashtree(fstab_entry, wait_for_verity_dev);
 }
 
 AvbHashtreeResult AvbHandle::SetUpAvbHashtree(FstabEntry* fstab_entry, bool wait_for_verity_dev) {
@@ -425,5 +449,19 @@
     return AvbHashtreeResult::kSuccess;
 }
 
+std::string AvbHandle::GetSecurityPatchLevel(const FstabEntry& fstab_entry) const {
+    if (vbmeta_images_.size() < 1) {
+        return "";
+    }
+    std::string avb_partition_name = DeriveAvbPartitionName(fstab_entry, fs_mgr_get_slot_suffix(),
+                                                            fs_mgr_get_other_slot_suffix());
+    auto avb_prop_name = "com.android.build." + avb_partition_name + ".security_patch";
+    return GetAvbPropertyDescriptor(avb_prop_name, vbmeta_images_);
+}
+
+bool AvbHandle::IsDeviceUnlocked() {
+    return android::fs_mgr::IsDeviceUnlocked();
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
index d026722..7127fa6 100644
--- a/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
+++ b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
@@ -85,6 +85,8 @@
     // TODO(bowgotsai): remove Open() and switch to LoadAndVerifyVbmeta().
     static AvbUniquePtr Open();                 // loads inline vbmeta, via libavb.
     static AvbUniquePtr LoadAndVerifyVbmeta();  // loads inline vbmeta.
+    static AvbUniquePtr LoadAndVerifyVbmeta(
+            const FstabEntry& fstab_entry);     // loads offline vbmeta.
     static AvbUniquePtr LoadAndVerifyVbmeta(    // loads offline vbmeta.
             const std::string& partition_name, const std::string& ab_suffix,
             const std::string& ab_other_suffix, const std::string& expected_public_key,
@@ -108,6 +110,10 @@
     static AvbHashtreeResult SetUpStandaloneAvbHashtree(FstabEntry* fstab_entry,
                                                         bool wait_for_verity_dev = true);
 
+    static bool IsDeviceUnlocked();
+
+    std::string GetSecurityPatchLevel(const FstabEntry& fstab_entry) const;
+
     const std::string& avb_version() const { return avb_version_; }
     const VBMetaInfo& vbmeta_info() const { return vbmeta_info_; }
     AvbHandleStatus status() const { return status_; }
diff --git a/fs_mgr/libfs_avb/tests/avb_util_test.cpp b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
index 835410f..0d342d3 100644
--- a/fs_mgr/libfs_avb/tests/avb_util_test.cpp
+++ b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
@@ -30,13 +30,14 @@
 using android::fs_mgr::DeriveAvbPartitionName;
 using android::fs_mgr::FstabEntry;
 using android::fs_mgr::GetAvbFooter;
+using android::fs_mgr::GetAvbPropertyDescriptor;
 using android::fs_mgr::GetChainPartitionInfo;
 using android::fs_mgr::GetTotalSize;
 using android::fs_mgr::LoadAndVerifyVbmetaByPartition;
 using android::fs_mgr::LoadAndVerifyVbmetaByPath;
+using android::fs_mgr::ValidatePublicKeyBlob;
 using android::fs_mgr::VBMetaData;
 using android::fs_mgr::VBMetaVerifyResult;
-using android::fs_mgr::VerifyPublicKeyBlob;
 using android::fs_mgr::VerifyVBMetaData;
 using android::fs_mgr::VerifyVBMetaSignature;
 
@@ -268,6 +269,67 @@
     EXPECT_EQ(nullptr, footer);
 }
 
+TEST_F(AvbUtilTest, GetAvbPropertyDescriptor_Basic) {
+    // Makes a vbmeta.img with some properties.
+    GenerateVBMetaImage("vbmeta.img", "SHA256_RSA4096", 0, data_dir_.Append("testkey_rsa4096.pem"),
+                        {}, /* include_descriptor_image_paths */
+                        {}, /* chain_partitions */
+                        "--prop foo:android "
+                        "--prop bar:treble "
+                        "--internal_release_string \"unit test\" ");
+    auto vbmeta = LoadVBMetaData("vbmeta.img");
+
+    // Puts the vbmeta into a vector, for GetAvbPropertyDescriptor to use.
+    std::vector<VBMetaData> vbmeta_images;
+    vbmeta_images.emplace_back(std::move(vbmeta));
+
+    EXPECT_EQ("android", GetAvbPropertyDescriptor("foo", vbmeta_images));
+    EXPECT_EQ("treble", GetAvbPropertyDescriptor("bar", vbmeta_images));
+    EXPECT_EQ("", GetAvbPropertyDescriptor("non-existent", vbmeta_images));
+}
+
+TEST_F(AvbUtilTest, GetAvbPropertyDescriptor_SecurityPatchLevel) {
+    // Generates a raw boot.img
+    const size_t boot_image_size = 5 * 1024 * 1024;
+    const size_t boot_partition_size = 10 * 1024 * 1024;
+    base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+    // Adds AVB Hash Footer.
+    AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
+                 data_dir_.Append("testkey_rsa2048.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates a raw system.img, use a smaller size to speed-up unit test.
+    const size_t system_image_size = 10 * 1024 * 1024;
+    const size_t system_partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", system_image_size);
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+                 data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--prop com.android.build.system.security_patch:2019-04-05 "
+                 "--internal_release_string \"unit test\"");
+
+    // Generates chain partition descriptors.
+    base::FilePath rsa4096_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+
+    // Makes a vbmeta.img including the 'system' chained descriptor.
+    GenerateVBMetaImage("vbmeta.img", "SHA256_RSA4096", 0, data_dir_.Append("testkey_rsa4096.pem"),
+                        {boot_path},                         /* include_descriptor_image_paths */
+                        {{"system", 3, rsa4096_public_key}}, /* chain_partitions */
+                        "--internal_release_string \"unit test\"");
+
+    auto vbmeta = LoadVBMetaData("vbmeta.img");
+    auto system_vbmeta = ExtractAndLoadVBMetaData(system_path, "system-vbmeta.img");
+
+    // Puts the vbmeta into a vector, for GetAvbPropertyDescriptor to use.
+    std::vector<VBMetaData> vbmeta_images;
+    vbmeta_images.emplace_back(std::move(vbmeta));
+    vbmeta_images.emplace_back(std::move(system_vbmeta));
+
+    EXPECT_EQ("2019-04-05",
+              GetAvbPropertyDescriptor("com.android.build.system.security_patch", vbmeta_images));
+}
+
 TEST_F(AvbUtilTest, GetVBMetaHeader) {
     // Generates a raw boot.img
     const size_t image_size = 5 * 1024 * 1024;
@@ -353,7 +415,7 @@
     EXPECT_EQ(content_padding.size() - padding.size(), vbmeta_padding.size());
 }
 
-TEST_F(AvbUtilTest, VerifyPublicKeyBlob) {
+TEST_F(AvbUtilTest, ValidatePublicKeyBlob) {
     // Generates a raw key.bin
     const size_t key_size = 2048;
     base::FilePath key_path = GenerateImage("key.bin", key_size);
@@ -363,12 +425,12 @@
 
     std::string expected_key_blob;
     EXPECT_TRUE(base::ReadFileToString(key_path, &expected_key_blob));
-    EXPECT_TRUE(VerifyPublicKeyBlob(key_data, key_size, expected_key_blob));
+    EXPECT_TRUE(ValidatePublicKeyBlob(key_data, key_size, expected_key_blob));
 
     key_data[10] ^= 0x80;  // toggles a bit and expects a failure
-    EXPECT_FALSE(VerifyPublicKeyBlob(key_data, key_size, expected_key_blob));
+    EXPECT_FALSE(ValidatePublicKeyBlob(key_data, key_size, expected_key_blob));
     key_data[10] ^= 0x80;  // toggles the bit again, should pass
-    EXPECT_TRUE(VerifyPublicKeyBlob(key_data, key_size, expected_key_blob));
+    EXPECT_TRUE(ValidatePublicKeyBlob(key_data, key_size, expected_key_blob));
 }
 
 TEST_F(AvbUtilTest, VerifyEmptyPublicKeyBlob) {
@@ -380,7 +442,37 @@
     EXPECT_EQ(key_size, base::ReadFile(key_path, (char*)key_data, key_size));
 
     std::string expected_key_blob = "";  // empty means no expectation, thus return true.
-    EXPECT_TRUE(VerifyPublicKeyBlob(key_data, key_size, expected_key_blob));
+    EXPECT_TRUE(ValidatePublicKeyBlob(key_data, key_size, expected_key_blob));
+}
+
+TEST_F(AvbUtilTest, ValidatePublicKeyBlob_MultipleAllowedKeys) {
+    base::FilePath rsa2048_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+    base::FilePath rsa4096_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+    base::FilePath rsa8192_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa8192.pem"));
+
+    std::vector<std::string> allowed_key_paths;
+    allowed_key_paths.push_back(rsa2048_public_key.value());
+    allowed_key_paths.push_back(rsa4096_public_key.value());
+
+    std::string expected_key_blob_2048;
+    EXPECT_TRUE(base::ReadFileToString(rsa2048_public_key, &expected_key_blob_2048));
+    std::string expected_key_blob_4096;
+    EXPECT_TRUE(base::ReadFileToString(rsa4096_public_key, &expected_key_blob_4096));
+    std::string expected_key_blob_8192;
+    EXPECT_TRUE(base::ReadFileToString(rsa8192_public_key, &expected_key_blob_8192));
+
+    EXPECT_TRUE(ValidatePublicKeyBlob(expected_key_blob_2048, allowed_key_paths));
+    EXPECT_TRUE(ValidatePublicKeyBlob(expected_key_blob_4096, allowed_key_paths));
+
+    EXPECT_FALSE(ValidatePublicKeyBlob(expected_key_blob_8192, allowed_key_paths));
+    EXPECT_FALSE(ValidatePublicKeyBlob("invalid_content", allowed_key_paths));
+    EXPECT_FALSE(ValidatePublicKeyBlob("", allowed_key_paths));
+
+    allowed_key_paths.push_back(rsa8192_public_key.value());
+    EXPECT_TRUE(ValidatePublicKeyBlob(expected_key_blob_8192, allowed_key_paths));
 }
 
 TEST_F(AvbUtilTest, VerifyVBMetaSignature) {
diff --git a/fs_mgr/libfs_avb/tests/fs_avb_device_test.cpp b/fs_mgr/libfs_avb/tests/fs_avb_device_test.cpp
index fc4eb5f..c8605d7 100644
--- a/fs_mgr/libfs_avb/tests/fs_avb_device_test.cpp
+++ b/fs_mgr/libfs_avb/tests/fs_avb_device_test.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <android-base/properties.h>
+#include <fs_avb/fs_avb.h>
 #include <fs_avb/fs_avb_util.h>
 #include <fstab/fstab.h>
 #include <gtest/gtest.h>
@@ -22,6 +23,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+using android::fs_mgr::AvbHandle;
+using android::fs_mgr::AvbHandleStatus;
 using android::fs_mgr::Fstab;
 using android::fs_mgr::FstabEntry;
 using android::fs_mgr::VBMetaData;
@@ -31,7 +34,7 @@
 
 // system vbmeta might not be at the end of /system when dynamic partition is
 // enabled. Therefore, disable it by default.
-TEST(PublicFsAvbDeviceTest, DISABLED_LoadAndVerifyVbmeta_SystemVbmeta) {
+TEST(FsAvbUtilTest, DISABLED_LoadAndVerifyVbmeta_SystemVbmeta) {
     Fstab fstab;
     EXPECT_TRUE(ReadDefaultFstab(&fstab));
 
@@ -51,7 +54,7 @@
     EXPECT_NE("", out_public_key_data);
 }
 
-TEST(PublicFsAvbDeviceTest, GetHashtreeDescriptor_SystemOther) {
+TEST(FsAvbUtilTest, GetHashtreeDescriptor_SystemOther) {
     // Non-A/B device doesn't have system_other partition.
     if (fs_mgr_get_slot_suffix() == "") return;
 
@@ -90,4 +93,55 @@
     EXPECT_NE(nullptr, hashtree_desc);
 }
 
+TEST(AvbHandleTest, LoadAndVerifyVbmeta_SystemOther) {
+    // Non-A/B device doesn't have system_other partition.
+    if (fs_mgr_get_slot_suffix() == "") return;
+
+    // Skip running this test if system_other is a logical partition.
+    // Note that system_other is still a physical partition on "retrofit" devices.
+    if (android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) &&
+        !android::base::GetBoolProperty("ro.boot.dynamic_partitions_retrofit", false)) {
+        return;
+    }
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile("/system/etc/fstab.postinstall", &fstab));
+
+    // It should have two lines in the fstab, the first for logical system_other,
+    // the other for physical system_other.
+    EXPECT_EQ(2UL, fstab.size());
+
+    // Use the 2nd fstab entry, which is for physical system_other partition.
+    FstabEntry* system_other_entry = &fstab[1];
+    // Assign the default key if it's not specified in the fstab.
+    if (system_other_entry->avb_keys.empty()) {
+        system_other_entry->avb_keys = "/system/etc/security/avb/system_other.avbpubkey";
+    }
+    auto avb_handle = AvbHandle::LoadAndVerifyVbmeta(*system_other_entry);
+    EXPECT_NE(nullptr, avb_handle) << "Failed to load system_other vbmeta. Try 'adb root'?";
+    EXPECT_EQ(AvbHandleStatus::kSuccess, avb_handle->status());
+}
+
+TEST(AvbHandleTest, GetSecurityPatchLevel) {
+    Fstab fstab;
+    EXPECT_TRUE(ReadDefaultFstab(&fstab));
+
+    auto avb_handle = AvbHandle::LoadAndVerifyVbmeta();
+    EXPECT_NE(nullptr, avb_handle) << "Failed to load inline vbmeta. Try 'adb root'?";
+    EXPECT_EQ(AvbHandleStatus::kSuccess, avb_handle->status());
+
+    // Gets security patch level with format: YYYY-MM-DD (e.g., 2019-04-05).
+    FstabEntry* system_entry = GetEntryForMountPoint(&fstab, "/system");
+    EXPECT_NE(nullptr, system_entry);
+    EXPECT_EQ(10UL, avb_handle->GetSecurityPatchLevel(*system_entry).length());
+
+    FstabEntry* vendor_entry = GetEntryForMountPoint(&fstab, "/vendor");
+    EXPECT_NE(nullptr, vendor_entry);
+    EXPECT_EQ(10UL, avb_handle->GetSecurityPatchLevel(*vendor_entry).length());
+
+    FstabEntry* product_entry = GetEntryForMountPoint(&fstab, "/product");
+    EXPECT_NE(nullptr, product_entry);
+    EXPECT_EQ(10UL, avb_handle->GetSecurityPatchLevel(*product_entry).length());
+}
+
 }  // namespace fs_avb_device_test
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index c39fbe7..27222af 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -550,11 +550,11 @@
 }
 
 bool MetadataBuilder::ValidatePartitionSizeChange(Partition* partition, uint64_t old_size,
-                                                  uint64_t new_size) {
+                                                  uint64_t new_size, bool force_check) {
     PartitionGroup* group = FindGroup(partition->group_name());
     CHECK(group);
 
-    if (new_size <= old_size) {
+    if (!force_check && new_size <= old_size) {
         return true;
     }
 
@@ -591,11 +591,25 @@
         free_regions = PrioritizeSecondHalfOfSuper(free_regions);
     }
 
-    // Find gaps that we can use for new extents. Note we store new extents in a
-    // temporary vector, and only commit them if we are guaranteed enough free
-    // space.
+    // Note we store new extents in a temporary vector, and only commit them
+    // if we are guaranteed enough free space.
     std::vector<std::unique_ptr<LinearExtent>> new_extents;
+
+    // If the last extent in the partition has a size < alignment, then the
+    // difference is unallocatable due to being misaligned. We peek for that
+    // case here to avoid wasting space.
+    if (auto extent = ExtendFinalExtent(partition, free_regions, sectors_needed)) {
+        sectors_needed -= extent->num_sectors();
+        new_extents.emplace_back(std::move(extent));
+    }
+
     for (auto& region : free_regions) {
+        // Note: this comes first, since we may enter the loop not needing any
+        // more sectors.
+        if (!sectors_needed) {
+            break;
+        }
+
         if (region.length() % sectors_per_block != 0) {
             // This should never happen, because it would imply that we
             // once allocated an extent that was not a multiple of the
@@ -613,22 +627,10 @@
         }
 
         uint64_t sectors = std::min(sectors_needed, region.length());
-        if (sectors < region.length()) {
-            const auto& block_device = block_devices_[region.device_index];
-            if (block_device.alignment) {
-                const uint64_t alignment = block_device.alignment / LP_SECTOR_SIZE;
-                sectors = AlignTo(sectors, alignment);
-                sectors = std::min(sectors, region.length());
-            }
-        }
         CHECK(sectors % sectors_per_block == 0);
 
         auto extent = std::make_unique<LinearExtent>(sectors, region.device_index, region.start);
         new_extents.push_back(std::move(extent));
-        if (sectors >= sectors_needed) {
-            sectors_needed = 0;
-            break;
-        }
         sectors_needed -= sectors;
     }
     if (sectors_needed) {
@@ -677,6 +679,64 @@
     return second_half;
 }
 
+std::unique_ptr<LinearExtent> MetadataBuilder::ExtendFinalExtent(
+        Partition* partition, const std::vector<Interval>& free_list,
+        uint64_t sectors_needed) const {
+    if (partition->extents().empty()) {
+        return nullptr;
+    }
+    LinearExtent* extent = partition->extents().back()->AsLinearExtent();
+    if (!extent) {
+        return nullptr;
+    }
+
+    // If the sector ends where the next aligned chunk begins, then there's
+    // no missing gap to try and allocate.
+    const auto& block_device = block_devices_[extent->device_index()];
+    uint64_t next_aligned_sector = AlignSector(block_device, extent->end_sector());
+    if (extent->end_sector() == next_aligned_sector) {
+        return nullptr;
+    }
+
+    uint64_t num_sectors = std::min(next_aligned_sector - extent->end_sector(), sectors_needed);
+    auto new_extent = std::make_unique<LinearExtent>(num_sectors, extent->device_index(),
+                                                     extent->end_sector());
+    if (IsAnyRegionAllocated(*new_extent.get()) ||
+        IsAnyRegionCovered(free_list, *new_extent.get())) {
+        LERROR << "Misaligned region " << new_extent->physical_sector() << ".."
+               << new_extent->end_sector() << " was allocated or marked allocatable.";
+        return nullptr;
+    }
+    return new_extent;
+}
+
+bool MetadataBuilder::IsAnyRegionCovered(const std::vector<Interval>& regions,
+                                         const LinearExtent& candidate) const {
+    for (const auto& region : regions) {
+        if (region.device_index == candidate.device_index() &&
+            (candidate.OwnsSector(region.start) || candidate.OwnsSector(region.end))) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool MetadataBuilder::IsAnyRegionAllocated(const LinearExtent& candidate) const {
+    for (const auto& partition : partitions_) {
+        for (const auto& extent : partition->extents()) {
+            LinearExtent* linear = extent->AsLinearExtent();
+            if (!linear || linear->device_index() != candidate.device_index()) {
+                continue;
+            }
+            if (linear->OwnsSector(candidate.physical_sector()) ||
+                linear->OwnsSector(candidate.end_sector() - 1)) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
 void MetadataBuilder::ShrinkPartition(Partition* partition, uint64_t aligned_size) {
     partition->ShrinkTo(aligned_size);
 }
@@ -861,7 +921,7 @@
     uint64_t aligned_size = AlignTo(requested_size, geometry_.logical_block_size);
     uint64_t old_size = partition->size();
 
-    if (!ValidatePartitionSizeChange(partition, old_size, aligned_size)) {
+    if (!ValidatePartitionSizeChange(partition, old_size, aligned_size, false)) {
         return false;
     }
 
@@ -973,7 +1033,12 @@
 
     ImportExtents(partition, metadata, source);
 
-    if (!ValidatePartitionSizeChange(partition, 0, partition->size())) {
+    // Note: we've already increased the partition size by calling
+    // ImportExtents(). In order to figure out the size before that,
+    // we would have to iterate the extents and add up the linear
+    // segments. Instead, we just force ValidatePartitionSizeChange
+    // to check if the current configuration is acceptable.
+    if (!ValidatePartitionSizeChange(partition, partition->size(), partition->size(), true)) {
         partition->RemoveExtents();
         return false;
     }
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index 69724f8..46bfe92 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -209,8 +209,8 @@
         ASSERT_TRUE(builder->ResizePartition(a, a->size() + 4096));
         ASSERT_TRUE(builder->ResizePartition(b, b->size() + 4096));
     }
-    EXPECT_EQ(a->size(), 7864320);
-    EXPECT_EQ(b->size(), 7864320);
+    EXPECT_EQ(a->size(), 40960);
+    EXPECT_EQ(b->size(), 40960);
 
     unique_ptr<LpMetadata> exported = builder->Export();
     ASSERT_NE(exported, nullptr);
@@ -218,7 +218,7 @@
     // Check that each starting sector is aligned.
     for (const auto& extent : exported->extents) {
         ASSERT_EQ(extent.target_type, LP_TARGET_TYPE_LINEAR);
-        EXPECT_EQ(extent.num_sectors, 1536);
+        EXPECT_EQ(extent.num_sectors, 80);
 
         uint64_t lba = extent.target_data * LP_SECTOR_SIZE;
         uint64_t aligned_lba = AlignTo(lba, device_info.alignment, device_info.alignment_offset);
@@ -226,7 +226,7 @@
     }
 
     // Sanity check one extent.
-    EXPECT_EQ(exported->extents.back().target_data, 30656);
+    EXPECT_EQ(exported->extents.back().target_data, 3008);
 }
 
 TEST_F(BuilderTest, UseAllDiskSpace) {
@@ -698,7 +698,7 @@
     EXPECT_EQ(metadata->extents[1].target_type, LP_TARGET_TYPE_LINEAR);
     EXPECT_EQ(metadata->extents[1].target_data, 1472);
     EXPECT_EQ(metadata->extents[1].target_source, 1);
-    EXPECT_EQ(metadata->extents[2].num_sectors, 129600);
+    EXPECT_EQ(metadata->extents[2].num_sectors, 129088);
     EXPECT_EQ(metadata->extents[2].target_type, LP_TARGET_TYPE_LINEAR);
     EXPECT_EQ(metadata->extents[2].target_data, 1472);
     EXPECT_EQ(metadata->extents[2].target_source, 2);
@@ -805,7 +805,7 @@
     EXPECT_EQ(exported->extents[0].target_data, 10487808);
     EXPECT_EQ(exported->extents[0].num_sectors, 10483712);
     EXPECT_EQ(exported->extents[1].target_data, 6292992);
-    EXPECT_EQ(exported->extents[1].num_sectors, 2099712);
+    EXPECT_EQ(exported->extents[1].num_sectors, 2099200);
     EXPECT_EQ(exported->extents[2].target_data, 1536);
     EXPECT_EQ(exported->extents[2].num_sectors, 6291456);
 }
@@ -821,7 +821,7 @@
     ASSERT_NE(vendor, nullptr);
     ASSERT_TRUE(builder->ResizePartition(system, device_info.alignment + 4096));
     ASSERT_TRUE(builder->ResizePartition(vendor, device_info.alignment));
-    ASSERT_EQ(system->size(), device_info.alignment * 2);
+    ASSERT_EQ(system->size(), device_info.alignment + 4096);
     ASSERT_EQ(vendor->size(), device_info.alignment);
 
     ASSERT_TRUE(builder->ResizePartition(system, device_info.alignment * 2));
@@ -835,3 +835,73 @@
     EXPECT_EQ(exported->extents[1].target_data, 4608);
     EXPECT_EQ(exported->extents[1].num_sectors, 1536);
 }
+
+TEST_F(BuilderTest, UpdateSuper) {
+    // Build the on-disk metadata that we saw before flashing.
+    auto builder = MetadataBuilder::New(8145338368ULL, 65536, 3);
+    ASSERT_NE(builder, nullptr);
+
+    ASSERT_TRUE(builder->AddGroup("google_dynamic_partitions_a", 4068474880ULL));
+    ASSERT_TRUE(builder->AddGroup("google_dynamic_partitions_b", 4068474880ULL));
+
+    Partition* partition = builder->AddPartition("system_a", "google_dynamic_partitions_a",
+                                                 LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(partition, nullptr);
+    ASSERT_TRUE(builder->AddLinearExtent(partition, "super", 1901568, 3608576));
+
+    partition = builder->AddPartition("vendor_a", "google_dynamic_partitions_a",
+                                      LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(partition, nullptr);
+    ASSERT_TRUE(builder->AddLinearExtent(partition, "super", 1521664, 5510144));
+
+    partition = builder->AddPartition("product_a", "google_dynamic_partitions_a",
+                                      LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(partition, nullptr);
+    ASSERT_TRUE(builder->AddLinearExtent(partition, "super", 3606528, 2048));
+
+    partition = builder->AddPartition("system_b", "google_dynamic_partitions_b",
+                                      LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(partition, nullptr);
+    ASSERT_TRUE(builder->AddLinearExtent(partition, "super", 1901568, 7955456));
+
+    partition = builder->AddPartition("vendor_b", "google_dynamic_partitions_b",
+                                      LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(partition, nullptr);
+    ASSERT_TRUE(builder->AddLinearExtent(partition, "super", 1521664, 9857024));
+
+    partition = builder->AddPartition("product_b", "google_dynamic_partitions_b",
+                                      LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(partition, nullptr);
+    ASSERT_TRUE(builder->AddLinearExtent(partition, "super", 3606528, 11378688));
+
+    auto on_disk = builder->Export();
+    ASSERT_NE(on_disk, nullptr);
+
+    // Build the super_empty from the new build.
+    builder = MetadataBuilder::New(8145338368ULL, 65536, 3);
+    ASSERT_NE(builder, nullptr);
+
+    ASSERT_TRUE(builder->AddGroup("google_dynamic_partitions_a", 4068474880ULL));
+    ASSERT_TRUE(builder->AddGroup("google_dynamic_partitions_b", 4068474880ULL));
+    ASSERT_NE(builder->AddPartition("system_a", "google_dynamic_partitions_a",
+                                    LP_PARTITION_ATTR_READONLY),
+              nullptr);
+    ASSERT_NE(builder->AddPartition("system_b", "google_dynamic_partitions_b",
+                                    LP_PARTITION_ATTR_READONLY),
+              nullptr);
+    ASSERT_NE(builder->AddPartition("vendor_a", "google_dynamic_partitions_a",
+                                    LP_PARTITION_ATTR_READONLY),
+              nullptr);
+    ASSERT_NE(builder->AddPartition("vendor_b", "google_dynamic_partitions_b",
+                                    LP_PARTITION_ATTR_READONLY),
+              nullptr);
+    ASSERT_NE(builder->AddPartition("product_a", "google_dynamic_partitions_a",
+                                    LP_PARTITION_ATTR_READONLY),
+              nullptr);
+    ASSERT_NE(builder->AddPartition("product_b", "google_dynamic_partitions_b",
+                                    LP_PARTITION_ATTR_READONLY),
+              nullptr);
+
+    std::set<std::string> partitions_to_keep{"system_a", "vendor_a", "product_a"};
+    ASSERT_TRUE(builder->ImportPartitions(*on_disk.get(), partitions_to_keep));
+}
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index 53f480f..c706f2a 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -66,6 +66,10 @@
     uint64_t end_sector() const { return physical_sector_ + num_sectors_; }
     uint32_t device_index() const { return device_index_; }
 
+    bool OwnsSector(uint64_t sector) const {
+        return sector >= physical_sector_ && sector < end_sector();
+    }
+
   private:
     uint32_t device_index_;
     uint64_t physical_sector_;
@@ -297,7 +301,8 @@
     uint64_t TotalSizeOfGroup(PartitionGroup* group) const;
     bool UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& info);
     bool FindBlockDeviceByName(const std::string& partition_name, uint32_t* index) const;
-    bool ValidatePartitionSizeChange(Partition* partition, uint64_t old_size, uint64_t new_size);
+    bool ValidatePartitionSizeChange(Partition* partition, uint64_t old_size, uint64_t new_size,
+                                     bool force_check);
     void ImportExtents(Partition* dest, const LpMetadata& metadata,
                        const LpMetadataPartition& source);
     bool ImportPartition(const LpMetadata& metadata, const LpMetadataPartition& source);
@@ -321,9 +326,15 @@
         }
     };
     std::vector<Interval> GetFreeRegions() const;
+    bool IsAnyRegionCovered(const std::vector<Interval>& regions,
+                            const LinearExtent& candidate) const;
+    bool IsAnyRegionAllocated(const LinearExtent& candidate) const;
     void ExtentsToFreeList(const std::vector<Interval>& extents,
                            std::vector<Interval>* free_regions) const;
     std::vector<Interval> PrioritizeSecondHalfOfSuper(const std::vector<Interval>& free_list);
+    std::unique_ptr<LinearExtent> ExtendFinalExtent(Partition* partition,
+                                                    const std::vector<Interval>& free_list,
+                                                    uint64_t sectors_needed) const;
 
     static bool sABOverrideValue;
     static bool sABOverrideSet;
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index ea12e96..19737fe 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -14,6 +14,7 @@
 
 cc_test {
     name: "fs_mgr_unit_test",
+    test_suites: ["device-tests"],
 
     shared_libs: [
         "libbase",
@@ -23,9 +24,6 @@
         "libfs_mgr",
         "libfstab",
     ],
-    data: [
-        "data/*",
-    ],
     srcs: [
         "fs_mgr_test.cpp",
     ],
@@ -36,3 +34,32 @@
         "-Werror",
     ],
 }
+
+cc_prebuilt_binary {
+    name: "adb-remount-test.sh",
+    srcs: ["adb-remount-test.sh"],
+    target: {
+        darwin: {
+            enabled: false,
+        },
+        windows: {
+            enabled: false,
+        },
+        android: {
+            enabled: false,
+        },
+    },
+    host_supported: true,
+}
+
+java_test_host {
+    name: "fs_mgr_vendor_overlay_test",
+
+    srcs:  ["src/**/VendorOverlayHostTest.java"],
+
+    libs: ["tradefed"],
+
+    test_config: "vendor-overlay-test.xml",
+
+    test_suites: ["general-tests"],
+}
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index 7fc9518..c09dc3d 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -11,23 +11,24 @@
 ##  USAGE
 ##
 
-USAGE="USAGE: `basename ${0}` [-s <SerialNumber>]
+USAGE="USAGE: `basename ${0}` [--help] [--serial <SerialNumber>] [options]
 
-adb remount tests (overlayfs focus)
+adb remount tests
+
+--help        This help
+--serial      Specify device (must if multiple are present)
+--color       Dress output with highlighting colors
+--print-time  Report the test duration
 
 Conditions:
  - Must be a userdebug build.
  - Must be in adb mode.
- - Kernel must have overlayfs enabled and patched to support override_creds.
- - Must have either squashfs, ext4-dedupe or right-sized partitions.
- - Minimum expectation system and vender are overlayfs covered partitions.
+ - Also tests overlayfs
+  - Kernel must have overlayfs enabled and patched to support override_creds.
+  - Must have either erofs, squashfs, ext4-dedupe or full partitions.
+  - Minimum expectation system and vender are overlayfs covered partitions.
 "
 
-if [ X"${1}" = X"--help" -o X"${1}" = X"-h" -o X"${1}" = X"-?" ]; then
-  echo "${USAGE}" >&2
-  exit 0
-fi
-
 ##
 ##  Helper Variables
 ##
@@ -44,6 +45,10 @@
 ORANGE="${ESCAPE}[38;5;255:165:0m"
 BLUE="${ESCAPE}[35m"
 NORMAL="${ESCAPE}[0m"
+TMPDIR=${TMPDIR:-/tmp}
+print_time=false
+start_time=`date +%s`
+ACTIVE_SLOT=
 
 ##
 ##  Helper Functions
@@ -66,7 +71,7 @@
 Returns: true if device is in adb mode" ]
 inAdb() {
   adb devices |
-    grep -v -e 'List of devices attached' -e '^$' |
+    grep -v -e 'List of devices attached' -e '^$' -e "[${SPACE}${TAB}]recovery\$" |
     if [ -n "${ANDROID_SERIAL}" ]; then
       grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
     else
@@ -74,11 +79,30 @@
     fi
 }
 
+[ "USAGE: inRecovery
+
+Returns: true if device is in recovery mode" ]
+inRecovery() {
+  local list="`adb devices |
+              grep -v -e 'List of devices attached' -e '^$'`"
+  if [ -n "${ANDROID_SERIAL}" ]; then
+    echo "${list}" |
+      grep "^${ANDROID_SERIAL}[${SPACE}${TAB}][${SPACE}${TAB}]*recovery\$" >/dev/null
+    return ${?}
+  fi
+  if echo "${list}" | wc -l | grep '^1$' >/dev/null; then
+    echo "${list}" |
+      grep "[${SPACE}${TAB}]recovery\$" >/dev/null
+    return ${?}
+  fi
+  false
+}
+
 [ "USAGE: adb_sh <commands> </dev/stdin >/dev/stdout 2>/dev/stderr
 
 Returns: true if the command succeeded" ]
 adb_sh() {
-  args=
+  local args=
   for i in "${@}"; do
     [ -z "${args}" ] || args="${args} "
     if [ X"${i}" != X"${i#\'}" ]; then
@@ -139,10 +163,10 @@
 Returns: content of file to stdout with carriage returns skipped,
          true of the file exists" ]
 adb_cat() {
-    OUTPUT="`adb_sh cat ${1} </dev/null 2>&1`"
-    retval=${?}
+    local OUTPUT="`adb_sh cat ${1} </dev/null 2>&1`"
+    local ret=${?}
     echo "${OUTPUT}" | tr -d '\r'
-    return ${retval}
+    return ${ret}
 }
 
 [ "USAGE: adb_reboot
@@ -161,7 +185,7 @@
     echo unknown
     return
   fi
-  duration="${1}"
+  local duration="${1}"
   if [ X"${duration}" != X"${duration%s}" ]; then
     duration=${duration%s}
   elif [ X"${duration}" != X"${duration%m}" ]; then
@@ -171,9 +195,9 @@
   elif [ X"${duration}" != X"${duration%d}" ]; then
     duration=`expr ${duration%d} \* 86400`
   fi
-  seconds=`expr ${duration} % 60`
-  minutes=`expr \( ${duration} / 60 \) % 60`
-  hours=`expr ${duration} / 3600`
+  local seconds=`expr ${duration} % 60`
+  local minutes=`expr \( ${duration} / 60 \) % 60`
+  local hours=`expr ${duration} / 3600`
   if [ 0 -eq ${minutes} -a 0 -eq ${hours} ]; then
     if [ 1 -eq ${duration} ]; then
       echo 1 second
@@ -199,25 +223,36 @@
 
 Returns: waits until the device has returned for adb or optional timeout" ]
 adb_wait() {
+  local ret
   if [ -n "${1}" ]; then
     echo -n ". . . waiting `format_duration ${1}`" ${ANDROID_SERIAL} ${USB_ADDRESS} "${CR}"
-    timeout --preserve-status --signal=KILL ${1} adb wait-for-device
-    retval=${?}
+    timeout --preserve-status --signal=KILL ${1} adb wait-for-device 2>/dev/null
+    ret=${?}
     echo -n "                                                                             ${CR}"
-    return ${retval}
   else
     adb wait-for-device
+    ret=${?}
   fi
+  if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then
+    local active_slot=`get_active_slot`
+    if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
+      echo "${ORANGE}[  WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}" >&2
+    fi
+  fi
+  return ${ret}
 }
 
 [ "USAGE: usb_status > stdout
 
-If adb_wait failed, check if device is in fastboot mode and report status
+If adb_wait failed, check if device is in adb, recovery or fastboot mode
+and report status string.
 
 Returns: \"(USB stack borken?)\", \"(In fastboot mode)\" or \"(in adb mode)\"" ]
 usb_status() {
   if inFastboot; then
     echo "(In fastboot mode)"
+  elif inRecovery; then
+    echo "(In recovery mode)"
   elif inAdb; then
     echo "(In adb mode)"
   else
@@ -229,18 +264,67 @@
 
 Returns: waits until the device has returned for fastboot or optional timeout" ]
 fastboot_wait() {
+  local ret
   # fastboot has no wait-for-device, but it does an automatic
   # wait and requires (even a nonsensical) command to do so.
   if [ -n "${1}" ]; then
     echo -n ". . . waiting `format_duration ${1}`" ${ANDROID_SERIAL} ${USB_ADDRESS} "${CR}"
     timeout --preserve-status --signal=KILL ${1} fastboot wait-for-device >/dev/null 2>/dev/null
-    retval=${?}
+    ret=${?}
     echo -n "                                                                             ${CR}"
-    ( exit ${retval} )
+    ( exit ${ret} )
   else
     fastboot wait-for-device >/dev/null 2>/dev/null
   fi ||
     inFastboot
+  ret=${?}
+  if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then
+    local active_slot=`get_active_slot`
+    if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
+      echo "${ORANGE}[  WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}" >&2
+    fi
+  fi
+  return ${ret}
+}
+
+[ "USAGE: recovery_wait [timeout]
+
+Returns: waits until the device has returned for recovery or optional timeout" ]
+recovery_wait() {
+  local ret
+  if [ -n "${1}" ]; then
+    echo -n ". . . waiting `format_duration ${1}`" ${ANDROID_SERIAL} ${USB_ADDRESS} "${CR}"
+    timeout --preserve-status --signal=KILL ${1} adb wait-for-recovery 2>/dev/null
+    ret=${?}
+    echo -n "                                                                             ${CR}"
+  else
+    adb wait-for-recovery
+    ret=${?}
+  fi
+  if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then
+    local active_slot=`get_active_slot`
+    if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
+      echo "${ORANGE}[  WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}" >&2
+    fi
+  fi
+  return ${ret}
+}
+
+[ "any_wait [timeout]
+
+Returns: waits until a device has returned or optional timeout" ]
+any_wait() {
+  (
+    adb_wait ${1} &
+    adb_pid=${!}
+    fastboot_wait ${1} &
+    fastboot_pid=${!}
+    recovery_wait ${1} &
+    recovery_pid=${!}
+    wait -n
+    kill "${adb_pid}" "${fastboot_pid}" "${recovery_pid}"
+  ) >/dev/null 2>/dev/null
+  inFastboot || inAdb || inRecovery
 }
 
 [ "USAGE: adb_root
@@ -249,11 +333,11 @@
 
 Returns: true if device in root state" ]
 adb_root() {
-  [ `adb_sh echo '${USER}'` != root ] || return 0
+  [ root != "`adb_sh echo '${USER}' </dev/null`" ] || return 0
   adb root >/dev/null </dev/null 2>/dev/null
   sleep 2
   adb_wait 2m &&
-    [ `adb_sh echo '${USER}'` = root ]
+    [ root = "`adb_sh echo '${USER}' </dev/null`" ]
 }
 
 [ "USAGE: adb_unroot
@@ -262,22 +346,22 @@
 
 Returns: true if device in un root state" ]
 adb_unroot() {
-  [ `adb_sh echo '${USER}'` = root ] || return 0
+  [ root = "`adb_sh echo '${USER}' </dev/null`" ] || return 0
   adb unroot >/dev/null </dev/null 2>/dev/null
   sleep 2
   adb_wait 2m &&
-    [ `adb_sh echo '${USER}'` != root ]
+    [ root != "`adb_sh echo '${USER}' </dev/null`" ]
 }
 
-[ "USAGE: fastboot_getvar var expected
+[ "USAGE: fastboot_getvar var expected >/dev/stderr
 
 Returns: true if var output matches expected" ]
 fastboot_getvar() {
-  O=`fastboot getvar ${1} 2>&1`
-  err=${?}
+  local O=`fastboot getvar ${1} 2>&1`
+  local ret=${?}
   O="${O#< waiting for * >?}"
   O="${O%%?Finished. Total time: *}"
-  if [ 0 -ne ${err} ]; then
+  if [ 0 -ne ${ret} ]; then
     echo ${O} >&2
     false
     return
@@ -293,13 +377,50 @@
   echo ${O} >&2
 }
 
+[ "USAGE: get_active_slot >/dev/stdout
+
+Returns: with a or b string reporting active slot" ]
+get_active_slot() {
+  if inAdb || inRecovery; then
+    get_property ro.boot.slot_suffix | tr -d _
+  elif inFastboot; then
+    fastboot_getvar current-slot 2>&1 | sed -n 's/current-slot: //p'
+  else
+    false
+  fi
+}
+
+[ "USAGE: restore
+
+Do nothing: should be redefined when necessary.  Called after cleanup.
+
+Returns: reverses configurations" ]
+restore() {
+  true
+}
+
 [ "USAGE: cleanup
 
 Do nothing: should be redefined when necessary
 
-Returns: cleans up any latent resources, reverses configurations" ]
-cleanup () {
-  :
+Returns: cleans up any latent resources" ]
+cleanup() {
+  true
+}
+
+[ "USAGE: test_duration >/dev/stderr
+
+Prints the duration of the test
+
+Returns: reports duration" ]
+test_duration() {
+  if ${print_time}; then
+    echo "${BLUE}[     INFO ]${NORMAL} end `date`"
+    [ -n "${start_time}" ] || return
+    end_time=`date +%s`
+    local diff_time=`expr ${end_time} - ${start_time}`
+    echo "${BLUE}[     INFO ]${NORMAL} duration `format_duration ${diff_time}`"
+  fi >&2
 }
 
 [ "USAGE: die [-d|-t <epoch>] [message] >/dev/stderr
@@ -321,63 +442,77 @@
   fi
   echo "${RED}[  FAILED  ]${NORMAL} ${@}" >&2
   cleanup
+  restore
+  test_duration
   exit 1
 }
 
-[ "USAGE: EXPECT_EQ <lval> <rval> [message]
+[ "USAGE: EXPECT_EQ <lval> <rval> [--warning [message]]
 
 Returns true if (regex) lval matches rval" ]
 EXPECT_EQ() {
-  lval="${1}"
-  rval="${2}"
+  local lval="${1}"
+  local rval="${2}"
   shift 2
+  local error=1
+  local prefix="${RED}[    ERROR ]${NORMAL}"
+  if [ X"${1}" = X"--warning" ]; then
+      prefix="${RED}[  WARNING ]${NORMAL}"
+      error=0
+      shift 1
+  fi
   if ! ( echo X"${rval}" | grep '^X'"${lval}"'$' >/dev/null 2>/dev/null ); then
     if [ `echo ${lval}${rval}${*} | wc -c` -gt 50 -o "${rval}" != "${rval%
 *}" ]; then
-      echo "ERROR: expected \"${lval}\"" >&2
-      echo "       got \"${rval}\"" |
+      echo "${prefix} expected \"${lval}\"" >&2
+      echo "${prefix} got \"${rval}\"" |
         sed ': again
              N
              s/\(\n\)\([^ ]\)/\1             \2/
              t again' >&2
       if [ -n "${*}" ] ; then
-        echo "       ${*}" >&2
+        echo "${prefix} ${*}" >&2
       fi
     else
-      echo "ERROR: expected \"${lval}\" got \"${rval}\" ${*}" >&2
+      echo "${prefix} expected \"${lval}\" got \"${rval}\" ${*}" >&2
     fi
-    return 1
+    return ${error}
   fi
   if [ -n "${*}" ] ; then
-    if [ X"${lval}" != X"${rval}" ]; then
+    prefix="${GREEN}[     INFO ]${NORMAL}"
+    if [ X"${lval}" != X"${rval}" ]; then  # we were supplied a regex?
       if [ `echo ${lval}${rval}${*} | wc -c` -gt 60 -o "${rval}" != "${rval% *}" ]; then
-        echo "INFO: ok \"${lval}\"" >&2
+        echo "${prefix} ok \"${lval}\"" >&2
         echo "       = \"${rval}\"" |
           sed ': again
                N
                s/\(\n\)\([^ ]\)/\1          \2/
                t again' >&2
         if [ -n "${*}" ] ; then
-          echo "      ${*}" >&2
+          echo "${prefix} ${*}" >&2
         fi
       else
-        echo "INFO: ok \"${lval}\" = \"${rval}\" ${*}" >&2
+        echo "${prefix} ok \"${lval}\" = \"${rval}\" ${*}" >&2
       fi
     else
-      echo "INFO: ok \"${lval}\" ${*}" >&2
+      echo "${prefix} ok \"${lval}\" ${*}" >&2
     fi
   fi
   return 0
 }
 
-[ "USAGE: check_eq <lval> <rval> [message]
+[ "USAGE: check_eq <lval> <rval> [--warning [message]]
 
 Exits if (regex) lval mismatches rval" ]
 check_eq() {
-  left="${1}"
-  right="${2}"
+  local lval="${1}"
+  local rval="${2}"
   shift 2
-  EXPECT_EQ "${left}" "${right}" ||
+  if [ X"${1}" = X"--warning" ]; then
+      EXPECT_EQ "${lval}" "${rval}" ${*}
+      return
+  fi
+  EXPECT_EQ "${lval}" "${rval}" ||
     die "${@}"
 }
 
@@ -391,8 +526,8 @@
     cat -
   fi |
   grep -v \
-    -e "^\(overlay\|tmpfs\|none\|sysfs\|proc\|selinuxfs\|debugfs\) " \
-    -e "^\(bpf\|cg2_bpf\|pstore\|tracefs\|adb\|mtp\|ptp\|devpts\) " \
+    -e "^\(overlay\|tmpfs\|none\|sysfs\|proc\|selinuxfs\|debugfs\|bpf\) " \
+    -e "^\(binfmt_misc\|cg2_bpf\|pstore\|tracefs\|adb\|mtp\|ptp\|devpts\) " \
     -e "^\(/data/media\|/dev/block/loop[0-9]*\) " \
     -e "^rootfs / rootfs rw," \
     -e " /\(cache\|mnt/scratch\|mnt/vendor/persist\|persist\|metadata\) "
@@ -413,21 +548,71 @@
 ##  MAINLINE
 ##
 
-if [ X"-s" = X"${1}" -a -n "${2}" ]; then
-  export ANDROID_SERIAL="${2}"
-  shift 2
+OPTIONS=`getopt --alternative --unquoted \
+                --longoptions help,serial:,colour,color,no-colour,no-color \
+                --longoptions gtest_print_time,print-time \
+                -- "?hs:" ${*}` ||
+  ( echo "${USAGE}" >&2 ; false ) ||
+  die "getopt failure"
+set -- ${OPTIONS}
+
+color=false
+while [ ${#} -gt 0 ]; do
+  case ${1} in
+    -h | --help | -\?)
+      echo "${USAGE}" >&2
+      exit 0
+      ;;
+    -s | --serial)
+      export ANDROID_SERIAL=${2}
+      shift
+      ;;
+    --color | --colour)
+      color=true
+      ;;
+    --no-color | --no-colour)
+      color=false
+      ;;
+    --print-time | --gtest_print_time)
+      print_time=true
+      ;;
+    --)
+      shift
+      break
+      ;;
+    -*)
+      echo "${USAGE}" >&2
+      die "${0}: error unknown option ${1}"
+      ;;
+    *)
+      break
+      ;;
+  esac
+  shift
+done
+if ! ${color}; then
+  GREEN=""
+  RED=""
+  ORANGE=""
+  BLUE=""
+  NORMAL=""
+fi
+
+if ${print_time}; then
+  echo "${BLUE}[     INFO ]${NORMAL}" start `date` >&2
 fi
 
 inFastboot && die "device in fastboot mode"
+inRecovery && die "device in recovery mode"
 if ! inAdb; then
-  echo "${ORANGE}[  WARNING ]${NORMAL} device not in adb mode"
+  echo "${ORANGE}[  WARNING ]${NORMAL} device not in adb mode" >&2
   adb_wait 2m
 fi
 inAdb || die "specified device not in adb mode"
 isDebuggable || die "device not a debug build"
 enforcing=true
 if ! adb_su getenforce </dev/null | grep 'Enforcing' >/dev/null; then
-  echo "${ORANGE}[  WARNING ]${NORMAL} device does not have sepolicy in enforcing mode"
+  echo "${ORANGE}[  WARNING ]${NORMAL} device does not have sepolicy in enforcing mode" >&2
   enforcing=false
 fi
 
@@ -435,7 +620,7 @@
 
 D=`get_property ro.serialno`
 [ -n "${D}" ] || D=`get_property ro.boot.serialno`
-[ -z "${D}" ] || ANDROID_SERIAL=${D}
+[ -z "${D}" -o -n "${ANDROID_SERIAL}" ] || ANDROID_SERIAL=${D}
 USB_SERIAL=
 [ -z "${ANDROID_SERIAL}" ] || USB_SERIAL=`find /sys/devices -name serial |
                                           grep usb |
@@ -450,16 +635,60 @@
 BUILD_DESCRIPTION=`get_property ro.build.description`
 [ -z "${BUILD_DESCRIPTION}" ] ||
   echo "${BLUE}[     INFO ]${NORMAL} ${BUILD_DESCRIPTION}" >&2
+ACTIVE_SLOT=`get_active_slot`
+[ -z "${ACTIVE_SLOT}" ] ||
+  echo "${BLUE}[     INFO ]${NORMAL} active slot is ${ACTIVE_SLOT}" >&2
 
-VERITY_WAS_ENABLED=false
+# Report existing partition sizes
+adb_sh ls -l /dev/block/by-name/ </dev/null 2>/dev/null |
+  sed -n 's@.* \([^ ]*\) -> /dev/block/\([^ ]*\)$@\1 \2@p' |
+  while read name device; do
+    case ${name} in
+      system_[ab] | system | vendor_[ab] | vendor | super | cache)
+        case ${device} in
+          sd*)
+            device=${device%%[0-9]*}/${device}
+            ;;
+        esac
+        size=`adb_su cat /sys/block/${device}/size 2>/dev/null </dev/null` &&
+          size=`expr ${size} / 2` &&
+          echo "${BLUE}[     INFO ]${NORMAL} partition ${name} device ${device} size ${size}K" >&2
+        ;;
+    esac
+  done
+
+# Can we test remount -R command?
+overlayfs_supported=true
 if [ "orange" = "`get_property ro.boot.verifiedbootstate`" -a \
      "2" = "`get_property partition.system.verified`" ]; then
-  VERITY_WAS_ENABLED=true
+  restore() {
+    ${overlayfs_supported} || return 0
+    inFastboot &&
+      fastboot reboot &&
+      adb_wait 2m
+    inAdb &&
+      adb_root &&
+      adb enable-verity >/dev/null 2>/dev/null &&
+      adb_reboot &&
+      adb_wait 2m
+  }
+
+  echo "${GREEN}[ RUN      ]${NORMAL} Testing adb shell su root remount -R command" >&2
+
+  adb_su remount -R system </dev/null || true
+  sleep 2
+  adb_wait 2m ||
+    die "waiting for device after remount -R `usb_status`"
+  if [ "orange" != "`get_property ro.boot.verifiedbootstate`" -o \
+       "2" = "`get_property partition.system.verified`" ]; then
+    die "remount -R command failed"
+  fi
+
+  echo "${GREEN}[       OK ]${NORMAL} adb shell su root remount -R command" >&2
 fi
 
 echo "${GREEN}[ RUN      ]${NORMAL} Testing kernel support for overlayfs" >&2
 
-overlayfs_supported=true;
 adb_wait || die "wait for device failed"
 adb_sh ls -d /sys/module/overlay </dev/null >/dev/null 2>/dev/null ||
   adb_sh grep "nodev${TAB}overlay" /proc/filesystems </dev/null >/dev/null 2>/dev/null &&
@@ -470,20 +699,17 @@
   ) ||
   overlayfs_supported=false
 if ${overlayfs_supported}; then
-  case `adb_sh uname -r </dev/null` in
-    4.[6789].* | 4.[1-9][0-9]* | [56789].*)
-      adb_su ls /sys/module/overlay/parameters/override_creds </dev/null >/dev/null &&
-        echo "${GREEN}[       OK ]${NORMAL} overlay module supports override_creds" >&2 ||
-        (
-          echo "${ORANGE}[  WARNING ]${NORMAL} overlay module does not support override_creds" >&2 &&
-          false
-        ) ||
-        overlayfs_supported=false;
-      ;;
-    *)
-      echo "${GREEN}[       OK ]${NORMAL} overlay module uses callers creds" >&2
-      ;;
-  esac
+  adb_su ls /sys/module/overlay/parameters/override_creds </dev/null >/dev/null 2>/dev/null &&
+    echo "${GREEN}[       OK ]${NORMAL} overlay module supports override_creds" >&2 ||
+    case `adb_sh uname -r </dev/null` in
+      4.[456789].* | 4.[1-9][0-9]* | [56789].*)
+        echo "${ORANGE}[  WARNING ]${NORMAL} overlay module does not support override_creds" >&2 &&
+        overlayfs_supported=false
+        ;;
+      *)
+        echo "${GREEN}[       OK ]${NORMAL} overlay module uses caller's creds" >&2
+        ;;
+    esac
 fi
 
 adb_root ||
@@ -531,7 +757,7 @@
 D=`echo "${D}" | cut -s -d' ' -f1 | sort -u`
 no_dedupe=true
 for d in ${D}; do
-  adb_sh tune2fs -l $d 2>&1 |
+  adb_sh tune2fs -l $d </dev/null 2>&1 |
     grep "Filesystem features:.*shared_blocks" >/dev/null &&
   no_dedupe=false
 done
@@ -550,8 +776,8 @@
 H=`adb disable-verity 2>&1`
 err=${?}
 L=
-D="${H%?Now reboot your device for settings to take effect}"
-if [ X"${D}" != X"${D##*using overlayfs}" ]; then
+D="${H%?Now reboot your device for settings to take effect*}"
+if [ X"${D}" != X"${D##*[Uu]sing overlayfs}" ]; then
   echo "${GREEN}[       OK ]${NORMAL} using overlayfs" >&2
 fi
 if [ ${err} != 0 ]; then
@@ -583,8 +809,8 @@
   T=`adb_date`
   H=`adb disable-verity 2>&1`
   err=${?}
-  D="${H%?Now reboot your device for settings to take effect}"
-  if [ X"${D}" != X"${D##*using overlayfs}" ]; then
+  D="${H%?Now reboot your device for settings to take effect*}"
+  if [ X"${D}" != X"${D##*[Uu]sing overlayfs}" ]; then
     echo "${GREEN}[       OK ]${NORMAL} using overlayfs" >&2
   fi
   if [ ${err} != 0 ]; then
@@ -615,7 +841,11 @@
 
 echo "${GREEN}[ RUN      ]${NORMAL} remount" >&2
 
-adb remount ||
+D=`adb remount 2>&1`
+ret=${?}
+echo "${D}"
+[ ${ret} != 0 ] ||
+  [ X"${D}" = X"${D##*remount failed}" ] ||
   ( [ -n "${L}" ] && echo "${L}" && false ) ||
   die -t "${T}" "adb remount failed"
 D=`adb_sh df -k </dev/null` &&
@@ -665,10 +895,33 @@
     die  "overlay takeover after remount"
   !(adb_sh grep "^overlay " /proc/mounts </dev/null |
     skip_unrelated_mounts |
-    grep " overlay ro,") &&
-    !(adb_sh grep " rw," /proc/mounts </dev/null |
-      skip_administrative_mounts data) ||
+    grep " overlay ro,") ||
     die "remount overlayfs missed a spot (ro)"
+  D=`adb_sh grep " rw," /proc/mounts </dev/null |
+     skip_administrative_mounts data`
+  if echo "${D}" | grep /dev/root >/dev/null; then
+    D=`echo / /
+       echo "${D}" | grep -v /dev/root`
+  fi
+  D=`echo "${D}" | cut -s -d' ' -f1 | sort -u`
+  bad_rw=false
+  for d in ${D}; do
+    if adb_sh tune2fs -l $d </dev/null 2>&1 |
+       grep "Filesystem features:.*shared_blocks" >/dev/null; then
+      bad_rw=true
+    else
+      d=`adb_sh df -k ${D} </dev/null |
+       sed 's@\([%] /\)\(apex\|bionic\|system\|vendor\)/[^ ][^ ]*$@\1@'`
+      [ X"${d}" = X"${d##* 100[%] }" ] ||
+        bad_rw=true
+    fi
+  done
+  [ -z "${D}" ] ||
+    D=`adb_sh df -k ${D} </dev/null |
+       sed -e 's@\([%] /\)\(apex\|bionic\|system\|vendor\)/[^ ][^ ]*$@\1@' \
+           -e 's/^Filesystem      /Filesystem (rw) /'`
+  [ -z "${D}" ] || echo "${D}"
+  ${bad_rw} && die "remount overlayfs missed a spot (rw)"
 else
   if [ ${ret} = 0 ]; then
     die -t ${T} "unexpected overlay takeover"
@@ -710,7 +963,7 @@
 
 adb_reboot &&
   adb_wait 2m ||
-  die "reboot after override content added failed"
+  die "reboot after override content added failed `usb_status`"
 
 if ${overlayfs_needed}; then
   D=`adb_su df -k </dev/null` &&
@@ -721,26 +974,25 @@
 
   adb_su sed -n '1,/overlay \/system/p' /proc/mounts </dev/null |
     skip_administrative_mounts |
-    grep -v ' \(squashfs\|ext4\|f2fs\|vfat\) ' &&
+    grep -v ' \(erofs\|squashfs\|ext4\|f2fs\|vfat\) ' &&
     echo "${ORANGE}[  WARNING ]${NORMAL} overlay takeover after first stage init" >&2 ||
     echo "${GREEN}[       OK ]${NORMAL} overlay takeover in first stage init" >&2
 fi
 
-B="`adb_cat /system/hello`" ||
-  die "re-read /system/hello after reboot"
-check_eq "${A}" "${B}" /system after reboot
-echo "${GREEN}[       OK ]${NORMAL} /system content remains after reboot" >&2
-# Only root can read vendor if sepolicy permissions are as expected.
 if ${enforcing}; then
-  adb_unroot
-  B="`adb_cat /vendor/hello`" &&
-    die "re-read /vendor/hello after reboot w/o root"
+  adb_unroot ||
+    die "device not in unroot'd state"
+  B="`adb_cat /vendor/hello 2>&1`"
   check_eq "cat: /vendor/hello: Permission denied" "${B}" vendor after reboot w/o root
   echo "${GREEN}[       OK ]${NORMAL} /vendor content correct MAC after reboot" >&2
 fi
-adb_root &&
-  B="`adb_cat /vendor/hello`" ||
-  die "re-read /vendor/hello after reboot"
+B="`adb_cat /system/hello`"
+check_eq "${A}" "${B}" /system after reboot
+echo "${GREEN}[       OK ]${NORMAL} /system content remains after reboot" >&2
+# Only root can read vendor if sepolicy permissions are as expected.
+adb_root ||
+  die "adb root"
+B="`adb_cat /vendor/hello`"
 check_eq "${A}" "${B}" vendor after reboot
 echo "${GREEN}[       OK ]${NORMAL} /vendor content remains after reboot" >&2
 
@@ -749,23 +1001,37 @@
   adb pull /system/lib/bootstrap/libc.so ${tempdir}/libc.so.fromdevice >/dev/null ||
   die "pull libc.so from device"
 diff ${tempdir}/libc.so ${tempdir}/libc.so.fromdevice > /dev/null || die "libc.so differ"
-rm -r ${tempdir}
+rm -rf ${tempdir}
+cleanup() {
+  true
+}
 echo "${GREEN}[       OK ]${NORMAL} /system/lib/bootstrap/libc.so content remains after reboot" >&2
 
 echo "${GREEN}[ RUN      ]${NORMAL} flash vendor, confirm its content disappears" >&2
 
 H=`adb_sh echo '${HOSTNAME}' </dev/null 2>/dev/null`
+is_bootloader_fastboot=false
+# cuttlefish?
+[ X"${H}" != X"${H#vsoc}" ] || is_bootloader_fastboot=true
 is_userspace_fastboot=false
-if [ -z "${ANDROID_PRODUCT_OUT}" ]; then
+
+if ! ${is_bootloader_fastboot}; then
+  echo "${ORANGE}[  WARNING ]${NORMAL} does not support fastboot, skipping"
+elif [ -z "${ANDROID_PRODUCT_OUT}" ]; then
   echo "${ORANGE}[  WARNING ]${NORMAL} build tree not setup, skipping"
 elif [ ! -s "${ANDROID_PRODUCT_OUT}/vendor.img" ]; then
   echo "${ORANGE}[  WARNING ]${NORMAL} vendor image missing, skipping"
 elif [ "${ANDROID_PRODUCT_OUT}" = "${ANDROID_PRODUCT_OUT%*/${H}}" ]; then
   echo "${ORANGE}[  WARNING ]${NORMAL} wrong vendor image, skipping"
+elif [ -z "${ANDROID_HOST_OUT}" ]; then
+  echo "${ORANGE}[  WARNING ]${NORMAL} please run lunch, skipping"
 else
-  adb reboot-fastboot &&
-    fastboot_wait 2m &&
-    fastboot flash vendor ||
+  adb reboot-fastboot ||
+    die "fastbootd not supported (wrong adb in path?)"
+  any_wait 2m &&
+    inFastboot ||
+    die "reboot into fastboot to flash vendor `usb_status` (bad bootloader?)"
+  fastboot flash vendor ||
     ( fastboot reboot && false) ||
     die "fastboot flash vendor"
   fastboot_getvar is-userspace yes &&
@@ -804,7 +1070,7 @@
     die "can not reboot out of fastboot"
   echo "${ORANGE}[  WARNING ]${NORMAL} adb after fastboot"
   adb_wait 2m ||
-    die "did not reboot after flash"
+    die "did not reboot after flash `usb_status`"
   if ${overlayfs_needed}; then
     adb_root &&
       D=`adb_sh df -k </dev/null` &&
@@ -818,26 +1084,22 @@
       if ${is_userspace_fastboot}; then
         die  "overlay supposed to be minus /vendor takeover after flash vendor"
       else
-        echo "${ORANGE}[  WARNING ]${NORMAL} user fastboot missing, ignoring a failure"
-        ( die  "overlay supposed to be minus /vendor takeover after flash vendor" )
+        echo "${ORANGE}[  WARNING ]${NORMAL} user fastboot missing required to invalidate, ignoring a failure" >&2
+        echo "${ORANGE}[  WARNING ]${NORMAL} overlay supposed to be minus /vendor takeover after flash vendor" >&2
       fi
   fi
-  B="`adb_cat /system/hello`" ||
-    die "re-read /system/hello after flash vendor"
+  B="`adb_cat /system/hello`"
   check_eq "${A}" "${B}" system after flash vendor
   adb_root ||
     die "adb root"
-  B="`adb_cat /vendor/hello`" &&
-    if ${is_userspace_fastboot} || ! ${overlayfs_needed}; then
-      die "re-read /vendor/hello after flash vendor"
-    else
-      echo "${ORANGE}[  WARNING ]${NORMAL} user fastboot missing, ignoring a failure"
-      ( die "re-read /vendor/hello after flash vendor" )
-    fi
+  B="`adb_cat /vendor/hello`"
   if ${is_userspace_fastboot} || ! ${overlayfs_needed}; then
-    check_eq "cat: /vendor/hello: No such file or directory" "${B}" vendor after flash vendor
+    check_eq "cat: /vendor/hello: No such file or directory" "${B}" \
+             vendor content after flash vendor
   else
-    ( check_eq "cat: /vendor/hello: No such file or directory" "${B}" vendor after flash vendor )
+    echo "${ORANGE}[  WARNING ]${NORMAL} user fastboot missing required to invalidate, ignoring a failure" >&2
+    check_eq "cat: /vendor/hello: No such file or directory" "${B}" \
+             --warning vendor content after flash vendor
   fi
 fi
 
@@ -847,7 +1109,7 @@
 H=`adb remount 2>&1`
 err=${?}
 L=
-D="${H%?Now reboot your device for settings to take effect}"
+D="${H%?Now reboot your device for settings to take effect*}"
 if [ X"${H}" != X"${D}" ]; then
   echo "${ORANGE}[  WARNING ]${NORMAL} adb remount requires a reboot after partial flash (legacy avb)"
   L=`adb_logcat -b all -v nsec -t ${T} 2>&1`
@@ -865,12 +1127,10 @@
   adb_sh rm /system/hello </dev/null ||
   ( [ -n "${L}" ] && echo "${L}" && false ) ||
   die -t ${T} "cleanup hello"
-B="`adb_cat /system/hello`" &&
-  die "re-read /system/hello after rm"
-check_eq "cat: /system/hello: No such file or directory" "${B}" after flash rm
-B="`adb_cat /vendor/hello`" &&
-  die "re-read /vendor/hello after rm"
-check_eq "cat: /vendor/hello: No such file or directory" "${B}" after flash rm
+B="`adb_cat /system/hello`"
+check_eq "cat: /system/hello: No such file or directory" "${B}" after rm
+B="`adb_cat /vendor/hello`"
+check_eq "cat: /vendor/hello: No such file or directory" "${B}" after rm
 
 if [ -n "${scratch_partition}" ]; then
 
@@ -878,17 +1138,18 @@
 
   adb reboot-fastboot ||
     die "Reboot into fastbootd"
+  img=${TMPDIR}/adb-remount-test-${$}.img
   cleanup() {
-    rm /tmp/adb-remount-test.img
+    rm ${img}
   }
-  dd if=/dev/zero of=/tmp/adb-remount-test.img bs=4096 count=16 2>/dev/null &&
+  dd if=/dev/zero of=${img} bs=4096 count=16 2>/dev/null &&
     fastboot_wait 2m ||
-    die "reboot into fastboot"
-  fastboot flash --force ${scratch_partition} /tmp/adb-remount-test.img
+    die "reboot into fastboot `usb_status`"
+  fastboot flash --force ${scratch_partition} ${img}
   err=${?}
   cleanup
   cleanup() {
-    :
+    true
   }
   fastboot reboot ||
     die "can not reboot out of fastboot"
@@ -900,14 +1161,32 @@
   T=`adb_date`
   D=`adb disable-verity 2>&1`
   err=${?}
-  adb remount ||
-    die "remount failed"
+  if [ X"${D}" != "${D%?Now reboot your device for settings to take effect*}" ]
+  then
+    echo "${ORANGE}[  WARNING ]${NORMAL} adb disable-verity requires a reboot after partial flash"
+    adb_reboot &&
+      adb_wait 2m &&
+      adb_root ||
+      die "failed to reboot"
+    T=`adb_date`
+    D="${D}
+`adb disable-verity 2>&1`"
+    err=${?}
+  fi
+
   echo "${D}"
   [ ${err} = 0 ] &&
     [ X"${D}" = X"${D##*setup failed}" ] &&
-    [ X"${D}" != X"${D##*using overlayfs}" ] &&
+    [ X"${D}" != X"${D##*[Uu]sing overlayfs}" ] &&
     echo "${GREEN}[       OK ]${NORMAL} ${scratch_partition} recreated" >&2 ||
     die -t ${T} "setup for overlayfs"
+  D=`adb remount 2>&1`
+  err=${?}
+  echo "${D}"
+  [ ${err} != 0 ] ||
+    [ X"${D}" = X"${D##*remount failed}" ] ||
+    ( echo "${D}" && false ) ||
+    die -t ${T} "remount failed"
 fi
 
 echo "${GREEN}[ RUN      ]${NORMAL} test raw remount commands" >&2
@@ -916,20 +1195,75 @@
 adb_reboot &&
   adb_wait 2m ||
   die "lost device after reboot to ro state `usb_status`"
-adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null &&
+adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null &&
   die "/vendor is not read-only"
-adb_su mount -o rw,remount /vendor ||
+adb_su mount -o rw,remount /vendor </dev/null ||
   die "remount command"
-adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null ||
+adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null ||
   die "/vendor is not read-write"
 echo "${GREEN}[       OK ]${NORMAL} mount -o rw,remount command works" >&2
 
-if $VERITY_WAS_ENABLED && $overlayfs_supported; then
+# Prerequisite is a prepped device from above.
+adb_reboot &&
+  adb_wait 2m ||
+  die "lost device after reboot to ro state (USB stack broken?)"
+adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null &&
+  die "/vendor is not read-only"
+adb_su remount vendor </dev/null ||
+  die "remount command"
+adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null ||
+  die "/vendor is not read-write"
+adb_sh grep " /system .* rw," /proc/mounts >/dev/null </dev/null &&
+  die "/vendor is not read-only"
+echo "${GREEN}[       OK ]${NORMAL} remount command works from setup" >&2
+
+# Prerequisite is an overlayfs deconstructed device but with verity disabled.
+# This also saves a lot of 'noise' from the command doing a mkfs on backing
+# storage and all the related tuning and adjustment.
+for d in ${OVERLAYFS_BACKING}; do
+  adb_su rm -rf /${d}/overlay </dev/null ||
+    die "/${d}/overlay wipe"
+done
+adb_reboot &&
+  adb_wait 2m ||
+  die "lost device after reboot after wipe (USB stack broken?)"
+adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null &&
+  die "/vendor is not read-only"
+adb_su remount vendor </dev/null ||
+  die "remount command"
+adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null ||
+  die "/vendor is not read-write"
+adb_sh grep " /system .* rw," /proc/mounts >/dev/null </dev/null &&
+  die "/system is not read-only"
+echo "${GREEN}[       OK ]${NORMAL} remount command works from scratch" >&2
+
+restore
+err=${?}
+
+if [ ${err} = 0 ] && ${overlayfs_supported}; then
+  echo "${GREEN}[ RUN      ]${NORMAL} test 'adb remount -R'" >&2
   adb_root &&
-    adb enable-verity &&
-    adb_reboot &&
+    adb remount -R &&
     adb_wait 2m ||
-    die "failed to restore verity" >&2
+    die "adb remount -R"
+  if [ "orange" != "`get_property ro.boot.verifiedbootstate`" -o \
+       "2" = "`get_property partition.system.verified`" ]; then
+    die "remount -R command failed to disable verity"
+  fi
+
+  echo "${GREEN}[       OK ]${NORMAL} 'adb remount -R' command" >&2
+
+  restore
+  err=${?}
 fi
 
+restore() {
+  true
+}
+
+[ ${err} = 0 ] ||
+  die "failed to restore verity" >&2
+
 echo "${GREEN}[  PASSED  ]${NORMAL} adb remount" >&2
+
+test_duration
diff --git a/fs_mgr/tests/data/fstab.example b/fs_mgr/tests/data/fstab.example
deleted file mode 100644
index aebce32..0000000
--- a/fs_mgr/tests/data/fstab.example
+++ /dev/null
@@ -1,15 +0,0 @@
-# Android fstab file.
-
-#<src>                                              <mnt_point>        <type>      <mnt_flags and options>                               <fs_mgr_flags>
-
-/dev/block/bootdevice/by-name/system                /                  ext4        ro,barrier=1                                          wait,slotselect,avb
-/dev/block/bootdevice/by-name/metadata              /metadata          ext4        noatime,nosuid,nodev,discard                          wait,formattable
-/dev/block/bootdevice/by-name/userdata              /data              f2fs        noatime,nosuid,nodev,discard,reserve_root=32768,resgid=1065,fsync_mode=nobarrier       latemount,wait,check,fileencryption=ice,keydirectory=/metadata/vold/metadata_encryption,quota,formattable,sysfs_path=/sys/devices/platform/soc/1d84000.ufshc,reservedsize=128M
-/dev/block/bootdevice/by-name/misc                  /misc              emmc        defaults                                              defaults
-/dev/block/bootdevice/by-name/modem                 /vendor/firmware_mnt          vfat        ro,shortname=lower,uid=1000,gid=1000,dmask=227,fmask=337,context=u:object_r:firmware_file:s0   wait,slotselect
-/devices/platform/soc/a600000.ssusb/a600000.dwc3*   auto               vfat        defaults                                              voldmanaged=usb:auto
-/dev/block/zram0                                    none               swap        defaults                                              zramsize=1073741824,max_comp_streams=8
-/dev/block/zram0                                    none2              swap        nodiratime,remount,bind                               zramsize=1073741824,max_comp_streams=8
-/dev/block/zram0                                    none3              swap        unbindable,private,slave                              zramsize=1073741824,max_comp_streams=8
-/dev/block/zram0                                    none4              swap        noexec,shared,rec                                     zramsize=1073741824,max_comp_streams=8
-/dev/block/zram0                                    none5              swap        rw                                                    zramsize=1073741824,max_comp_streams=8
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 1815a38..72afa69 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -203,10 +203,32 @@
     EXPECT_EQ(i, fstab.size());
 }
 
-TEST(fs_mgr, ReadFstabFromFile_MountOptions) {
+// TODO(124837435): enable it later when it can pass TreeHugger.
+TEST(fs_mgr, DISABLED_ReadFstabFromFile_MountOptions) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    std::string fstab_contents = R"fs(
+source /            ext4    ro,barrier=1                    wait,slotselect,avb
+source /metadata    ext4    noatime,nosuid,nodev,discard    wait,formattable
+
+source /data        f2fs    noatime,nosuid,nodev,discard,reserve_root=32768,resgid=1065,fsync_mode=nobarrier    latemount,wait,check,fileencryption=ice,keydirectory=/metadata/vold/metadata_encryption,quota,formattable,sysfs_path=/sys/devices/platform/soc/1d84000.ufshc,reservedsize=128M
+
+source /misc        emmc    defaults                        defaults
+
+source /vendor/firmware_mnt    vfat    ro,shortname=lower,uid=1000,gid=1000,dmask=227,fmask=337,context=u:object_r:firmware_file:s0    wait,slotselect
+
+source auto         vfat    defaults                        voldmanaged=usb:auto
+source none         swap    defaults                        zramsize=1073741824,max_comp_streams=8
+source none2        swap    nodiratime,remount,bind         zramsize=1073741824,max_comp_streams=8
+source none3        swap    unbindable,private,slave        zramsize=1073741824,max_comp_streams=8
+source none4        swap    noexec,shared,rec               zramsize=1073741824,max_comp_streams=8
+source none5        swap    rw                              zramsize=1073741824,max_comp_streams=8
+)fs";
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
     Fstab fstab;
-    std::string fstab_file = android::base::GetExecutableDirectory() + "/data/fstab.example";
-    EXPECT_TRUE(ReadFstabFromFile(fstab_file, &fstab));
+    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+    ASSERT_EQ(11U, fstab.size());
 
     EXPECT_EQ("/", fstab[0].mount_point);
     EXPECT_EQ(static_cast<unsigned long>(MS_RDONLY), fstab[0].flags);
@@ -286,7 +308,8 @@
     // clang-format on
 }
 
-TEST(fs_mgr, ReadFstabFromFile_FsMgrFlags) {
+// TODO(124837435): enable it later when it can pass TreeHugger.
+TEST(fs_mgr, DISABLED_ReadFstabFromFile_FsMgrFlags) {
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
     std::string fstab_contents = R"fs(
@@ -297,7 +320,7 @@
 source none4       swap   defaults      checkpoint=fs
 source none5       swap   defaults      defaults
 )fs";
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -376,7 +399,7 @@
 source none2       swap   defaults      forcefdeorfbe=
 
 )fs";
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -456,7 +479,7 @@
     std::string fstab_contents = R"fs(
 source none0       swap   defaults      encryptable=/dir/key
 )fs";
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -480,7 +503,7 @@
 source none2       swap   defaults      voldmanaged=sdcard:3
 source none3       swap   defaults      voldmanaged=sdcard:auto
 )fs";
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -521,7 +544,7 @@
 source none0       swap   defaults      length=blah
 source none1       swap   defaults      length=123456
 )fs";
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -547,7 +570,7 @@
 source none0       swap   defaults      swapprio=blah
 source none1       swap   defaults      swapprio=123456
 )fs";
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -577,7 +600,7 @@
 source none4       swap   defaults      zramsize=105%
 source none5       swap   defaults      zramsize=%
 )fs";
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -623,7 +646,7 @@
 source none0       swap   defaults      verify=/dir/key
 )fs";
 
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -646,7 +669,7 @@
 source none0       swap   defaults      forceencrypt=/dir/key
 )fs";
 
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -669,7 +692,7 @@
 source none0       swap   defaults      forcefdeorfbe=/dir/key
 )fs";
 
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -704,7 +727,7 @@
 source none10      swap   defaults      fileencryption=ice:adiantum:
 )fs";
 
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -787,7 +810,7 @@
 source none0       swap   defaults      max_comp_streams=blah
 source none1       swap   defaults      max_comp_streams=123456
 )fs";
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -815,7 +838,7 @@
 source none2       swap   defaults      reservedsize=1K
 source none3       swap   defaults      reservedsize=2m
 )fs";
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -853,7 +876,7 @@
 source none2       swap   defaults      eraseblk=5000
 source none3       swap   defaults      eraseblk=8192
 )fs";
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -891,7 +914,7 @@
 source none2       swap   defaults      logicalblk=5000
 source none3       swap   defaults      logicalblk=8192
 )fs";
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -925,13 +948,14 @@
     ASSERT_TRUE(tf.fd != -1);
     std::string fstab_contents = R"fs(
 source none0       swap   defaults      avb=vbmeta_partition
+source none1       swap   defaults      avb_keys=/path/to/test.avbpubkey
 )fs";
 
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
-    ASSERT_EQ(1U, fstab.size());
+    ASSERT_EQ(2U, fstab.size());
 
     auto entry = fstab.begin();
     EXPECT_EQ("none0", entry->mount_point);
@@ -941,6 +965,12 @@
     EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
 
     EXPECT_EQ("vbmeta_partition", entry->vbmeta_partition);
+    entry++;
+
+    EXPECT_EQ("none1", entry->mount_point);
+    FstabEntry::FsMgrFlags empty_flags = {};  // no flags should be set for avb_keys.
+    EXPECT_TRUE(CompareFlags(empty_flags, entry->fs_mgr_flags));
+    EXPECT_EQ("/path/to/test.avbpubkey", entry->avb_keys);
 }
 
 TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_KeyDirectory) {
@@ -950,7 +980,7 @@
 source none0       swap   defaults      keydirectory=/dir/key
 )fs";
 
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -972,7 +1002,7 @@
 source none0       swap   defaults      sysfs_path=/sys/device
 )fs";
 
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -1002,7 +1032,7 @@
 
 )fs";
 
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
diff --git a/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java b/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java
new file mode 100644
index 0000000..f08cab2
--- /dev/null
+++ b/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+package com.android.tests.vendoroverlay;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test the vendor overlay feature. Requires adb remount with OverlayFS.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class VendorOverlayHostTest extends BaseHostJUnit4Test {
+  boolean wasRoot = false;
+
+  @Before
+  public void setup() throws DeviceNotAvailableException {
+    wasRoot = getDevice().isAdbRoot();
+    if (!wasRoot) {
+      Assume.assumeTrue("Test requires root", getDevice().enableAdbRoot());
+    }
+
+    Assume.assumeTrue("Skipping vendor overlay test due to lack of necessary OverlayFS support",
+        testConditionsMet());
+
+    getDevice().remountSystemWritable();
+    // Was OverlayFS used by adb remount? Without it we can't safely re-enable dm-verity.
+    Pattern vendorPattern = Pattern.compile("^overlay .+ /vendor$", Pattern.MULTILINE);
+    Pattern productPattern = Pattern.compile("^overlay .+ /product$", Pattern.MULTILINE);
+    CommandResult result = getDevice().executeShellV2Command("df");
+    Assume.assumeTrue("OverlayFS not used for adb remount on /vendor",
+        vendorPattern.matcher(result.getStdout()).find());
+    Assume.assumeTrue("OverlayFS not used for adb remount on /product",
+        productPattern.matcher(result.getStdout()).find());
+  }
+
+  private boolean cmdSucceeded(CommandResult result) {
+    return result.getStatus() == CommandStatus.SUCCESS;
+  }
+
+  private void assumeMkdirSuccess(String dir) throws DeviceNotAvailableException {
+    CommandResult result = getDevice().executeShellV2Command("mkdir -p " + dir);
+    Assume.assumeTrue("Couldn't create " + dir, cmdSucceeded(result));
+  }
+
+  /**
+   * Tests that files in the appropriate /product/vendor_overlay dir are overlaid onto /vendor.
+   */
+  @Test
+  public void testVendorOverlay() throws DeviceNotAvailableException {
+    String vndkVersion = getDevice().executeShellV2Command("getprop ro.vndk.version").getStdout();
+
+    // Create files and modify policy
+    CommandResult result = getDevice().executeShellV2Command(
+        "echo '/(product|system/product)/vendor_overlay/" + vndkVersion +
+        "/.* u:object_r:vendor_file:s0'" + " >> /system/etc/selinux/plat_file_contexts");
+    Assume.assumeTrue("Couldn't modify plat_file_contexts", cmdSucceeded(result));
+    assumeMkdirSuccess("/vendor/testdir");
+    assumeMkdirSuccess("/vendor/diffcontext");
+    assumeMkdirSuccess("/product/vendor_overlay/'" + vndkVersion + "'/testdir");
+    result = getDevice().executeShellV2Command(
+        "echo overlay > /product/vendor_overlay/'" + vndkVersion + "'/testdir/test");
+    Assume.assumeTrue("Couldn't create text file in testdir", cmdSucceeded(result));
+    assumeMkdirSuccess("/product/vendor_overlay/'" + vndkVersion + "'/noexist/test");
+    assumeMkdirSuccess("/product/vendor_overlay/'" + vndkVersion + "'/diffcontext/test");
+    result = getDevice().executeShellV2Command(
+        "restorecon -r /product/vendor_overlay/'" + vndkVersion + "'/testdir");
+    Assume.assumeTrue("Couldn't write testdir context", cmdSucceeded(result));
+
+    getDevice().reboot();
+
+    // Test that the file was overlaid properly
+    result = getDevice().executeShellV2Command("[ $(cat /vendor/testdir/test) = overlay ]");
+    Assert.assertTrue("test file was not overlaid onto /vendor/", cmdSucceeded(result));
+    result = getDevice().executeShellV2Command("[ ! -d /vendor/noexist/test ]");
+    Assert.assertTrue("noexist dir shouldn't exist on /vendor", cmdSucceeded(result));
+    result = getDevice().executeShellV2Command("[ ! -d /vendor/diffcontext/test ]");
+    Assert.assertTrue("diffcontext dir shouldn't exist on /vendor", cmdSucceeded(result));
+  }
+
+  // Duplicate of fs_mgr_overlayfs_valid() logic
+  // Requires root
+  public boolean testConditionsMet() throws DeviceNotAvailableException {
+    if (cmdSucceeded(getDevice().executeShellV2Command(
+        "[ -e /sys/module/overlay/parameters/override_creds ]"))) {
+      return true;
+    }
+    if (cmdSucceeded(getDevice().executeShellV2Command("[ ! -e /sys/module/overlay ]"))) {
+      return false;
+    }
+    CommandResult result = getDevice().executeShellV2Command("awk '{ print $3 }' /proc/version");
+    Pattern kernelVersionPattern = Pattern.compile("([1-9])[.]([0-9]+).*");
+    Matcher kernelVersionMatcher = kernelVersionPattern.matcher(result.getStdout());
+    kernelVersionMatcher.find();
+    int majorKernelVersion;
+    int minorKernelVersion;
+    try {
+      majorKernelVersion = Integer.parseInt(kernelVersionMatcher.group(1));
+      minorKernelVersion = Integer.parseInt(kernelVersionMatcher.group(2));
+    } catch (Exception e) {
+      return false;
+    }
+    if (majorKernelVersion < 4) {
+      return true;
+    }
+    if (majorKernelVersion > 4) {
+      return false;
+    }
+    if (minorKernelVersion > 6) {
+      return false;
+    }
+    return true;
+  }
+
+  @After
+  public void tearDown() throws DeviceNotAvailableException {
+    if (getDevice().executeAdbCommand("enable-verity").contains("Now reboot your device")) {
+      getDevice().reboot();
+    }
+    if (!wasRoot) {
+      getDevice().disableAdbRoot();
+    }
+  }
+}
+
diff --git a/fs_mgr/tests/vendor-overlay-test.xml b/fs_mgr/tests/vendor-overlay-test.xml
new file mode 100644
index 0000000..0b5c8cc
--- /dev/null
+++ b/fs_mgr/tests/vendor-overlay-test.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+<configuration description="Config for vendor overlay test cases">
+    <test class="com.android.tradefed.testtype.HostTest" >
+        <option name="jar" value="fs_mgr_vendor_overlay_test.jar" />
+    </test>
+</configuration>
+
diff --git a/gatekeeperd/Android.bp b/gatekeeperd/Android.bp
index 8e9c7ea..2b7db79 100644
--- a/gatekeeperd/Android.bp
+++ b/gatekeeperd/Android.bp
@@ -31,6 +31,7 @@
     shared_libs: [
         "libbinder",
         "libgatekeeper",
+        "libgsi",
         "liblog",
         "libhardware",
         "libbase",
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index 446b66e..8700c34 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -26,6 +26,8 @@
 #include <memory>
 
 #include <android/security/keystore/IKeystoreService.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/PermissionCache.h>
@@ -34,6 +36,7 @@
 #include <hardware/hw_auth_token.h>
 #include <keystore/keystore.h> // For error code
 #include <keystore/keystore_return_types.h>
+#include <libgsi/libgsi.h>
 #include <log/log.h>
 #include <utils/Log.h>
 #include <utils/String16.h>
@@ -59,6 +62,7 @@
     GateKeeperProxy() {
         clear_state_if_needed_done = false;
         hw_device = IGatekeeper::getService();
+        is_running_gsi = android::base::GetBoolProperty(android::gsi::kGsiBootedProp, false);
 
         if (hw_device == nullptr) {
             ALOGW("falling back to software GateKeeper");
@@ -86,7 +90,7 @@
             return;
         }
 
-        if (mark_cold_boot()) {
+        if (mark_cold_boot() && !is_running_gsi) {
             ALOGI("cold boot: clearing state");
             if (hw_device != nullptr) {
                 hw_device->deleteAllUsers([](const GatekeeperResponse &){});
@@ -138,6 +142,18 @@
         }
     }
 
+    // This should only be called on uids being passed to the GateKeeper HAL. It ensures that
+    // secure storage shared across a GSI image and a host image will not overlap.
+    uint32_t adjust_uid(uint32_t uid) {
+        static constexpr uint32_t kGsiOffset = 1000000;
+        CHECK(uid < kGsiOffset);
+        CHECK(hw_device != nullptr);
+        if (is_running_gsi) {
+            return uid + kGsiOffset;
+        }
+        return uid;
+    }
+
     virtual int enroll(uint32_t uid,
             const uint8_t *current_password_handle, uint32_t current_password_handle_length,
             const uint8_t *current_password, uint32_t current_password_length,
@@ -181,7 +197,8 @@
             newPwd.setToExternal(const_cast<uint8_t*>(desired_password),
                                  desired_password_length);
 
-            Return<void> hwRes = hw_device->enroll(uid, curPwdHandle, curPwd, newPwd,
+            uint32_t hw_uid = adjust_uid(uid);
+            Return<void> hwRes = hw_device->enroll(hw_uid, curPwdHandle, curPwd, newPwd,
                               [&ret, enrolled_password_handle, enrolled_password_handle_length]
                                    (const GatekeeperResponse &rsp) {
                 ret = static_cast<int>(rsp.code); // propagate errors
@@ -266,13 +283,14 @@
             // handle version 0 does not have hardware backed flag, and thus cannot be upgraded to
             // a HAL if there was none before
             if (handle->version == 0 || handle->hardware_backed) {
+                uint32_t hw_uid = adjust_uid(uid);
                 android::hardware::hidl_vec<uint8_t> curPwdHandle;
                 curPwdHandle.setToExternal(const_cast<uint8_t*>(enrolled_password_handle),
                                            enrolled_password_handle_length);
                 android::hardware::hidl_vec<uint8_t> enteredPwd;
                 enteredPwd.setToExternal(const_cast<uint8_t*>(provided_password),
                                          provided_password_length);
-                Return<void> hwRes = hw_device->verify(uid, challenge, curPwdHandle, enteredPwd,
+                Return<void> hwRes = hw_device->verify(hw_uid, challenge, curPwdHandle, enteredPwd,
                                         [&ret, request_reenroll, auth_token, auth_token_length]
                                              (const GatekeeperResponse &rsp) {
                     ret = static_cast<int>(rsp.code); // propagate errors
@@ -354,7 +372,8 @@
         clear_sid(uid);
 
         if (hw_device != nullptr) {
-            hw_device->deleteUser(uid, [] (const GatekeeperResponse &){});
+            uint32_t hw_uid = adjust_uid(uid);
+            hw_device->deleteUser(hw_uid, [] (const GatekeeperResponse &){});
         }
     }
 
@@ -394,6 +413,7 @@
     std::unique_ptr<SoftGateKeeperDevice> soft_device;
 
     bool clear_state_if_needed_done;
+    bool is_running_gsi;
 };
 }// namespace android
 
diff --git a/healthd/Android.bp b/healthd/Android.bp
index 6b00f81..2cf6be9 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -84,3 +84,29 @@
         "manifest_healthd.xml"
     ],
 }
+
+cc_library_static {
+    name: "libhealthd_charger_nops",
+
+    srcs: [
+        "healthd_mode_charger_nops.cpp",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    header_libs: [
+        "libhealthd_headers",
+    ],
+
+    static_libs: [
+        "android.hardware.health@2.0-impl",
+    ],
+
+    shared_libs: [
+        "android.hardware.health@2.0",
+        "libutils",
+    ],
+}
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 823ed06..d18f15a 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -2,14 +2,14 @@
 
 LOCAL_PATH := $(call my-dir)
 
+### libhealthd_draw ###
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := libhealthd_draw
 
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
-LOCAL_STATIC_LIBRARIES := \
-	libminui \
-	libbase
+LOCAL_STATIC_LIBRARIES := libminui
+LOCAL_SHARED_LIBRARIES := libbase
 LOCAL_SRC_FILES := healthd_draw.cpp
 
 ifneq ($(TARGET_HEALTHD_DRAW_SPLIT_SCREEN),)
@@ -28,6 +28,7 @@
 
 include $(BUILD_STATIC_LIBRARY)
 
+### libhealthd_charger ###
 include $(CLEAR_VARS)
 
 LOCAL_CFLAGS := -Werror
@@ -49,24 +50,22 @@
     $(LOCAL_PATH)/include
 
 LOCAL_STATIC_LIBRARIES := \
-    android.hardware.health@2.0 \
     android.hardware.health@2.0-impl \
-    android.hardware.health@1.0 \
     android.hardware.health@1.0-convert \
     libhealthstoragedefault \
+    libhealthd_draw \
     libminui \
-    libpng \
-    libz \
-    libutils \
+
+LOCAL_SHARED_LIBRARIES := \
+    android.hardware.health@2.0 \
     libbase \
     libcutils \
-    libhealthd_draw \
     liblog \
-    libm \
-    libc \
+    libpng \
+    libutils \
 
 ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
-LOCAL_STATIC_LIBRARIES += libsuspend
+LOCAL_SHARED_LIBRARIES += libsuspend
 endif
 
 include $(BUILD_STATIC_LIBRARY)
@@ -81,10 +80,6 @@
     charger.cpp \
 
 LOCAL_MODULE := charger
-LOCAL_MODULE_TAGS := optional
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
-LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 
 LOCAL_CFLAGS := -Werror
@@ -94,8 +89,6 @@
 
 CHARGER_STATIC_LIBRARIES := \
     android.hardware.health@2.0-impl \
-    android.hardware.health@2.0 \
-    android.hardware.health@1.0 \
     android.hardware.health@1.0-convert \
     libbinderthreadstate \
     libhidltransport \
@@ -104,54 +97,97 @@
     libhealthstoragedefault \
     libvndksupport \
     libhealthd_charger \
+    libhealthd_charger_nops \
     libhealthd_draw \
     libbatterymonitor \
+
+CHARGER_SHARED_LIBRARIES := \
+    android.hardware.health@2.0 \
     libbase \
-    libutils \
     libcutils \
     libjsoncpp \
     libprocessgroup \
     liblog \
-    libm \
-    libc \
-
-LOCAL_STATIC_LIBRARIES := $(CHARGER_STATIC_LIBRARIES)
+    libutils \
 
 ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
-LOCAL_STATIC_LIBRARIES += \
-    libminui \
-    libpng \
-    libz \
-
+CHARGER_STATIC_LIBRARIES += libminui
+CHARGER_SHARED_LIBRARIES += libpng
 endif
 
 ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
-LOCAL_STATIC_LIBRARIES += libsuspend
+CHARGER_SHARED_LIBRARIES += libsuspend
 endif
 
+LOCAL_STATIC_LIBRARIES := $(CHARGER_STATIC_LIBRARIES)
+LOCAL_SHARED_LIBRARIES := $(CHARGER_SHARED_LIBRARIES)
+
 LOCAL_HAL_STATIC_LIBRARIES := libhealthd
 
-# Symlink /charger to /sbin/charger
+# Symlink /charger to /system/bin/charger
 LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT) \
-    && ln -sf /sbin/charger $(TARGET_ROOT_OUT)/charger
+    && ln -sf /system/bin/charger $(TARGET_ROOT_OUT)/charger
 
 include $(BUILD_EXECUTABLE)
 
+### charger.recovery ###
 include $(CLEAR_VARS)
-LOCAL_MODULE := charger_test
-LOCAL_MODULE_TAGS := optional
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Wall -Werror -DCHARGER_TEST -DCHARGER_NO_UI
-LOCAL_STATIC_LIBRARIES := $(CHARGER_STATIC_LIBRARIES)
+
 LOCAL_SRC_FILES := \
     charger.cpp \
+
+LOCAL_MODULE := charger.recovery
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin
+LOCAL_MODULE_STEM := charger
+
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_CFLAGS := -Wall -Werror
+LOCAL_CFLAGS += -DCHARGER_NO_UI
+
+# charger.recovery doesn't link against libhealthd_{charger,draw} or libminui, since it doesn't need
+# any UI support.
+LOCAL_STATIC_LIBRARIES := \
+    android.hardware.health@2.0-impl \
+    android.hardware.health@1.0-convert \
+    libbinderthreadstate \
+    libhidltransport \
+    libhidlbase \
+    libhwbinder_noltopgo \
+    libhealthstoragedefault \
+    libvndksupport \
+    libhealthd_charger_nops \
+    libbatterymonitor \
+
+# These shared libs will be installed to recovery image because of the dependency in `recovery`
+# module.
+LOCAL_SHARED_LIBRARIES := \
+    android.hardware.health@2.0 \
+    libbase \
+    libcutils \
+    liblog \
+    libutils \
+
+# The use of LOCAL_HAL_STATIC_LIBRARIES prevents from building this module with Android.bp.
+LOCAL_HAL_STATIC_LIBRARIES := libhealthd
+
+include $(BUILD_EXECUTABLE)
+
+### charger_test ###
+include $(CLEAR_VARS)
+LOCAL_MODULE := charger_test
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_CFLAGS := -Wall -Werror -DCHARGER_NO_UI
+LOCAL_STATIC_LIBRARIES := $(CHARGER_STATIC_LIBRARIES)
+LOCAL_SHARED_LIBRARIES := $(CHARGER_SHARED_LIBRARIES)
+LOCAL_SRC_FILES := \
     charger_test.cpp \
 
 include $(BUILD_EXECUTABLE)
 
 CHARGER_STATIC_LIBRARIES :=
+CHARGER_SHARED_LIBRARIES :=
 
+### charger_res_images ###
 ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
 define _add-charger-image
 include $$(CLEAR_VARS)
diff --git a/healthd/charger.cpp b/healthd/charger.cpp
index 43e7fd5..085cceb 100644
--- a/healthd/charger.cpp
+++ b/healthd/charger.cpp
@@ -14,98 +14,13 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "charger"
-#define KLOG_LEVEL 6
+#include "healthd_mode_charger.h"
+#include "healthd_mode_charger_nops.h"
 
-#include <health2/Health.h>
-#include <healthd/healthd.h>
-
-#include <stdlib.h>
-#include <string.h>
-#include <cutils/klog.h>
-
-using namespace android;
-
-// main healthd loop
-extern int healthd_main(void);
-
-// Charger mode
-
-extern void healthd_mode_charger_init(struct healthd_config *config);
-extern int healthd_mode_charger_preparetowait(void);
-extern void healthd_mode_charger_heartbeat(void);
-extern void healthd_mode_charger_battery_update(
-    struct android::BatteryProperties *props);
-
-// NOPs for modes that need no special action
-
-static void healthd_mode_nop_init(struct healthd_config *config);
-static int healthd_mode_nop_preparetowait(void);
-static void healthd_mode_nop_heartbeat(void);
-static void healthd_mode_nop_battery_update(
-    struct android::BatteryProperties *props);
-
-static struct healthd_mode_ops healthd_nops = {
-    .init = healthd_mode_nop_init,
-    .preparetowait = healthd_mode_nop_preparetowait,
-    .heartbeat = healthd_mode_nop_heartbeat,
-    .battery_update = healthd_mode_nop_battery_update,
-};
-
-#ifdef CHARGER_NO_UI
-static struct healthd_mode_ops charger_ops = healthd_nops;
-#else
-static struct healthd_mode_ops charger_ops = {
-    .init = healthd_mode_charger_init,
-    .preparetowait = healthd_mode_charger_preparetowait,
-    .heartbeat = healthd_mode_charger_heartbeat,
-    .battery_update = healthd_mode_charger_battery_update,
-};
-#endif
-
-static void healthd_mode_nop_init(struct healthd_config* config) {
-    using android::hardware::health::V2_0::implementation::Health;
-    Health::initInstance(config);
-}
-
-static int healthd_mode_nop_preparetowait(void) {
-    return -1;
-}
-
-static void healthd_mode_nop_heartbeat(void) {
-}
-
-static void healthd_mode_nop_battery_update(
-    struct android::BatteryProperties* /*props*/) {
-}
-
-int healthd_charger_main(int argc, char** argv) {
-    int ch;
-
-    healthd_mode_ops = &charger_ops;
-
-    while ((ch = getopt(argc, argv, "cr")) != -1) {
-        switch (ch) {
-            case 'c':
-                // -c is now a noop
-                break;
-            case 'r':
-                // force nops for recovery
-                healthd_mode_ops = &healthd_nops;
-                break;
-            case '?':
-            default:
-                KLOG_ERROR(LOG_TAG, "Unrecognized charger option: %c\n",
-                        optopt);
-                exit(1);
-        }
-    }
-
-    return healthd_main();
-}
-
-#ifndef CHARGER_TEST
 int main(int argc, char** argv) {
+#ifdef CHARGER_NO_UI
+    return healthd_charger_nops(argc, argv);
+#else
     return healthd_charger_main(argc, argv);
-}
 #endif
+}
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 8f2f727..5fe58ac 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -54,6 +54,9 @@
 
 using namespace android;
 
+// main healthd loop
+extern int healthd_main(void);
+
 char* locale;
 
 #ifndef max
@@ -711,3 +714,33 @@
     healthd_config = config;
     charger->boot_min_cap = config->boot_min_cap;
 }
+
+static struct healthd_mode_ops charger_ops = {
+        .init = healthd_mode_charger_init,
+        .preparetowait = healthd_mode_charger_preparetowait,
+        .heartbeat = healthd_mode_charger_heartbeat,
+        .battery_update = healthd_mode_charger_battery_update,
+};
+
+int healthd_charger_main(int argc, char** argv) {
+    int ch;
+
+    healthd_mode_ops = &charger_ops;
+
+    while ((ch = getopt(argc, argv, "cr")) != -1) {
+        switch (ch) {
+            case 'c':
+                // -c is now a noop
+                break;
+            case 'r':
+                // -r is now a noop
+                break;
+            case '?':
+            default:
+                LOGE("Unrecognized charger option: %c\n", optopt);
+                exit(1);
+        }
+    }
+
+    return healthd_main();
+}
diff --git a/healthd/healthd_mode_charger.h b/healthd/healthd_mode_charger.h
new file mode 100644
index 0000000..2f0c9f2
--- /dev/null
+++ b/healthd/healthd_mode_charger.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+int healthd_charger_main(int argc, char** argv);
diff --git a/healthd/healthd_mode_charger_nops.cpp b/healthd/healthd_mode_charger_nops.cpp
new file mode 100644
index 0000000..bcc04d5
--- /dev/null
+++ b/healthd/healthd_mode_charger_nops.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 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 "healthd_mode_charger_nops.h"
+
+#include <health2/Health.h>
+#include <healthd/healthd.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+using namespace android;
+
+// main healthd loop
+extern int healthd_main(void);
+
+// NOPs for modes that need no special action
+
+static void healthd_mode_nop_init(struct healthd_config* config);
+static int healthd_mode_nop_preparetowait(void);
+static void healthd_mode_nop_heartbeat(void);
+static void healthd_mode_nop_battery_update(struct android::BatteryProperties* props);
+
+static struct healthd_mode_ops healthd_nops = {
+        .init = healthd_mode_nop_init,
+        .preparetowait = healthd_mode_nop_preparetowait,
+        .heartbeat = healthd_mode_nop_heartbeat,
+        .battery_update = healthd_mode_nop_battery_update,
+};
+
+static void healthd_mode_nop_init(struct healthd_config* config) {
+    using android::hardware::health::V2_0::implementation::Health;
+    Health::initInstance(config);
+}
+
+static int healthd_mode_nop_preparetowait(void) {
+    return -1;
+}
+
+static void healthd_mode_nop_heartbeat(void) {}
+
+static void healthd_mode_nop_battery_update(struct android::BatteryProperties* /*props*/) {}
+
+int healthd_charger_nops(int /* argc */, char** /* argv */) {
+    healthd_mode_ops = &healthd_nops;
+    return healthd_main();
+}
diff --git a/healthd/healthd_mode_charger_nops.h b/healthd/healthd_mode_charger_nops.h
new file mode 100644
index 0000000..a37b247
--- /dev/null
+++ b/healthd/healthd_mode_charger_nops.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+int healthd_charger_nops(int argc, char** argv);
diff --git a/init/Android.bp b/init/Android.bp
index 639d8d1..8292aa0 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -91,7 +91,7 @@
 cc_library_static {
     name: "libinit",
     recovery_available: true,
-    defaults: ["init_defaults"],
+    defaults: ["init_defaults", "selinux_policy_version"],
     srcs: [
         "action.cpp",
         "action_manager.cpp",
@@ -110,6 +110,7 @@
         "init.cpp",
         "keychords.cpp",
         "modalias_handler.cpp",
+        "mount_handler.cpp",
         "mount_namespace.cpp",
         "parser.cpp",
         "persistent_properties.cpp",
@@ -132,7 +133,7 @@
         "ueventd_parser.cpp",
         "util.cpp",
     ],
-    whole_static_libs: ["libcap"],
+    whole_static_libs: ["libcap", "com.android.sysprop.apex"],
     header_libs: ["bootimg_headers"],
     proto: {
         type: "lite",
diff --git a/init/Android.mk b/init/Android.mk
index 59d7f11..39af0e6 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -2,6 +2,8 @@
 
 LOCAL_PATH:= $(call my-dir)
 
+-include system/sepolicy/policy_version.mk
+
 # --
 
 ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
@@ -28,7 +30,8 @@
     -DSHUTDOWN_ZERO_TIMEOUT=0
 endif
 
-init_options += -DLOG_UEVENTS=0
+init_options += -DLOG_UEVENTS=0 \
+    -DSEPOLICY_VERSION=$(POLICYVERS)
 
 init_cflags += \
     $(init_options) \
@@ -63,8 +66,9 @@
 LOCAL_UNSTRIPPED_PATH := $(TARGET_RAMDISK_OUT_UNSTRIPPED)
 
 # Set up the same mount points on the ramdisk that system-as-root contains.
-LOCAL_POST_INSTALL_CMD := \
-    mkdir -p $(TARGET_RAMDISK_OUT)/dev \
+LOCAL_POST_INSTALL_CMD := mkdir -p \
+    $(TARGET_RAMDISK_OUT)/apex \
+    $(TARGET_RAMDISK_OUT)/dev \
     $(TARGET_RAMDISK_OUT)/mnt \
     $(TARGET_RAMDISK_OUT)/proc \
     $(TARGET_RAMDISK_OUT)/sys \
@@ -93,6 +97,7 @@
     libselinux \
     libcap \
     libgsi \
+    libcom.android.sysprop.apex \
 
 LOCAL_SANITIZE := signed-integer-overflow
 # First stage init is weird: it may start without stdout/stderr, and no /proc.
diff --git a/init/README.md b/init/README.md
index f0e5d55..b2039b4 100644
--- a/init/README.md
+++ b/init/README.md
@@ -660,12 +660,19 @@
 
 Properties
 ----------
-Init provides information about the services that it is responsible
-for via the below properties.
+Init provides state information with the following properties.
 
 `init.svc.<name>`
 > State of a named service ("stopped", "stopping", "running", "restarting")
 
+`dev.mnt.blk.<mount_point>`
+> Block device base name associated with a *mount_point*.
+  The *mount_point* has / replaced by . and if referencing the root mount point
+  "/", it will use "/root", specifically `dev.mnt.blk.root`.
+  Meant for references to `/sys/device/block/${dev.mnt.blk.<mount_point>}/` and
+  `/sys/fs/ext4/${dev.mnt.blk.<mount_point>}/` to tune the block device
+  characteristics in a device agnostic manner.
+
 
 Boot timing
 -----------
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 538ed00..8437e37 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -1118,11 +1118,11 @@
     }
 }
 
-static Result<Success> do_setup_runtime_bionic(const BuiltinArguments& args) {
+static Result<Success> do_enter_default_mount_ns(const BuiltinArguments& args) {
     if (SwitchToDefaultMountNamespace()) {
         return Success();
     } else {
-        return Error() << "Failed to setup runtime bionic";
+        return Error() << "Failed to enter into default mount namespace";
     }
 }
 
@@ -1173,10 +1173,10 @@
         {"rmdir",                   {1,     1,    {true,   do_rmdir}}},
         {"setprop",                 {2,     2,    {true,   do_setprop}}},
         {"setrlimit",               {3,     3,    {false,  do_setrlimit}}},
-        {"setup_runtime_bionic",    {0,     0,    {false,  do_setup_runtime_bionic}}},
         {"start",                   {1,     1,    {false,  do_start}}},
         {"stop",                    {1,     1,    {false,  do_stop}}},
         {"swapon_all",              {1,     1,    {false,  do_swapon_all}}},
+        {"enter_default_mount_ns",  {0,     0,    {false,  do_enter_default_mount_ns}}},
         {"symlink",                 {2,     2,    {true,   do_symlink}}},
         {"sysclktz",                {1,     1,    {false,  do_sysclktz}}},
         {"trigger",                 {1,     1,    {false,  do_trigger}}},
diff --git a/init/epoll.cpp b/init/epoll.cpp
index 4bca09e..94dd553 100644
--- a/init/epoll.cpp
+++ b/init/epoll.cpp
@@ -16,6 +16,7 @@
 
 #include "epoll.h"
 
+#include <stdint.h>
 #include <sys/epoll.h>
 
 #include <chrono>
@@ -37,13 +38,16 @@
     return Success();
 }
 
-Result<Success> Epoll::RegisterHandler(int fd, std::function<void()> handler) {
+Result<Success> Epoll::RegisterHandler(int fd, std::function<void()> handler, uint32_t events) {
+    if (!events) {
+        return Error() << "Must specify events";
+    }
     auto [it, inserted] = epoll_handlers_.emplace(fd, std::move(handler));
     if (!inserted) {
         return Error() << "Cannot specify two epoll handlers for a given FD";
     }
     epoll_event ev;
-    ev.events = EPOLLIN;
+    ev.events = events;
     // std::map's iterators do not get invalidated until erased, so we use the
     // pointer to the std::function in the map directly for epoll_ctl.
     ev.data.ptr = reinterpret_cast<void*>(&it->second);
diff --git a/init/epoll.h b/init/epoll.h
index 85a791c..9789bef 100644
--- a/init/epoll.h
+++ b/init/epoll.h
@@ -17,6 +17,9 @@
 #ifndef _INIT_EPOLL_H
 #define _INIT_EPOLL_H
 
+#include <stdint.h>
+#include <sys/epoll.h>
+
 #include <chrono>
 #include <functional>
 #include <map>
@@ -34,7 +37,8 @@
     Epoll();
 
     Result<Success> Open();
-    Result<Success> RegisterHandler(int fd, std::function<void()> handler);
+    Result<Success> RegisterHandler(int fd, std::function<void()> handler,
+                                    uint32_t events = EPOLLIN);
     Result<Success> UnregisterHandler(int fd);
     Result<Success> Wait(std::optional<std::chrono::milliseconds> timeout);
 
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index e11d897..c566676 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -155,6 +155,10 @@
     // part of the product partition, e.g. because they are mounted read-write.
     CHECKCALL(mkdir("/mnt/product", 0755));
 
+    // /apex is used to mount APEXes
+    CHECKCALL(mount("tmpfs", "/apex", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
+                    "mode=0755,uid=0,gid=0"));
+
 #undef CHECKCALL
 
     // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
@@ -195,6 +199,12 @@
         SwitchRoot("/first_stage_ramdisk");
     }
 
+    // If this file is present, the second-stage init will use a userdebug sepolicy
+    // and load adb_debug.prop to allow adb root, if the device is unlocked.
+    if (access("/force_debuggable", F_OK) == 0) {
+        setenv("INIT_FORCE_DEBUGGABLE", "true", 1);
+    }
+
     if (!DoFirstStageMount()) {
         LOG(FATAL) << "Failed to mount required partitions early ...";
     }
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 898e28e..4b0f05d 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -189,6 +189,29 @@
     return true;
 }
 
+static bool IsStandaloneImageRollback(const AvbHandle& builtin_vbmeta,
+                                      const AvbHandle& standalone_vbmeta,
+                                      const FstabEntry& fstab_entry) {
+    std::string old_spl = builtin_vbmeta.GetSecurityPatchLevel(fstab_entry);
+    std::string new_spl = standalone_vbmeta.GetSecurityPatchLevel(fstab_entry);
+
+    bool rollbacked = false;
+    if (old_spl.empty() || new_spl.empty() || new_spl < old_spl) {
+        rollbacked = true;
+    }
+
+    if (rollbacked) {
+        LOG(ERROR) << "Image rollback detected for " << fstab_entry.mount_point
+                   << ", SPL switches from '" << old_spl << "' to '" << new_spl << "'";
+        if (AvbHandle::IsDeviceUnlocked()) {
+            LOG(INFO) << "Allowing rollbacked standalone image when the device is unlocked";
+            return false;
+        }
+    }
+
+    return rollbacked;
+}
+
 // Class Definitions
 // -----------------
 FirstStageMount::FirstStageMount(Fstab fstab)
@@ -557,7 +580,14 @@
             required_devices_partition_names_.emplace(basename(device.c_str()));
             auto uevent_callback = [this](const Uevent& uevent) { return UeventCallback(uevent); };
             uevent_listener_.RegenerateUevents(uevent_callback);
-            uevent_listener_.Poll(uevent_callback, 10s);
+            if (!required_devices_partition_names_.empty()) {
+                uevent_listener_.Poll(uevent_callback, 10s);
+                if (!required_devices_partition_names_.empty()) {
+                    LOG(ERROR) << __PRETTY_FUNCTION__
+                               << ": partition(s) not found after polling timeout: "
+                               << android::base::Join(required_devices_partition_names_, ", ");
+                }
+            }
         } else {
             InitMappedDevice(device);
         }
@@ -737,7 +767,7 @@
         if (!InitAvbHandle()) return false;
         hashtree_result =
                 avb_handle_->SetUpAvbHashtree(fstab_entry, false /* wait_for_verity_dev */);
-    } else if (!fstab_entry->avb_key.empty()) {
+    } else if (!fstab_entry->avb_keys.empty()) {
         if (!InitAvbHandle()) return false;
         // Checks if hashtree should be disabled from the top-level /vbmeta.
         if (avb_handle_->status() == AvbHandleStatus::kHashtreeDisabled ||
@@ -746,7 +776,15 @@
                        << fstab_entry->mount_point;
             return true;  // Returns true to mount the partition directly.
         } else {
-            hashtree_result = AvbHandle::SetUpStandaloneAvbHashtree(
+            auto avb_standalone_handle = AvbHandle::LoadAndVerifyVbmeta(*fstab_entry);
+            if (!avb_standalone_handle) {
+                LOG(ERROR) << "Failed to load offline vbmeta for " << fstab_entry->mount_point;
+                return false;
+            }
+            if (IsStandaloneImageRollback(*avb_handle_, *avb_standalone_handle, *fstab_entry)) {
+                return false;
+            }
+            hashtree_result = avb_standalone_handle->SetUpAvbHashtree(
                     fstab_entry, false /* wait_for_verity_dev */);
         }
     } else {
diff --git a/init/init.cpp b/init/init.cpp
index a8924f2..a5f4549 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -39,9 +39,11 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <cutils/android_reboot.h>
+#include <fs_avb/fs_avb.h>
 #include <fs_mgr_vendor_overlay.h>
 #include <keyutils.h>
 #include <libavb/libavb.h>
+#include <libgsi/libgsi.h>
 #include <processgroup/processgroup.h>
 #include <selinux/android.h>
 
@@ -55,6 +57,7 @@
 #include "first_stage_mount.h"
 #include "import_parser.h"
 #include "keychords.h"
+#include "mount_handler.h"
 #include "mount_namespace.h"
 #include "property_service.h"
 #include "reboot.h"
@@ -73,6 +76,7 @@
 using android::base::StringPrintf;
 using android::base::Timer;
 using android::base::Trim;
+using android::fs_mgr::AvbHandle;
 
 namespace android {
 namespace init {
@@ -91,6 +95,7 @@
 static bool shutting_down;
 static std::string shutdown_command;
 static bool do_shutdown = false;
+static bool load_debug_prop = false;
 
 std::vector<std::string> late_import_paths;
 
@@ -654,10 +659,17 @@
     const char* avb_version = getenv("INIT_AVB_VERSION");
     if (avb_version) property_set("ro.boot.avb_version", avb_version);
 
+    // See if need to load debug props to allow adb root, when the device is unlocked.
+    const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");
+    if (force_debuggable_env && AvbHandle::IsDeviceUnlocked()) {
+        load_debug_prop = "true"s == force_debuggable_env;
+    }
+
     // Clean up our environment.
     unsetenv("INIT_STARTED_AT");
     unsetenv("INIT_SELINUX_TOOK");
     unsetenv("INIT_AVB_VERSION");
+    unsetenv("INIT_FORCE_DEBUGGABLE");
 
     // Now set up SELinux for second stage.
     SelinuxSetupKernelLogging();
@@ -671,10 +683,11 @@
 
     InstallSignalFdHandler(&epoll);
 
-    property_load_boot_defaults();
+    property_load_boot_defaults(load_debug_prop);
     fs_mgr_vendor_overlay_mount_all();
     export_oem_lock_status();
     StartPropertyService(&epoll);
+    MountHandler mount_handler(&epoll);
     set_usb_controller();
 
     const BuiltinFunctionMap function_map;
@@ -695,6 +708,13 @@
     // Nexus 9 boot time, so it's disabled by default.
     if (false) DumpState();
 
+    // Make the GSI status available before scripts start running.
+    if (android::gsi::IsGsiRunning()) {
+        property_set("ro.gsid.image_running", "1");
+    } else {
+        property_set("ro.gsid.image_running", "0");
+    }
+
     am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
 
     am.QueueEventTrigger("early-init");
diff --git a/init/mount_handler.cpp b/init/mount_handler.cpp
new file mode 100644
index 0000000..12dfc6d
--- /dev/null
+++ b/init/mount_handler.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2019 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 "mount_handler.h"
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <string>
+#include <utility>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <fs_mgr.h>
+#include <fstab/fstab.h>
+
+#include "epoll.h"
+#include "property_service.h"
+
+namespace android {
+namespace init {
+
+namespace {
+
+MountHandlerEntry ParseMount(const std::string& line) {
+    auto fields = android::base::Split(line, " ");
+    while (fields.size() < 3) fields.emplace_back("");
+    if (fields[0] == "/dev/root") {
+        if (android::fs_mgr::Fstab fstab; android::fs_mgr::ReadDefaultFstab(&fstab)) {
+            if (auto entry = GetEntryForMountPoint(&fstab, "/")) {
+                fields[0] = entry->blk_device;
+            }
+        }
+    }
+    if (android::base::StartsWith(fields[0], "/dev/")) {
+        if (std::string link; android::base::Readlink(fields[0], &link)) {
+            fields[0] = link;
+        }
+    }
+    return MountHandlerEntry(fields[0], fields[1], fields[2]);
+}
+
+void SetMountProperty(const MountHandlerEntry& entry, bool add) {
+    static constexpr char devblock[] = "/dev/block/";
+    if (!android::base::StartsWith(entry.blk_device, devblock)) return;
+    std::string value;
+    if (add) {
+        value = entry.blk_device.substr(strlen(devblock));
+        if (android::base::StartsWith(value, "sd")) {
+            // All sd partitions inherit their queue characteristics
+            // from the whole device reference.  Strip partition number.
+            auto it = std::find_if(value.begin(), value.end(), [](char c) { return isdigit(c); });
+            if (it != value.end()) value.erase(it, value.end());
+        }
+        auto queue = "/sys/block/" + value + "/queue";
+        struct stat sb;
+        if (stat(queue.c_str(), &sb) || !S_ISDIR(sb.st_mode)) value = "";
+        if (stat(entry.mount_point.c_str(), &sb) || !S_ISDIR(sb.st_mode)) value = "";
+        // Skip the noise associated with APEX until there is a need
+        if (android::base::StartsWith(value, "loop")) value = "";
+    }
+    std::string property =
+            "dev.mnt.blk" + ((entry.mount_point == "/") ? "/root" : entry.mount_point);
+    std::replace(property.begin(), property.end(), '/', '.');
+    if (value.empty() && android::base::GetProperty(property, "").empty()) return;
+    property_set(property, value);
+}
+
+}  // namespace
+
+MountHandlerEntry::MountHandlerEntry(const std::string& blk_device, const std::string& mount_point,
+                                     const std::string& fs_type)
+    : blk_device(blk_device), mount_point(mount_point), fs_type(fs_type) {}
+
+bool MountHandlerEntry::operator<(const MountHandlerEntry& r) const {
+    if (blk_device < r.blk_device) return true;
+    if (blk_device > r.blk_device) return false;
+    if (mount_point < r.mount_point) return true;
+    if (mount_point > r.mount_point) return false;
+    return fs_type < r.fs_type;
+}
+
+MountHandler::MountHandler(Epoll* epoll) : epoll_(epoll), fp_(fopen("/proc/mounts", "re"), fclose) {
+    if (!fp_) PLOG(FATAL) << "Could not open /proc/mounts";
+    auto result = epoll->RegisterHandler(
+            fileno(fp_.get()), [this]() { this->MountHandlerFunction(); }, EPOLLERR | EPOLLPRI);
+    if (!result) LOG(FATAL) << result.error();
+}
+
+MountHandler::~MountHandler() {
+    if (fp_) epoll_->UnregisterHandler(fileno(fp_.get())).IgnoreError();
+}
+
+void MountHandler::MountHandlerFunction() {
+    rewind(fp_.get());
+    char* buf = nullptr;
+    size_t len = 0;
+    auto untouched = mounts_;
+    while (getline(&buf, &len, fp_.get()) != -1) {
+        auto entry = ParseMount(std::string(buf, len));
+        auto match = untouched.find(entry);
+        if (match == untouched.end()) {
+            SetMountProperty(entry, true);
+            mounts_.emplace(std::move(entry));
+        } else {
+            untouched.erase(match);
+        }
+    }
+    free(buf);
+    for (auto entry : untouched) {
+        auto match = mounts_.find(entry);
+        if (match == mounts_.end()) continue;
+        mounts_.erase(match);
+        SetMountProperty(entry, false);
+    }
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/mount_handler.h b/init/mount_handler.h
new file mode 100644
index 0000000..e524a74
--- /dev/null
+++ b/init/mount_handler.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdio.h>
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include "epoll.h"
+
+namespace android {
+namespace init {
+
+struct MountHandlerEntry {
+    MountHandlerEntry(const std::string& blk_device, const std::string& mount_point,
+                      const std::string& fs_type);
+
+    bool operator<(const MountHandlerEntry& r) const;
+
+    const std::string blk_device;
+    const std::string mount_point;
+    const std::string fs_type;
+};
+
+class MountHandler {
+  public:
+    explicit MountHandler(Epoll* epoll);
+    MountHandler(const MountHandler&) = delete;
+    MountHandler(MountHandler&&) = delete;
+    MountHandler& operator=(const MountHandler&) = delete;
+    MountHandler& operator=(MountHandler&&) = delete;
+    ~MountHandler();
+
+  private:
+    void MountHandlerFunction();
+
+    Epoll* epoll_;
+    std::unique_ptr<FILE, decltype(&fclose)> fp_;
+    std::set<MountHandlerEntry> mounts_;
+};
+
+}  // namespace init
+}  // namespace android
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index 413fe8f..5305dc7 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -21,6 +21,7 @@
 #include <string>
 #include <vector>
 
+#include <ApexProperties.sysprop.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
@@ -32,37 +33,6 @@
 namespace init {
 namespace {
 
-static constexpr const char* kLinkerMountPoint = "/bionic/bin/linker";
-static constexpr const char* kBootstrapLinkerPath = "/system/bin/bootstrap/linker";
-static constexpr const char* kRuntimeLinkerPath = "/apex/com.android.runtime/bin/linker";
-
-static constexpr const char* kBionicLibsMountPointDir = "/bionic/lib/";
-static constexpr const char* kBootstrapBionicLibsDir = "/system/lib/bootstrap/";
-static constexpr const char* kRuntimeBionicLibsDir = "/apex/com.android.runtime/lib/bionic/";
-
-static constexpr const char* kLinkerMountPoint64 = "/bionic/bin/linker64";
-static constexpr const char* kBootstrapLinkerPath64 = "/system/bin/bootstrap/linker64";
-static constexpr const char* kRuntimeLinkerPath64 = "/apex/com.android.runtime/bin/linker64";
-
-static constexpr const char* kBionicLibsMountPointDir64 = "/bionic/lib64/";
-static constexpr const char* kBootstrapBionicLibsDir64 = "/system/lib64/bootstrap/";
-static constexpr const char* kRuntimeBionicLibsDir64 = "/apex/com.android.runtime/lib64/bionic/";
-
-static const std::vector<std::string> kBionicLibFileNames = {"libc.so", "libm.so", "libdl.so"};
-
-static bool BindMount(const std::string& source, const std::string& mount_point,
-                      bool recursive = false) {
-    unsigned long mountflags = MS_BIND;
-    if (recursive) {
-        mountflags |= MS_REC;
-    }
-    if (mount(source.c_str(), mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
-        PLOG(ERROR) << "Could not bind-mount " << source << " to " << mount_point;
-        return false;
-    }
-    return true;
-}
-
 static bool MakeShared(const std::string& mount_point, bool recursive = false) {
     unsigned long mountflags = MS_SHARED;
     if (recursive) {
@@ -104,37 +74,9 @@
     return ret;
 }
 
-static bool BindMountBionic(const std::string& linker_source, const std::string& lib_dir_source,
-                            const std::string& linker_mount_point,
-                            const std::string& lib_mount_dir) {
-    if (access(linker_source.c_str(), F_OK) != 0) {
-        PLOG(INFO) << linker_source << " does not exist. skipping mounting bionic there.";
-        // This can happen for 64-bit bionic in 32-bit only device.
-        // It is okay to skip mounting the 64-bit bionic.
-        return true;
-    }
-    if (!BindMount(linker_source, linker_mount_point)) {
-        return false;
-    }
-    if (!MakePrivate(linker_mount_point)) {
-        return false;
-    }
-    for (const auto& libname : kBionicLibFileNames) {
-        std::string mount_point = lib_mount_dir + libname;
-        std::string source = lib_dir_source + libname;
-        if (!BindMount(source, mount_point)) {
-            return false;
-        }
-        if (!MakePrivate(mount_point)) {
-            return false;
-        }
-    }
-    return true;
-}
-
-static bool IsBionicUpdatable() {
-    static bool result = android::base::GetBoolProperty("ro.apex.IsBionicUpdatable", false);
-    return result;
+static bool IsApexUpdatable() {
+    static bool updatable = android::sysprop::ApexProperties::updatable().value_or(false);
+    return updatable;
 }
 
 static android::base::unique_fd bootstrap_ns_fd;
@@ -153,36 +95,21 @@
     // point to private.
     if (!MakeShared("/", true /*recursive*/)) return false;
 
-    // Since different files (bootstrap or runtime APEX) should be mounted to
-    // the same mount point paths (e.g. /bionic/bin/linker, /bionic/lib/libc.so,
-    // etc.) across the two mount namespaces, we create a private mount point at
-    // /bionic so that a mount event for the bootstrap bionic in the mount
-    // namespace for pre-apexd processes is not propagated to the other mount
-    // namespace for post-apexd process, and vice versa.
-    //
-    // Other mount points other than /bionic, however, are all still shared.
-    if (!BindMount("/bionic", "/bionic", true /*recursive*/)) return false;
-    if (!MakePrivate("/bionic")) return false;
-
-    // Bind-mount bootstrap bionic.
-    if (!BindMountBionic(kBootstrapLinkerPath, kBootstrapBionicLibsDir, kLinkerMountPoint,
-                         kBionicLibsMountPointDir))
-        return false;
-    if (!BindMountBionic(kBootstrapLinkerPath64, kBootstrapBionicLibsDir64, kLinkerMountPoint64,
-                         kBionicLibsMountPointDir64))
-        return false;
+    // /apex is a private mountpoint to give different sets of APEXes for
+    // the bootstrap and default mount namespaces. The processes running with
+    // the bootstrap namespace get APEXes from the read-only partition.
+    if (!(MakePrivate("/apex"))) return false;
 
     bootstrap_ns_fd.reset(OpenMountNamespace());
     bootstrap_ns_id = GetMountNamespaceId();
 
-    // When bionic is updatable via the runtime APEX, we create separate mount
+    // When APEXes are updatable (e.g. not-flattened), we create separate mount
     // namespaces for processes that are started before and after the APEX is
-    // activated by apexd. In the namespace for pre-apexd processes, the bionic
-    // from the /system partition (that we call bootstrap bionic) is
-    // bind-mounted. In the namespace for post-apexd processes, the bionic from
-    // the runtime APEX is bind-mounted.
+    // activated by apexd. In the namespace for pre-apexd processes, small
+    // number of essential APEXes (e.g. com.android.runtime) are activated.
+    // In the namespace for post-apexd processes, all APEXes are activated.
     bool success = true;
-    if (IsBionicUpdatable() && !IsRecoveryMode()) {
+    if (IsApexUpdatable() && !IsRecoveryMode()) {
         // Creating a new namespace by cloning, saving, and switching back to
         // the original namespace.
         if (unshare(CLONE_NEWNS) == -1) {
@@ -192,15 +119,6 @@
         default_ns_fd.reset(OpenMountNamespace());
         default_ns_id = GetMountNamespaceId();
 
-        // By this unmount, the bootstrap bionic are not mounted in the default
-        // mount namespace.
-        if (umount2("/bionic", MNT_DETACH) == -1) {
-            PLOG(ERROR) << "Cannot unmount /bionic";
-            // Don't return here. We have to switch back to the bootstrap
-            // namespace.
-            success = false;
-        }
-
         if (setns(bootstrap_ns_fd.get(), CLONE_NEWNS) == -1) {
             PLOG(ERROR) << "Cannot switch back to bootstrap mount namespace";
             return false;
@@ -227,17 +145,6 @@
         }
     }
 
-    // Bind-mount bionic from the runtime APEX since it is now available. Note
-    // that in case of IsBionicUpdatable() == false, these mounts are over the
-    // existing existing bind mounts for the bootstrap bionic, which effectively
-    // becomes hidden.
-    if (!BindMountBionic(kRuntimeLinkerPath, kRuntimeBionicLibsDir, kLinkerMountPoint,
-                         kBionicLibsMountPointDir))
-        return false;
-    if (!BindMountBionic(kRuntimeLinkerPath64, kRuntimeBionicLibsDir64, kLinkerMountPoint64,
-                         kBionicLibsMountPointDir64))
-        return false;
-
     LOG(INFO) << "Switched to default mount namespace";
     return true;
 }
@@ -248,7 +155,7 @@
         return true;
     }
     if (bootstrap_ns_id != GetMountNamespaceId() && bootstrap_ns_fd.get() != -1 &&
-        IsBionicUpdatable()) {
+        IsApexUpdatable()) {
         if (setns(bootstrap_ns_fd.get(), CLONE_NEWNS) == -1) {
             PLOG(ERROR) << "Failed to switch to bootstrap mount namespace.";
             return false;
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 46e5e12..fc5538c 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -39,6 +39,7 @@
 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
 #include <sys/_system_properties.h>
 
+#include <map>
 #include <memory>
 #include <queue>
 #include <vector>
@@ -442,8 +443,8 @@
 }
 
 // This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
-uint32_t HandlePropertySet(const std::string& name, const std::string& value,
-                           const std::string& source_context, const ucred& cr, std::string* error) {
+uint32_t CheckPermissions(const std::string& name, const std::string& value,
+                          const std::string& source_context, const ucred& cr, std::string* error) {
     if (!IsLegalPropertyName(name)) {
         *error = "Illegal property name";
         return PROP_ERROR_INVALID_NAME;
@@ -456,7 +457,6 @@
             return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
         }
 
-        HandleControlMessage(name.c_str() + 4, value, cr.pid);
         return PROP_SUCCESS;
     }
 
@@ -475,6 +475,21 @@
         return PROP_ERROR_INVALID_VALUE;
     }
 
+    return PROP_SUCCESS;
+}
+
+// This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
+uint32_t HandlePropertySet(const std::string& name, const std::string& value,
+                           const std::string& source_context, const ucred& cr, std::string* error) {
+    if (auto ret = CheckPermissions(name, value, source_context, cr, error); ret != PROP_SUCCESS) {
+        return ret;
+    }
+
+    if (StartsWith(name, "ctl.")) {
+        HandleControlMessage(name.c_str() + 4, value, cr.pid);
+        return PROP_SUCCESS;
+    }
+
     // sys.powerctl is a special property that is used to make the device reboot.  We want to log
     // any process that sets this property to be able to accurately blame the cause of a shutdown.
     if (name == "sys.powerctl") {
@@ -579,13 +594,15 @@
     }
 }
 
-static bool load_properties_from_file(const char *, const char *);
+static bool load_properties_from_file(const char*, const char*,
+                                      std::map<std::string, std::string>*);
 
 /*
  * Filter is used to decide which properties to load: NULL loads all keys,
  * "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
  */
-static void LoadProperties(char* data, const char* filter, const char* filename) {
+static void LoadProperties(char* data, const char* filter, const char* filename,
+                           std::map<std::string, std::string>* properties) {
     char *key, *value, *eol, *sol, *tmp, *fn;
     size_t flen = 0;
 
@@ -624,7 +641,7 @@
                 while (isspace(*key)) key++;
             }
 
-            load_properties_from_file(fn, key);
+            load_properties_from_file(fn, key, properties);
 
         } else {
             value = strchr(key, '=');
@@ -651,12 +668,19 @@
                 continue;
             }
 
-            uint32_t result = 0;
             ucred cr = {.pid = 1, .uid = 0, .gid = 0};
             std::string error;
-            result = HandlePropertySet(key, value, context, cr, &error);
-            if (result != PROP_SUCCESS) {
-                LOG(ERROR) << "Unable to set property '" << key << "' to '" << value
+            if (CheckPermissions(key, value, context, cr, &error) == PROP_SUCCESS) {
+                auto it = properties->find(key);
+                if (it == properties->end()) {
+                    (*properties)[key] = value;
+                } else if (it->second != value) {
+                    LOG(WARNING) << "Overriding previous 'ro.' property '" << key << "':'"
+                                 << it->second << "' with new value '" << value << "'";
+                    it->second = value;
+                }
+            } else {
+                LOG(ERROR) << "Do not have permissions to set '" << key << "' to '" << value
                            << "' in property file '" << filename << "': " << error;
             }
         }
@@ -665,7 +689,8 @@
 
 // Filter is used to decide which properties to load: NULL loads all keys,
 // "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
-static bool load_properties_from_file(const char* filename, const char* filter) {
+static bool load_properties_from_file(const char* filename, const char* filter,
+                                      std::map<std::string, std::string>* properties) {
     Timer t;
     auto file_contents = ReadFile(filename);
     if (!file_contents) {
@@ -675,7 +700,7 @@
     }
     file_contents->push_back('\n');
 
-    LoadProperties(file_contents->data(), filter, filename);
+    LoadProperties(file_contents->data(), filter, filename, properties);
     LOG(VERBOSE) << "(Loading properties from " << filename << " took " << t << ".)";
     return true;
 }
@@ -698,7 +723,15 @@
 
 static void load_override_properties() {
     if (ALLOW_LOCAL_PROP_OVERRIDE) {
-        load_properties_from_file("/data/local.prop", NULL);
+        std::map<std::string, std::string> properties;
+        load_properties_from_file("/data/local.prop", nullptr, &properties);
+        for (const auto& [name, value] : properties) {
+            std::string error;
+            if (PropertySet(name, value, &error) != PROP_SUCCESS) {
+                LOG(ERROR) << "Could not set '" << name << "' to '" << value
+                           << "' in /data/local.prop: " << error;
+            }
+        }
     }
 }
 
@@ -833,26 +866,41 @@
     }
 }
 
-void property_load_boot_defaults() {
+void property_load_boot_defaults(bool load_debug_prop) {
     // TODO(b/117892318): merge prop.default and build.prop files into one
-    // TODO(b/122864654): read the prop files from all partitions and then
-    // resolve the duplication by their origin so that RO and non-RO properties
-    // have a consistent overriding order.
-    if (!load_properties_from_file("/system/etc/prop.default", NULL)) {
+    // We read the properties and their values into a map, in order to always allow properties
+    // loaded in the later property files to override the properties in loaded in the earlier
+    // property files, regardless of if they are "ro." properties or not.
+    std::map<std::string, std::string> properties;
+    if (!load_properties_from_file("/system/etc/prop.default", nullptr, &properties)) {
         // Try recovery path
-        if (!load_properties_from_file("/prop.default", NULL)) {
+        if (!load_properties_from_file("/prop.default", nullptr, &properties)) {
             // Try legacy path
-            load_properties_from_file("/default.prop", NULL);
+            load_properties_from_file("/default.prop", nullptr, &properties);
         }
     }
-    load_properties_from_file("/product/build.prop", NULL);
-    load_properties_from_file("/product_services/build.prop", NULL);
-    load_properties_from_file("/odm/default.prop", NULL);
-    load_properties_from_file("/vendor/default.prop", NULL);
-    load_properties_from_file("/system/build.prop", NULL);
-    load_properties_from_file("/odm/build.prop", NULL);
-    load_properties_from_file("/vendor/build.prop", NULL);
-    load_properties_from_file("/factory/factory.prop", "ro.*");
+    load_properties_from_file("/system/build.prop", nullptr, &properties);
+    load_properties_from_file("/vendor/default.prop", nullptr, &properties);
+    load_properties_from_file("/vendor/build.prop", nullptr, &properties);
+    load_properties_from_file("/odm/default.prop", nullptr, &properties);
+    load_properties_from_file("/odm/build.prop", nullptr, &properties);
+    load_properties_from_file("/product/build.prop", nullptr, &properties);
+    load_properties_from_file("/product_services/build.prop", nullptr, &properties);
+    load_properties_from_file("/factory/factory.prop", "ro.*", &properties);
+
+    if (load_debug_prop) {
+        constexpr static const char kAdbDebugProp[] = "/system/etc/adb_debug.prop";
+        LOG(INFO) << "Loading " << kAdbDebugProp;
+        load_properties_from_file(kAdbDebugProp, nullptr, &properties);
+    }
+
+    for (const auto& [name, value] : properties) {
+        std::string error;
+        if (PropertySet(name, value, &error) != PROP_SUCCESS) {
+            LOG(ERROR) << "Could not set '" << name << "' to '" << value
+                       << "' while loading .prop files" << error;
+        }
+    }
 
     property_initialize_ro_product_props();
     property_derive_build_fingerprint();
@@ -908,6 +956,13 @@
             LoadPropertyInfoFromFile("/vendor/etc/selinux/nonplat_property_contexts",
                                      &property_infos);
         }
+        if (access("/product/etc/selinux/product_property_contexts", R_OK) != -1) {
+            LoadPropertyInfoFromFile("/product/etc/selinux/product_property_contexts",
+                                     &property_infos);
+        }
+        if (access("/odm/etc/selinux/odm_property_contexts", R_OK) != -1) {
+            LoadPropertyInfoFromFile("/odm/etc/selinux/odm_property_contexts", &property_infos);
+        }
     } else {
         if (!LoadPropertyInfoFromFile("/plat_property_contexts", &property_infos)) {
             return;
@@ -916,6 +971,8 @@
             // Fallback to nonplat_* if vendor_* doesn't exist.
             LoadPropertyInfoFromFile("/nonplat_property_contexts", &property_infos);
         }
+        LoadPropertyInfoFromFile("/product_property_contexts", &property_infos);
+        LoadPropertyInfoFromFile("/odm_property_contexts", &property_infos);
     }
 
     auto serialized_contexts = std::string();
diff --git a/init/property_service.h b/init/property_service.h
index 9022f5a..85e7bc0 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -36,7 +36,7 @@
 extern bool PropertyChildReap(pid_t pid);
 
 void property_init(void);
-void property_load_boot_defaults(void);
+void property_load_boot_defaults(bool);
 void load_persist_props(void);
 void load_system_props(void);
 void StartPropertyService(Epoll* epoll);
diff --git a/init/selinux.cpp b/init/selinux.cpp
index ee302c1..aa66baa 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -61,14 +61,18 @@
 #include <android-base/parseint.h>
 #include <android-base/unique_fd.h>
 #include <cutils/android_reboot.h>
+#include <fs_avb/fs_avb.h>
 #include <selinux/android.h>
 
 #include "reboot_utils.h"
 #include "util.h"
 
+using namespace std::string_literals;
+
 using android::base::ParseInt;
 using android::base::Timer;
 using android::base::unique_fd;
+using android::fs_mgr::AvbHandle;
 
 namespace android {
 namespace init {
@@ -267,6 +271,8 @@
 }
 
 constexpr const char plat_policy_cil_file[] = "/system/etc/selinux/plat_sepolicy.cil";
+constexpr const char userdebug_plat_policy_cil_file[] =
+        "/system/etc/selinux/userdebug_plat_sepolicy.cil";
 
 bool IsSplitPolicyDevice() {
     return access(plat_policy_cil_file, R_OK) != -1;
@@ -282,10 +288,21 @@
     // secilc is invoked to compile the above three policy files into a single monolithic policy
     // file. This file is then loaded into the kernel.
 
+    // See if we need to load userdebug_plat_sepolicy.cil instead of plat_sepolicy.cil.
+    const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");
+    bool use_userdebug_policy =
+            ((force_debuggable_env && "true"s == force_debuggable_env) &&
+             AvbHandle::IsDeviceUnlocked() && access(userdebug_plat_policy_cil_file, F_OK) == 0);
+    if (use_userdebug_policy) {
+        LOG(WARNING) << "Using userdebug system sepolicy";
+    }
+
     // Load precompiled policy from vendor image, if a matching policy is found there. The policy
     // must match the platform policy on the system image.
     std::string precompiled_sepolicy_file;
-    if (FindPrecompiledSplitPolicy(&precompiled_sepolicy_file)) {
+    // use_userdebug_policy requires compiling sepolicy with userdebug_plat_sepolicy.cil.
+    // Thus it cannot use the precompiled policy from vendor image.
+    if (!use_userdebug_policy && FindPrecompiledSplitPolicy(&precompiled_sepolicy_file)) {
         unique_fd fd(open(precompiled_sepolicy_file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
         if (fd != -1) {
             if (selinux_android_load_policy_from_fd(fd, precompiled_sepolicy_file.c_str()) < 0) {
@@ -299,14 +316,6 @@
 
     LOG(INFO) << "Compiling SELinux policy";
 
-    // Determine the highest policy language version supported by the kernel
-    set_selinuxmnt("/sys/fs/selinux");
-    int max_policy_version = security_policyvers();
-    if (max_policy_version == -1) {
-        PLOG(ERROR) << "Failed to determine highest policy version supported by kernel";
-        return false;
-    }
-
     // We store the output of the compilation on /dev because this is the most convenient tmpfs
     // storage mount available this early in the boot sequence.
     char compiled_sepolicy[] = "/dev/sepolicy.XXXXXX";
@@ -353,14 +362,13 @@
     if (access(odm_policy_cil_file.c_str(), F_OK) == -1) {
         odm_policy_cil_file.clear();
     }
-    const std::string version_as_string = std::to_string(max_policy_version);
+    const std::string version_as_string = std::to_string(SEPOLICY_VERSION);
 
     // clang-format off
     std::vector<const char*> compile_args {
         "/system/bin/secilc",
-        plat_policy_cil_file,
+        use_userdebug_policy ? userdebug_plat_policy_cil_file : plat_policy_cil_file,
         "-m", "-M", "true", "-G", "-N",
-        // Target the highest policy language version supported by the kernel
         "-c", version_as_string.c_str(),
         plat_mapping_file.c_str(),
         "-o", compiled_sepolicy,
@@ -459,6 +467,8 @@
 
     selinux_android_restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
     selinux_android_restorecon("/dev/device-mapper", 0);
+
+    selinux_android_restorecon("/apex", 0);
 }
 
 int SelinuxKlogCallback(int type, const char* fmt, ...) {
diff --git a/init/service.cpp b/init/service.cpp
index 2186a85..cba42c4 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -1059,7 +1059,7 @@
         std::string oom_str = std::to_string(oom_score_adjust_);
         std::string oom_file = StringPrintf("/proc/%d/oom_score_adj", pid);
         if (!WriteStringToFile(oom_str, oom_file)) {
-            PLOG(ERROR) << "couldn't write oom_score_adj: " << strerror(errno);
+            PLOG(ERROR) << "couldn't write oom_score_adj";
         }
     }
 
diff --git a/libasyncio/Android.bp b/libasyncio/Android.bp
index 0fd2a3a..4ab439d 100644
--- a/libasyncio/Android.bp
+++ b/libasyncio/Android.bp
@@ -27,7 +27,6 @@
     name: "libasyncio",
     defaults: ["libasyncio_defaults"],
     vendor_available: true,
-    double_loadable: true,
     recovery_available: true,
     host_supported: true,
     srcs: [
diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp
index 6a967f7..781819a 100644
--- a/libbacktrace/BacktraceMap.cpp
+++ b/libbacktrace/BacktraceMap.cpp
@@ -46,8 +46,7 @@
   }
 }
 
-BacktraceMap::~BacktraceMap() {
-}
+BacktraceMap::~BacktraceMap() {}
 
 void BacktraceMap::FillIn(uint64_t addr, backtrace_map_t* map) {
   ScopedBacktraceMapIteratorLock lock(this);
@@ -68,12 +67,13 @@
   char permissions[5];
   int name_pos;
 
-// Mac OS vmmap(1) output:
-// __TEXT                 0009f000-000a1000 [    8K     8K] r-x/rwx SM=COW  /Volumes/android/dalvik-dev/out/host/darwin-x86/bin/libcorkscrew_test\n
-// 012345678901234567890123456789012345678901234567890123456789
-// 0         1         2         3         4         5
-  if (sscanf(line, "%*21c %" SCNx64 "-%" SCNx64 " [%*13c] %3c/%*3c SM=%*3c  %n",
-             &start, &end, permissions, &name_pos) != 3) {
+  // Mac OS vmmap(1) output:
+  // __TEXT                 0009f000-000a1000 [    8K     8K] r-x/rwx SM=COW
+  // /Volumes/android/dalvik-dev/out/host/darwin-x86/bin/libcorkscrew_test\n
+  // 012345678901234567890123456789012345678901234567890123456789
+  // 0         1         2         3         4         5
+  if (sscanf(line, "%*21c %" SCNx64 "-%" SCNx64 " [%*13c] %3c/%*3c SM=%*3c  %n", &start, &end,
+             permissions, &name_pos) != 3) {
     return false;
   }
 
@@ -90,21 +90,21 @@
     map->flags |= PROT_EXEC;
   }
 
-  map->name = line+name_pos;
-  if (!map->name.empty() && map->name[map->name.length()-1] == '\n') {
-    map->name.erase(map->name.length()-1);
+  map->name = line + name_pos;
+  if (!map->name.empty() && map->name[map->name.length() - 1] == '\n') {
+    map->name.erase(map->name.length() - 1);
   }
 
-  ALOGV("Parsed map: start=%p, end=%p, flags=%x, name=%s",
-        reinterpret_cast<void*>(map->start), reinterpret_cast<void*>(map->end),
-        map->flags, map->name.c_str());
+  ALOGV("Parsed map: start=%p, end=%p, flags=%x, name=%s", reinterpret_cast<void*>(map->start),
+        reinterpret_cast<void*>(map->end), map->flags, map->name.c_str());
   return true;
 }
 #endif  // defined(__APPLE__)
 
 bool BacktraceMap::Build() {
 #if defined(__APPLE__)
-  char cmd[sizeof(pid_t)*3 + sizeof("vmmap -w -resident -submap -allSplitLibs -interleaved ") + 1];
+  char
+      cmd[sizeof(pid_t) * 3 + sizeof("vmmap -w -resident -submap -allSplitLibs -interleaved ") + 1];
   char line[1024];
   // cmd is guaranteed to always be big enough to hold this string.
   snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid_);
@@ -113,7 +113,7 @@
     return false;
   }
 
-  while(fgets(line, sizeof(line), fp)) {
+  while (fgets(line, sizeof(line), fp)) {
     backtrace_map_t map;
     if (ParseLine(line, &map)) {
       maps_.push_back(map);
@@ -123,7 +123,7 @@
   return true;
 #else
   return android::procinfo::ReadProcessMaps(
-      pid_, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t, const char* name) {
+      pid_, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t, ino_t, const char* name) {
         maps_.resize(maps_.size() + 1);
         backtrace_map_t& map = maps_.back();
         map.start = start;
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
index 9d15af2..4518891 100644
--- a/libbacktrace/UnwindStackMap.cpp
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -55,7 +55,7 @@
   }
 
   // Iterate through the maps and fill in the backtrace_map_t structure.
-  for (auto* map_info : *stack_maps_) {
+  for (const auto& map_info : *stack_maps_) {
     backtrace_map_t map;
     map.start = map_info->start;
     map.end = map_info->end;
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index f1dcd50..a3df380 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -219,8 +219,6 @@
     { 00755, AID_ROOT,      AID_SHELL,     0, "product/bin/*" },
     { 00750, AID_ROOT,      AID_SHELL,     0, "sbin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/*" },
-    { 00755, AID_ROOT,      AID_ROOT,      0, "system/lib/valgrind/*" },
-    { 00755, AID_ROOT,      AID_ROOT,      0, "system/lib64/valgrind/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/xbin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/apex/*/bin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "vendor/bin/*" },
diff --git a/libcutils/include/cutils/native_handle.h b/libcutils/include/cutils/native_handle.h
index 10f5bc0..f6cae36 100644
--- a/libcutils/include/cutils/native_handle.h
+++ b/libcutils/include/cutils/native_handle.h
@@ -23,6 +23,9 @@
 extern "C" {
 #endif
 
+#define NATIVE_HANDLE_MAX_FDS 1024
+#define NATIVE_HANDLE_MAX_INTS 1024
+
 /* Declare a char array for use with native_handle_init */
 #define NATIVE_HANDLE_DECLARE_STORAGE(name, maxFds, maxInts) \
     alignas(native_handle_t) char (name)[                            \
diff --git a/libcutils/native_handle.cpp b/libcutils/native_handle.cpp
index 66f7a3d..b409e5b 100644
--- a/libcutils/native_handle.cpp
+++ b/libcutils/native_handle.cpp
@@ -22,9 +22,6 @@
 #include <string.h>
 #include <unistd.h>
 
-static const int kMaxNativeFds = 1024;
-static const int kMaxNativeInts = 1024;
-
 native_handle_t* native_handle_init(char* storage, int numFds, int numInts) {
     if ((uintptr_t) storage % alignof(native_handle_t)) {
         errno = EINVAL;
@@ -39,7 +36,8 @@
 }
 
 native_handle_t* native_handle_create(int numFds, int numInts) {
-    if (numFds < 0 || numInts < 0 || numFds > kMaxNativeFds || numInts > kMaxNativeInts) {
+    if (numFds < 0 || numInts < 0 || numFds > NATIVE_HANDLE_MAX_FDS ||
+        numInts > NATIVE_HANDLE_MAX_INTS) {
         errno = EINVAL;
         return NULL;
     }
diff --git a/libcutils/trace-dev.inc b/libcutils/trace-dev.inc
index c9580af..e3da77b 100644
--- a/libcutils/trace-dev.inc
+++ b/libcutils/trace-dev.inc
@@ -80,7 +80,7 @@
 // Determine whether application-level tracing is enabled for this process.
 static bool atrace_is_app_tracing_enabled()
 {
-    bool sys_debuggable = __android_log_is_debuggable();
+    bool sys_debuggable = property_get_bool("ro.debuggable", 0);
     bool result = false;
 
     if (sys_debuggable || atrace_is_debuggable) {
diff --git a/libkeyutils/Android.bp b/libkeyutils/Android.bp
index e816926..dda491a 100644
--- a/libkeyutils/Android.bp
+++ b/libkeyutils/Android.bp
@@ -19,13 +19,14 @@
 
 cc_binary {
     name: "mini-keyctl",
-    srcs: ["mini_keyctl.cpp"],
-
+    srcs: [
+        "mini_keyctl.cpp",
+        "mini_keyctl_utils.cpp"
+    ],
     shared_libs: [
         "libbase",
         "libkeyutils",
         "liblog",
     ],
-
-    cflags: ["-Werror", "-Wall", "-Wextra"],
+    cflags: ["-Werror", "-Wall", "-Wextra", "-fexceptions"],
 }
diff --git a/libkeyutils/mini_keyctl.cpp b/libkeyutils/mini_keyctl.cpp
index abc8f82..e09c864 100644
--- a/libkeyutils/mini_keyctl.cpp
+++ b/libkeyutils/mini_keyctl.cpp
@@ -18,159 +18,82 @@
  * A tool loads keys to keyring.
  */
 
-#include <dirent.h>
-#include <errno.h>
-#include <sys/types.h>
+#include "mini_keyctl_utils.h"
+
+#include <error.h>
+#include <stdio.h>
 #include <unistd.h>
 
-#include <fstream>
-#include <iostream>
-#include <iterator>
-#include <sstream>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/strings.h>
-#include <keyutils.h>
-
-static constexpr int kMaxCertSize = 4096;
-
-// Add all the certs from directory path to keyring with keyring_id. Returns the number of keys
-// added.
-int AddKeys(const std::string& path, const key_serial_t keyring_id, const std::string& keyring_desc,
-            int start_index) {
-  std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(path.c_str()), closedir);
-  if (!dir) {
-    PLOG(WARNING) << "Failed to open directory " << path;
-    return 0;
-  }
-  int keys_added = 0;
-  struct dirent* dp;
-  while ((dp = readdir(dir.get())) != NULL) {
-    if (dp->d_type != DT_REG) {
-      continue;
-    }
-    std::string cert_path = path + "/" + dp->d_name;
-    std::string cert_buf;
-    if (!android::base::ReadFileToString(cert_path, &cert_buf, false /* follow_symlinks */)) {
-      LOG(ERROR) << "Failed to read " << cert_path;
-      continue;
-    }
-
-    if (cert_buf.size() > kMaxCertSize) {
-      LOG(ERROR) << "Certficate size too large: " << cert_path;
-      continue;
-    }
-
-    // Add key to keyring.
-    int key_desc_index = keys_added + start_index;
-    std::string key_desc = keyring_desc + "-key" + std::to_string(key_desc_index);
-    key_serial_t key =
-        add_key("asymmetric", key_desc.c_str(), &cert_buf[0], cert_buf.size(), keyring_id);
-    if (key < 0) {
-      PLOG(ERROR) << "Failed to add key to keyring: " << cert_path;
-      continue;
-    }
-    keys_added++;
-  }
-  return keys_added;
-}
-
-std::vector<std::string> SplitBySpace(const std::string& s) {
-  std::istringstream iss(s);
-  return std::vector<std::string>{std::istream_iterator<std::string>{iss},
-                                  std::istream_iterator<std::string>{}};
-}
-
-// Find the keyring id. Because request_key(2) syscall is not available or the key is
-// kernel keyring, the id is looked up from /proc/keys. The keyring description may contain other
-// information in the descritption section depending on the key type, only the first word in the
-// keyring description is used for searching.
-bool GetKeyringId(const std::string& keyring_desc, key_serial_t* keyring_id) {
-  if (!keyring_id) {
-    LOG(ERROR) << "keyring_id is null";
-    return false;
-  }
-
-  // Only keys allowed by SELinux rules will be shown here.
-  std::ifstream proc_keys_file("/proc/keys");
-  if (!proc_keys_file.is_open()) {
-    PLOG(ERROR) << "Failed to open /proc/keys";
-    return false;
-  }
-
-  std::string line;
-  while (getline(proc_keys_file, line)) {
-    std::vector<std::string> tokens = SplitBySpace(line);
-    if (tokens.size() < 9) {
-      continue;
-    }
-    std::string key_id = tokens[0];
-    std::string key_type = tokens[7];
-    // The key description may contain space.
-    std::string key_desc_prefix = tokens[8];
-    // The prefix has a ":" at the end
-    std::string key_desc_pattern = keyring_desc + ":";
-    if (key_type != "keyring" || key_desc_prefix != key_desc_pattern) {
-      continue;
-    }
-    *keyring_id = std::stoi(key_id, nullptr, 16);
-    return true;
-  }
-  return false;
-}
+#include <android-base/parseint.h>
 
 static void Usage(int exit_code) {
-  fprintf(stderr, "usage: mini-keyctl -c PATHS -s DESCRIPTION\n");
-  fprintf(stderr, "\n");
-  fprintf(stderr, "-c, --cert_dirs     the certificate locations, separated by comma\n");
-  fprintf(stderr, "-k, --keyring       the keyring description\n");
+  fprintf(stderr, "usage: mini-keyctl <action> [args,]\n");
+  fprintf(stderr, "       mini-keyctl add <type> <desc> <data> <keyring>\n");
+  fprintf(stderr, "       mini-keyctl padd <type> <desc> <keyring>\n");
+  fprintf(stderr, "       mini-keyctl dadd <type> <desc_prefix> <cert_dir> <keyring>\n");
+  fprintf(stderr, "       mini-keyctl unlink <key> <keyring>\n");
+  fprintf(stderr, "       mini-keyctl restrict_keyring <keyring>\n");
+  fprintf(stderr, "       mini-keyctl security <key>\n");
   _exit(exit_code);
 }
 
-int main(int argc, char** argv) {
-  if (argc < 5) Usage(1);
+static key_serial_t parseKeyOrDie(const char* str) {
+  key_serial_t key;
+  if (!android::base::ParseInt(str, &key)) {
+    error(1 /* exit code */, 0 /* errno */, "Unparsable key: '%s'\n", str);
+  }
+  return key;
+}
 
-  std::string arg_cert_dirs;
-  std::string arg_keyring_desc;
+int main(int argc, const char** argv) {
+  if (argc < 2) Usage(1);
+  const std::string action = argv[1];
 
-  for (int i = 1; i < argc; i++) {
-    std::string option = argv[i];
-    if (option == "-c" || option == "--cert_dirs") {
-      if (i + 1 < argc) arg_cert_dirs = argv[++i];
-    } else if (option == "-k" || option == "--keyring") {
-      if (i + 1 < argc) arg_keyring_desc = argv[++i];
+  if (action == "add") {
+    if (argc != 6) Usage(1);
+    std::string type = argv[2];
+    std::string desc = argv[3];
+    std::string data = argv[4];
+    std::string keyring = argv[5];
+    return Add(type, desc, data, keyring);
+  } else if (action == "dadd") {
+    if (argc != 6) Usage(1);
+    std::string type = argv[2];
+    // The key description contains desc_prefix and an index.
+    std::string desc_prefix = argv[3];
+    std::string cert_dir = argv[4];
+    std::string keyring = argv[5];
+    return AddCertsFromDir(type, desc_prefix, cert_dir, keyring);
+  } else if (action == "padd") {
+    if (argc != 5) Usage(1);
+    std::string type = argv[2];
+    std::string desc = argv[3];
+    std::string keyring = argv[4];
+    return Padd(type, desc, keyring);
+  } else if (action == "restrict_keyring") {
+    if (argc != 3) Usage(1);
+    std::string keyring = argv[2];
+    return RestrictKeyring(keyring);
+  } else if (action == "unlink") {
+    if (argc != 4) Usage(1);
+    key_serial_t key = parseKeyOrDie(argv[2]);
+    const std::string keyring = argv[3];
+    return Unlink(key, keyring);
+  } else if (action == "security") {
+    if (argc != 3) Usage(1);
+    const char* key_str = argv[2];
+    key_serial_t key = parseKeyOrDie(key_str);
+    std::string context = RetrieveSecurityContext(key);
+    if (context.empty()) {
+      perror(key_str);
+      return 1;
     }
-  }
-
-  if (arg_cert_dirs.empty() || arg_keyring_desc.empty()) {
-    LOG(ERROR) << "Missing cert_dirs or keyring desc";
+    fprintf(stderr, "%s\n", context.c_str());
+    return 0;
+  } else {
+    fprintf(stderr, "Unrecognized action: %s\n", action.c_str());
     Usage(1);
   }
 
-  // Get the keyring id
-  key_serial_t key_ring_id;
-  if (!GetKeyringId(arg_keyring_desc, &key_ring_id)) {
-    PLOG(ERROR) << "Can't find keyring with " << arg_keyring_desc;
-    return 1;
-  }
-
-  std::vector<std::string> cert_dirs = android::base::Split(arg_cert_dirs, ",");
-  int start_index = 0;
-  for (const auto& cert_dir : cert_dirs) {
-    int keys_added = AddKeys(cert_dir, key_ring_id, arg_keyring_desc, start_index);
-    start_index += keys_added;
-  }
-
-  // Prevent new keys to be added.
-  if (!android::base::GetBoolProperty("ro.debuggable", false) &&
-      keyctl_restrict_keyring(key_ring_id, nullptr, nullptr) < 0) {
-    PLOG(ERROR) << "Failed to restrict key ring " << arg_keyring_desc;
-    return 1;
-  }
-
   return 0;
 }
diff --git a/libkeyutils/mini_keyctl_utils.cpp b/libkeyutils/mini_keyctl_utils.cpp
new file mode 100644
index 0000000..9fe2dfe
--- /dev/null
+++ b/libkeyutils/mini_keyctl_utils.cpp
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2019 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 <mini_keyctl_utils.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <iostream>
+#include <iterator>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <keyutils.h>
+
+static constexpr int kMaxCertSize = 4096;
+
+static std::vector<std::string> SplitBySpace(const std::string& s) {
+  std::istringstream iss(s);
+  return std::vector<std::string>{std::istream_iterator<std::string>{iss},
+                                  std::istream_iterator<std::string>{}};
+}
+
+// Find the keyring id. Because request_key(2) syscall is not available or the key is
+// kernel keyring, the id is looked up from /proc/keys. The keyring description may contain other
+// information in the descritption section depending on the key type, only the first word in the
+// keyring description is used for searching.
+static bool GetKeyringId(const std::string& keyring_desc, key_serial_t* keyring_id) {
+  if (!keyring_id) {
+    LOG(ERROR) << "keyring_id is null";
+    return false;
+  }
+
+  // If the keyring id is already a hex number, directly convert it to keyring id
+  if (android::base::ParseInt(keyring_desc.c_str(), keyring_id)) {
+    return true;
+  }
+
+  // Only keys allowed by SELinux rules will be shown here.
+  std::ifstream proc_keys_file("/proc/keys");
+  if (!proc_keys_file.is_open()) {
+    PLOG(ERROR) << "Failed to open /proc/keys";
+    return false;
+  }
+
+  std::string line;
+  while (getline(proc_keys_file, line)) {
+    std::vector<std::string> tokens = SplitBySpace(line);
+    if (tokens.size() < 9) {
+      continue;
+    }
+    std::string key_id = tokens[0];
+    std::string key_type = tokens[7];
+    // The key description may contain space.
+    std::string key_desc_prefix = tokens[8];
+    // The prefix has a ":" at the end
+    std::string key_desc_pattern = keyring_desc + ":";
+    if (key_type != "keyring" || key_desc_prefix != key_desc_pattern) {
+      continue;
+    }
+    *keyring_id = std::stoi(key_id, nullptr, 16);
+    return true;
+  }
+  return false;
+}
+
+int AddCertsFromDir(const std::string& type, const std::string& desc_prefix,
+                    const std::string& cert_dir, const std::string& keyring) {
+  key_serial_t keyring_id;
+  if (!GetKeyringId(keyring, &keyring_id)) {
+    LOG(ERROR) << "Can not find keyring id";
+    return 1;
+  }
+
+  std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(cert_dir.c_str()), closedir);
+  if (!dir) {
+    PLOG(WARNING) << "Failed to open directory " << cert_dir;
+    return 1;
+  }
+  int keys_added = 0;
+  struct dirent* dp;
+  while ((dp = readdir(dir.get())) != NULL) {
+    if (dp->d_type != DT_REG) {
+      continue;
+    }
+    std::string cert_path = cert_dir + "/" + dp->d_name;
+    std::string cert_buf;
+    if (!android::base::ReadFileToString(cert_path, &cert_buf, false /* follow_symlinks */)) {
+      LOG(ERROR) << "Failed to read " << cert_path;
+      continue;
+    }
+
+    if (cert_buf.size() > kMaxCertSize) {
+      LOG(ERROR) << "Certficate size too large: " << cert_path;
+      continue;
+    }
+
+    // Add key to keyring.
+    int key_desc_index = keys_added;
+    std::string key_desc = desc_prefix + std::to_string(key_desc_index);
+    key_serial_t key =
+        add_key(type.c_str(), key_desc.c_str(), &cert_buf[0], cert_buf.size(), keyring_id);
+    if (key < 0) {
+      PLOG(ERROR) << "Failed to add key to keyring: " << cert_path;
+      continue;
+    }
+    LOG(INFO) << "Key " << cert_path << " added to " << keyring << " with key id 0x" << std::hex
+              << key;
+    keys_added++;
+  }
+  return 0;
+}
+
+int Unlink(key_serial_t key, const std::string& keyring) {
+  key_serial_t keyring_id;
+  if (!GetKeyringId(keyring, &keyring_id)) {
+    LOG(ERROR) << "Can't find keyring " << keyring;
+    return 1;
+  }
+
+  if (keyctl_unlink(key, keyring_id) < 0) {
+    PLOG(ERROR) << "Failed to unlink key 0x" << std::hex << key << " from keyring " << keyring_id;
+    return 1;
+  }
+  return 0;
+}
+
+int Add(const std::string& type, const std::string& desc, const std::string& data,
+        const std::string& keyring) {
+  if (data.size() > kMaxCertSize) {
+    LOG(ERROR) << "Certificate too large";
+    return 1;
+  }
+
+  key_serial_t keyring_id;
+  if (!GetKeyringId(keyring, &keyring_id)) {
+    LOG(ERROR) << "Can not find keyring id";
+    return 1;
+  }
+
+  key_serial_t key = add_key(type.c_str(), desc.c_str(), data.c_str(), data.size(), keyring_id);
+
+  if (key < 0) {
+    PLOG(ERROR) << "Failed to add key";
+    return 1;
+  }
+
+  LOG(INFO) << "Key " << desc << " added to " << keyring << " with key id: 0x" << std::hex << key;
+  return 0;
+}
+
+int Padd(const std::string& type, const std::string& desc, const std::string& keyring) {
+  key_serial_t keyring_id;
+  if (!GetKeyringId(keyring, &keyring_id)) {
+    LOG(ERROR) << "Can not find keyring id";
+    return 1;
+  }
+
+  // read from stdin to get the certificates
+  std::istreambuf_iterator<char> begin(std::cin), end;
+  std::string data(begin, end);
+
+  if (data.size() > kMaxCertSize) {
+    LOG(ERROR) << "Certificate too large";
+    return 1;
+  }
+
+  key_serial_t key = add_key(type.c_str(), desc.c_str(), data.c_str(), data.size(), keyring_id);
+
+  if (key < 0) {
+    PLOG(ERROR) << "Failed to add key";
+    return 1;
+  }
+
+  LOG(INFO) << "Key " << desc << " added to " << keyring << " with key id: 0x" << std::hex << key;
+  return 0;
+}
+
+int RestrictKeyring(const std::string& keyring) {
+  key_serial_t keyring_id;
+  if (!GetKeyringId(keyring, &keyring_id)) {
+    LOG(ERROR) << "Cannot find keyring id";
+    return 1;
+  }
+
+  if (keyctl_restrict_keyring(keyring_id, nullptr, nullptr) < 0) {
+    PLOG(ERROR) << "Cannot restrict keyring " << keyring;
+    return 1;
+  }
+  return 0;
+}
+
+std::string RetrieveSecurityContext(key_serial_t key) {
+  // Simply assume this size is enough in practice.
+  const int kMaxSupportedSize = 256;
+  std::string context;
+  context.resize(kMaxSupportedSize);
+  long retval = keyctl_get_security(key, context.data(), kMaxSupportedSize);
+  if (retval < 0) {
+    PLOG(ERROR) << "Cannot get security context of key 0x" << std::hex << key;
+    return std::string();
+  }
+  if (retval > kMaxSupportedSize) {
+    LOG(ERROR) << "The key has unexpectedly long security context than " << kMaxSupportedSize;
+    return std::string();
+  }
+  context.resize(retval);
+  return context;
+}
diff --git a/libkeyutils/mini_keyctl_utils.h b/libkeyutils/mini_keyctl_utils.h
new file mode 100644
index 0000000..804a357
--- /dev/null
+++ b/libkeyutils/mini_keyctl_utils.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 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 "include/keyutils.h"
+
+#include <string>
+
+// Add all files in a directory as certificates to a keyring. |keyring| could be the keyring
+// description or keyring id in hex.
+int AddCertsFromDir(const std::string& type, const std::string& desc_prefix,
+                    const std::string& cert_dir, const std::string& keyring);
+
+// Add key to a keyring. Returns non-zero if error happens.
+int Add(const std::string& type, const std::string& desc, const std::string& data,
+        const std::string& keyring);
+
+// Add key from stdin to a keyring. Returns non-zero if error happens.
+int Padd(const std::string& type, const std::string& desc, const std::string& keyring);
+
+// Removes the link from a keyring to a key if exists. Return non-zero if error happens.
+int Unlink(key_serial_t key, const std::string& keyring);
+
+// Apply key-linking to a keyring. Return non-zero if error happens.
+int RestrictKeyring(const std::string& keyring);
+
+// Retrieves a key's security context. Return the context string, or empty string on error.
+std::string RetrieveSecurityContext(key_serial_t key);
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 1d4a0a0..9b41ebe 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -46,6 +46,8 @@
     vendor_available: true,
     recovery_available: true,
     export_include_dirs: ["include"],
+    system_shared_libs: [],
+    stl: "none",
     target: {
         windows: {
             enabled: true,
@@ -99,6 +101,11 @@
     header_libs: ["liblog_headers"],
     export_header_lib_headers: ["liblog_headers"],
 
+    stubs: {
+        symbol_file: "liblog.map.txt",
+        versions: ["10000"],
+    },
+
     cflags: [
         "-Werror",
         // This is what we want to do:
diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt
index 191ef1b..ce4c53c 100644
--- a/liblog/liblog.map.txt
+++ b/liblog/liblog.map.txt
@@ -19,12 +19,12 @@
     android_logger_get_log_readable_size; # vndk
     android_logger_get_log_version; # vndk
     android_logger_get_log_size; # vndk
-    android_logger_list_alloc; # vndk
-    android_logger_list_alloc_time; # vndk
-    android_logger_list_free; # vndk
+    android_logger_list_alloc; # apex vndk
+    android_logger_list_alloc_time; # apex vndk
+    android_logger_list_free; # apex vndk
     android_logger_list_open; # vndk
-    android_logger_list_read; # vndk
-    android_logger_open; # vndk
+    android_logger_list_read; # apex vndk
+    android_logger_open; # apex vndk
     android_logger_set_log_size; # vndk
 };
 
@@ -33,18 +33,18 @@
     android_logger_get_prune_list; # vndk
     android_logger_set_prune_list; # vndk
     android_logger_get_statistics; # vndk
-    __android_log_error_write; # vndk
+    __android_log_error_write; # apex vndk
     __android_log_is_loggable;
-    create_android_logger; #vndk
-    android_log_destroy; #vndk
-    android_log_write_list_begin; #vndk
-    android_log_write_list_end; #vndk
-    android_log_write_int32; #vndk
-    android_log_write_int64; #vndk
-    android_log_write_string8; #vndk
-    android_log_write_string8_len; #vndk
-    android_log_write_float32; #vndk
-    android_log_write_list; #vndk
+    create_android_logger; # apex vndk
+    android_log_destroy; # apex vndk
+    android_log_write_list_begin; # apex vndk
+    android_log_write_list_end; # apex vndk
+    android_log_write_int32; # apex vndk
+    android_log_write_int64; # apex vndk
+    android_log_write_string8; # apex vndk
+    android_log_write_string8_len; # apex vndk
+    android_log_write_float32; # apex vndk
+    android_log_write_list; # apex vndk
 
 };
 
@@ -56,19 +56,19 @@
 
 LIBLOG_Q {
   global:
+    __android_log_bswrite; # apex
+    __android_log_btwrite; # apex
+    __android_log_bwrite; # apex
+    __android_log_close; # apex
+    __android_log_security; # apex
     android_log_reset; #vndk
     android_log_parser_reset; #vndk
 };
 
 LIBLOG_PRIVATE {
   global:
-    __android_log_bswrite;
-    __android_log_btwrite;
-    __android_log_bwrite;
-    __android_log_close;
     __android_log_pmsg_file_read;
     __android_log_pmsg_file_write;
-    __android_log_security;
     __android_log_security_bswrite;
     __android_logger_get_buffer_size;
     __android_logger_property_get_bool;
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
index 908fe7f..7fa3f43 100644
--- a/liblog/logger_write.cpp
+++ b/liblog/logger_write.cpp
@@ -413,6 +413,8 @@
   if (!tag) tag = "";
 
   /* XXX: This needs to go! */
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wstring-plus-int"
   if (bufID != LOG_ID_RADIO) {
     switch (tag[0]) {
       case 'H':
@@ -454,6 +456,7 @@
         break;
     }
   }
+#pragma clang diagnostic pop
 
 #if __BIONIC__
   if (prio == ANDROID_LOG_FATAL) {
diff --git a/libmeminfo/libdmabufinfo/dmabufinfo.cpp b/libmeminfo/libdmabufinfo/dmabufinfo.cpp
index 439cf68..9fb22a1 100644
--- a/libmeminfo/libdmabufinfo/dmabufinfo.cpp
+++ b/libmeminfo/libdmabufinfo/dmabufinfo.cpp
@@ -150,18 +150,14 @@
         auto buf = std::find_if(dmabufs->begin(), dmabufs->end(),
                                 [&inode](const DmaBuffer& dbuf) { return dbuf.inode() == inode; });
         if (buf != dmabufs->end()) {
-            if (buf->name() == "" || buf->name() == "<unknown>")
-                buf->SetName(name);
-            if (buf->exporter() == "" || buf->exporter() == "<unknown>")
-                buf->SetExporter(exporter);
-            if (buf->count() == 0)
-                buf->SetCount(count);
+            if (buf->name() == "" || buf->name() == "<unknown>") buf->SetName(name);
+            if (buf->exporter() == "" || buf->exporter() == "<unknown>") buf->SetExporter(exporter);
+            if (buf->count() == 0) buf->SetCount(count);
             buf->AddFdRef(pid);
             continue;
         }
 
-        DmaBuffer& db =
-                dmabufs->emplace_back(sb.st_ino, sb.st_blocks * 512, count, exporter, name);
+        DmaBuffer& db = dmabufs->emplace_back(sb.st_ino, sb.st_blocks * 512, count, exporter, name);
         db.AddFdRef(pid);
     }
 
@@ -182,29 +178,12 @@
     // Process the map if it is dmabuf. Add map reference to existing object in 'dmabufs'
     // if it was already found. If it wasn't create a new one and append it to 'dmabufs'
     auto account_dmabuf = [&](uint64_t start, uint64_t end, uint16_t /* flags */,
-                              uint64_t /* pgoff */, const char* name) {
+                              uint64_t /* pgoff */, ino_t inode, const char* name) {
         // no need to look into this mapping if it is not dmabuf
         if (!FileIsDmaBuf(std::string(name))) {
             return;
         }
 
-        // TODO (b/123532375) : Add inode number to the callback of ReadMapFileContent.
-        //
-        // Workaround: we know 'name' points to the name at the end of 'line'.
-        // We use that to backtrack and pick up the inode number from the line as well.
-        // start    end      flag pgoff    mj:mn inode   name
-        // 00400000-00409000 r-xp 00000000 00:00 426998  /dmabuf (deleted)
-        const char* p = name;
-        p--;
-        // skip spaces
-        while (p != line && *p == ' ') {
-            p--;
-        }
-        // walk backwards to the beginning of inode number
-        while (p != line && isdigit(*p)) {
-            p--;
-        }
-        uint64_t inode = strtoull(p, nullptr, 10);
         auto buf = std::find_if(dmabufs->begin(), dmabufs->end(),
                                 [&inode](const DmaBuffer& dbuf) { return dbuf.inode() == inode; });
         if (buf != dmabufs->end()) {
diff --git a/libmeminfo/procmeminfo.cpp b/libmeminfo/procmeminfo.cpp
index 069b6b3..934d65c 100644
--- a/libmeminfo/procmeminfo.cpp
+++ b/libmeminfo/procmeminfo.cpp
@@ -246,7 +246,7 @@
     // parse and read /proc/<pid>/maps
     std::string maps_file = ::android::base::StringPrintf("/proc/%d/maps", pid_);
     if (!::android::procinfo::ReadMapFile(
-                maps_file, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
+                maps_file, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t,
                                const char* name) {
                     maps_.emplace_back(Vma(start, end, pgoff, flags, name));
                 })) {
@@ -394,7 +394,7 @@
         // If it has, we are looking for the vma stats
         // 00400000-00409000 r-xp 00000000 fc:00 426998  /usr/lib/gvfs/gvfsd-http
         if (!::android::procinfo::ReadMapFileContent(
-                    line, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
+                    line, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t,
                               const char* name) {
                         vma.start = start;
                         vma.end = end;
diff --git a/libmeminfo/tools/librank.cpp b/libmeminfo/tools/librank.cpp
index 2c2583d..e53c746 100644
--- a/libmeminfo/tools/librank.cpp
+++ b/libmeminfo/tools/librank.cpp
@@ -26,6 +26,7 @@
 #include <unistd.h>
 
 #include <algorithm>
+#include <map>
 #include <memory>
 #include <vector>
 
@@ -122,30 +123,22 @@
 
     const std::string& name() const { return name_; }
     const MemUsage& usage() const { return usage_; }
-    const std::vector<ProcessRecord>& processes() const { return procs_; }
+    const std::map<pid_t, ProcessRecord>& processes() const { return procs_; }
     uint64_t pss() const { return usage_.pss; }
     void AddUsage(const ProcessRecord& proc, const MemUsage& mem_usage) {
-        auto process = std::find_if(procs_.begin(), procs_.end(),
-                                    [&](auto p) -> bool { return p.pid() == proc.pid(); });
-        if (process == procs_.end()) {
-            process = procs_.emplace(procs_.end(), proc.pid());
-        }
-        process->AddUsage(mem_usage);
+        auto [it, inserted] = procs_.insert(std::pair<pid_t, ProcessRecord>(proc.pid(), proc));
+        it->second.AddUsage(mem_usage);
         add_mem_usage(&usage_, mem_usage);
     }
 
-    void Sort(std::function<bool(const ProcessRecord&, const ProcessRecord&)>& sorter) {
-        std::sort(procs_.begin(), procs_.end(), sorter);
-    }
-
   private:
     std::string name_;
     MemUsage usage_;
-    std::vector<ProcessRecord> procs_;
+    std::map<pid_t, ProcessRecord> procs_;
 };
 
 // List of every library / map
-static std::vector<LibRecord> g_libs;
+static std::map<std::string, LibRecord> g_libs;
 
 // List of library/map names that we don't want to show by default
 static const std::vector<std::string> g_blacklisted_libs = {"[heap]", "[stack]"};
@@ -204,13 +197,10 @@
             continue;
         }
 
-        auto lib = std::find_if(g_libs.begin(), g_libs.end(),
-                                [&](auto l) -> bool { return map.name == l.name(); });
-        if (lib == g_libs.end()) {
-            lib = g_libs.emplace(g_libs.end(), map.name);
-        }
+        auto [it, inserted] =
+            g_libs.insert(std::pair<std::string, LibRecord>(map.name, LibRecord(map.name)));
+        it->second.AddUsage(proc, map.usage);
 
-        lib->AddUsage(proc, map.usage);
         if (!g_has_swap && map.usage.swap) {
             g_has_swap = true;
         }
@@ -321,11 +311,16 @@
     }
     printf("Name/PID\n");
 
+    std::vector<LibRecord> v_libs;
+    v_libs.reserve(g_libs.size());
+    std::transform(g_libs.begin(), g_libs.end(), std::back_inserter(v_libs),
+        [] (std::pair<std::string, LibRecord> const& pair) { return pair.second; });
+
     // sort the libraries by their pss
-    std::sort(g_libs.begin(), g_libs.end(),
+    std::sort(v_libs.begin(), v_libs.end(),
               [](const LibRecord& l1, const LibRecord& l2) { return l1.pss() > l2.pss(); });
 
-    for (auto& lib : g_libs) {
+    for (auto& lib : v_libs) {
         printf("%6" PRIu64 "K   %7s   %6s   %6s   %6s  ", lib.pss() / 1024, "", "", "", "");
         if (g_has_swap) {
             printf(" %6s  ", "");
@@ -333,9 +328,15 @@
         printf("%s\n", lib.name().c_str());
 
         // sort all mappings first
-        lib.Sort(sort_func);
 
-        for (auto& p : lib.processes()) {
+        std::vector<ProcessRecord> procs;
+        procs.reserve(lib.processes().size());
+        std::transform(lib.processes().begin(), lib.processes().end(), std::back_inserter(procs),
+            [] (std::pair<pid_t, ProcessRecord> const& pair) { return pair.second; });
+
+        std::sort(procs.begin(), procs.end(), sort_func);
+
+        for (auto& p : procs) {
             const MemUsage& usage = p.usage();
             printf(" %6s  %7" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  ", "",
                    usage.vss / 1024, usage.rss / 1024, usage.pss / 1024, usage.uss / 1024);
diff --git a/libmemtrack/Android.bp b/libmemtrack/Android.bp
index 320b851..4e4554a 100644
--- a/libmemtrack/Android.bp
+++ b/libmemtrack/Android.bp
@@ -6,7 +6,6 @@
     vndk: {
         enabled: true,
     },
-    double_loadable: true,
     srcs: ["memtrack.cpp"],
     export_include_dirs: ["include"],
     local_include_dirs: ["include"],
diff --git a/libmemunreachable/HeapWalker.cpp b/libmemunreachable/HeapWalker.cpp
index 89837f7..7cae048 100644
--- a/libmemunreachable/HeapWalker.cpp
+++ b/libmemunreachable/HeapWalker.cpp
@@ -59,12 +59,19 @@
   }
 }
 
+// Sanitizers may consider certain memory inaccessible through certain pointers.
+// With MTE this will need to use unchecked instructions or disable tag checking globally.
+static uintptr_t ReadWordAtAddressUnsafe(uintptr_t word_ptr)
+    __attribute__((no_sanitize("address", "hwaddress"))) {
+  return *reinterpret_cast<uintptr_t*>(word_ptr);
+}
+
 bool HeapWalker::WordContainsAllocationPtr(uintptr_t word_ptr, Range* range, AllocationInfo** info) {
   walking_ptr_ = word_ptr;
   // This access may segfault if the process under test has done something strange,
   // for example mprotect(PROT_NONE) on a native heap page.  If so, it will be
   // caught and handled by mmaping a zero page over the faulting page.
-  uintptr_t value = *reinterpret_cast<uintptr_t*>(word_ptr);
+  uintptr_t value = ReadWordAtAddressUnsafe(word_ptr);
   walking_ptr_ = 0;
   if (value >= valid_allocations_range_.begin && value < valid_allocations_range_.end) {
     AllocationMap::iterator it = allocations_.find(Range{value, value + 1});
@@ -200,6 +207,6 @@
   }
 }
 
-ScopedSignalHandler::SignalFn ScopedSignalHandler::handler_;
+Allocator<ScopedSignalHandler::SignalFnMap>::unique_ptr ScopedSignalHandler::handler_map_;
 
 }  // namespace android
diff --git a/libmemunreachable/HeapWalker.h b/libmemunreachable/HeapWalker.h
index 9e3db08..f00bcca 100644
--- a/libmemunreachable/HeapWalker.h
+++ b/libmemunreachable/HeapWalker.h
@@ -52,7 +52,8 @@
         allocation_bytes_(0),
         roots_(allocator),
         root_vals_(allocator),
-        segv_handler_(),
+        sigsegv_handler_(allocator),
+        sigbus_handler_(allocator),
         walking_ptr_(0),
         walking_range_{0, 0},
         segv_logged_(false),
@@ -62,10 +63,14 @@
     valid_mappings_range_.end = 0;
     valid_mappings_range_.begin = ~valid_allocations_range_.end;
 
-    segv_handler_.install(
+    sigsegv_handler_.install(
         SIGSEGV, [=](ScopedSignalHandler& handler, int signal, siginfo_t* siginfo, void* uctx) {
           this->HandleSegFault(handler, signal, siginfo, uctx);
         });
+    sigbus_handler_.install(
+        SIGBUS, [=](ScopedSignalHandler& handler, int signal, siginfo_t* siginfo, void* uctx) {
+          this->HandleSegFault(handler, signal, siginfo, uctx);
+        });
   }
 
   ~HeapWalker() {}
@@ -106,7 +111,8 @@
   allocator::vector<Range> roots_;
   allocator::vector<uintptr_t> root_vals_;
 
-  ScopedSignalHandler segv_handler_;
+  ScopedSignalHandler sigsegv_handler_;
+  ScopedSignalHandler sigbus_handler_;
   volatile uintptr_t walking_ptr_;
   Range walking_range_;
   bool segv_logged_;
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
index 3d7b8a8..299c320 100644
--- a/libmemunreachable/MemUnreachable.cpp
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -217,6 +217,10 @@
   return ret == 0;
 }
 
+static bool is_sanitizer_mapping(const allocator::string& s) {
+  return s == "[anon:low shadow]" || s == "[anon:high shadow]" || has_prefix(s, "[anon:hwasan");
+}
+
 bool MemUnreachable::ClassifyMappings(const allocator::vector<Mapping>& mappings,
                                       allocator::vector<Mapping>& heap_mappings,
                                       allocator::vector<Mapping>& anon_mappings,
@@ -258,7 +262,8 @@
     } else if (mapping_name.size() == 0) {
       globals_mappings.emplace_back(*it);
     } else if (has_prefix(mapping_name, "[anon:") &&
-               mapping_name != "[anon:leak_detector_malloc]") {
+               mapping_name != "[anon:leak_detector_malloc]" &&
+               !is_sanitizer_mapping(mapping_name)) {
       // TODO(ccross): it would be nice to treat named anonymous mappings as
       // possible leaks, but naming something in a .bss or .data section makes
       // it impossible to distinguish them from mmaped and then named mappings.
diff --git a/libmemunreachable/ProcessMappings.cpp b/libmemunreachable/ProcessMappings.cpp
index 701ce16..8e1be4c 100644
--- a/libmemunreachable/ProcessMappings.cpp
+++ b/libmemunreachable/ProcessMappings.cpp
@@ -18,6 +18,7 @@
 #include <fcntl.h>
 #include <inttypes.h>
 #include <string.h>
+#include <sys/types.h>
 #include <unistd.h>
 
 #include <android-base/unique_fd.h>
@@ -30,7 +31,8 @@
 struct ReadMapCallback {
   ReadMapCallback(allocator::vector<Mapping>& mappings) : mappings_(mappings) {}
 
-  void operator()(uint64_t start, uint64_t end, uint16_t flags, uint64_t, const char* name) const {
+  void operator()(uint64_t start, uint64_t end, uint16_t flags, uint64_t, ino_t,
+                  const char* name) const {
     mappings_.emplace_back(start, end, flags & PROT_READ, flags & PROT_WRITE, flags & PROT_EXEC,
                            name);
   }
diff --git a/libmemunreachable/ScopedSignalHandler.h b/libmemunreachable/ScopedSignalHandler.h
index 9e08a8e..ef4473f 100644
--- a/libmemunreachable/ScopedSignalHandler.h
+++ b/libmemunreachable/ScopedSignalHandler.h
@@ -24,6 +24,7 @@
 
 #include "android-base/macros.h"
 
+#include "Allocator.h"
 #include "log.h"
 
 namespace android {
@@ -32,17 +33,29 @@
  public:
   using Fn = std::function<void(ScopedSignalHandler&, int, siginfo_t*, void*)>;
 
-  explicit ScopedSignalHandler() : signal_(-1) {}
+  explicit ScopedSignalHandler(Allocator<ScopedSignalHandler> allocator) : signal_(-1) {
+    if (handler_map_ == nullptr) {
+      Allocator<SignalFnMap> map_allocator = allocator;
+      handler_map_ = map_allocator.make_unique(allocator);
+    }
+  }
   ~ScopedSignalHandler() { reset(); }
 
   template <class F>
   void install(int signal, F&& f) {
     if (signal_ != -1) MEM_LOG_ALWAYS_FATAL("ScopedSignalHandler already installed");
 
-    handler_ = SignalFn([=](int signal, siginfo_t* si, void* uctx) { f(*this, signal, si, uctx); });
+    if (handler_map_->find(signal) != handler_map_->end()) {
+      MEM_LOG_ALWAYS_FATAL("ScopedSignalHandler already installed for %d", signal);
+    }
+
+    (*handler_map_)[signal] =
+        SignalFn([=](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); };
+    act.sa_sigaction = [](int signal, siginfo_t* si, void* uctx) {
+      ((*handler_map_)[signal])(signal, si, uctx);
+    };
     act.sa_flags = SA_SIGINFO;
 
     int ret = sigaction(signal, &act, &old_act_);
@@ -59,19 +72,22 @@
       if (ret < 0) {
         MEM_ALOGE("failed to uninstall segfault handler");
       }
-      handler_ = SignalFn{};
+
+      handler_map_->erase(signal_);
+      if (handler_map_->empty()) {
+        handler_map_.reset();
+      }
       signal_ = -1;
     }
   }
 
  private:
   using SignalFn = std::function<void(int, siginfo_t*, void*)>;
+  using SignalFnMap = allocator::unordered_map<int, SignalFn>;
   DISALLOW_COPY_AND_ASSIGN(ScopedSignalHandler);
   int signal_;
   struct sigaction old_act_;
-  // TODO(ccross): to support multiple ScopedSignalHandlers handler_ would need
-  // to be a static map of signals to handlers, but allocated with Allocator.
-  static SignalFn handler_;
+  static Allocator<SignalFnMap>::unique_ptr handler_map_;
 };
 
 }  // namespace android
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
index b806ae4..b9f0dbf 100644
--- a/libnativeloader/Android.bp
+++ b/libnativeloader/Android.bp
@@ -21,7 +21,6 @@
     shared_libs: [
         "libnativehelper",
         "liblog",
-        "libcutils",
         "libnativebridge",
         "libbase",
     ],
diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h
index 260f655..51fb875 100644
--- a/libnativeloader/include/nativeloader/native_loader.h
+++ b/libnativeloader/include/nativeloader/native_loader.h
@@ -36,14 +36,9 @@
 __attribute__((visibility("default")))
 void InitializeNativeLoader();
 
-__attribute__((visibility("default")))
-jstring CreateClassLoaderNamespace(JNIEnv* env,
-                                   int32_t target_sdk_version,
-                                   jobject class_loader,
-                                   bool is_shared,
-                                   bool is_for_vendor,
-                                   jstring library_path,
-                                   jstring permitted_path);
+__attribute__((visibility("default"))) jstring CreateClassLoaderNamespace(
+    JNIEnv* env, int32_t target_sdk_version, jobject class_loader, bool is_shared, jstring dex_path,
+    jstring library_path, jstring permitted_path);
 
 __attribute__((visibility("default"))) void* OpenNativeLibrary(
     JNIEnv* env, int32_t target_sdk_version, const char* path, jobject class_loader,
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 5394d7e..1c2581f 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -21,7 +21,6 @@
 #ifdef __ANDROID__
 #define LOG_TAG "libnativeloader"
 #include "nativeloader/dlext_namespaces.h"
-#include "cutils/properties.h"
 #include "log/log.h"
 #endif
 #include <dirent.h>
@@ -32,6 +31,7 @@
 #include <list>
 #include <memory>
 #include <mutex>
+#include <regex>
 #include <string>
 #include <vector>
 
@@ -116,6 +116,8 @@
 
 static constexpr const char* kVndkNamespaceName = "vndk";
 
+static constexpr const char* kDefaultNamespaceName = "default";
+static constexpr const char* kPlatformNamespaceName = "platform";
 static constexpr const char* kRuntimeNamespaceName = "runtime";
 
 // classloader-namespace is a linker namespace that is created for the loaded
@@ -137,10 +139,32 @@
 
 static constexpr const char* kApexPath = "/apex/";
 
+#if defined(__LP64__)
+static constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/lib64";
+static constexpr const char* kVendorLibPath = "/vendor/lib64";
+static constexpr const char* kProductLibPath = "/product/lib64:/system/product/lib64";
+#else
+static constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/lib";
+static constexpr const char* kVendorLibPath = "/vendor/lib";
+static constexpr const char* kProductLibPath = "/product/lib:/system/product/lib";
+#endif
+
+static const std::regex kVendorDexPathRegex("(^|:)/vendor/");
+static const std::regex kProductDexPathRegex("(^|:)(/system)?/product/");
+
+// Define origin of APK if it is from vendor partition or product partition
+typedef enum {
+  APK_ORIGIN_DEFAULT = 0,
+  APK_ORIGIN_VENDOR = 1,
+  APK_ORIGIN_PRODUCT = 2,
+} ApkOrigin;
+
 static bool is_debuggable() {
-  char debuggable[PROP_VALUE_MAX];
-  property_get("ro.debuggable", debuggable, "0");
-  return std::string(debuggable) == "1";
+  bool debuggable = false;
+#ifdef __BIONIC__
+  debuggable = android::base::GetBoolProperty("ro.debuggable", false);
+#endif
+  return debuggable;
 }
 
 static std::string vndk_version_str() {
@@ -170,7 +194,7 @@
   LibraryNamespaces() : initialized_(false) { }
 
   NativeLoaderNamespace* Create(JNIEnv* env, uint32_t target_sdk_version, jobject class_loader,
-                                bool is_shared, bool is_for_vendor, jstring java_library_path,
+                                bool is_shared, jstring dex_path, jstring java_library_path,
                                 jstring java_permitted_path, std::string* error_msg) {
     std::string library_path; // empty string by default.
 
@@ -179,6 +203,8 @@
       library_path = library_path_utf_chars.c_str();
     }
 
+    ApkOrigin apk_origin = GetApkOriginFromDexPath(env, dex_path);
+
     // (http://b/27588281) This is a workaround for apps using custom
     // classloaders and calling System.load() with an absolute path which
     // is outside of the classloader library search path.
@@ -225,31 +251,50 @@
     std::string system_exposed_libraries = system_public_libraries_;
     const char* namespace_name = kClassloaderNamespaceName;
     android_namespace_t* vndk_ns = nullptr;
-    if (is_for_vendor && !is_shared) {
-      LOG_FATAL_IF(is_native_bridge, "Unbundled vendor apk must not use translated architecture");
+    if ((apk_origin == APK_ORIGIN_VENDOR ||
+         (apk_origin == APK_ORIGIN_PRODUCT && target_sdk_version > 29)) &&
+        !is_shared) {
+      LOG_FATAL_IF(is_native_bridge,
+                   "Unbundled vendor / product apk must not use translated architecture");
 
-      // For vendor apks, give access to the vendor lib even though
+      // For vendor / product apks, give access to the vendor / product lib even though
       // they are treated as unbundled; the libs and apks are still bundled
-      // together in the vendor partition.
-#if defined(__LP64__)
-      std::string vendor_lib_path = "/vendor/lib64";
-#else
-      std::string vendor_lib_path = "/vendor/lib";
-#endif
-      library_path = library_path + ":" + vendor_lib_path.c_str();
-      permitted_path = permitted_path + ":" + vendor_lib_path.c_str();
+      // together in the vendor / product partition.
+      const char* origin_partition;
+      const char* origin_lib_path;
+
+      switch (apk_origin) {
+        case APK_ORIGIN_VENDOR:
+          origin_partition = "vendor";
+          origin_lib_path = kVendorLibPath;
+          break;
+        case APK_ORIGIN_PRODUCT:
+          origin_partition = "product";
+          origin_lib_path = kProductLibPath;
+          break;
+        default:
+          origin_partition = "unknown";
+          origin_lib_path = "";
+      }
+
+      LOG_FATAL_IF(is_native_bridge, "Unbundled %s apk must not use translated architecture",
+                   origin_partition);
+
+      library_path = library_path + ":" + origin_lib_path;
+      permitted_path = permitted_path + ":" + origin_lib_path;
 
       // Also give access to LLNDK libraries since they are available to vendors
       system_exposed_libraries = system_exposed_libraries + ":" + system_llndk_libraries_.c_str();
 
       // Give access to VNDK-SP libraries from the 'vndk' namespace.
       vndk_ns = android_get_exported_namespace(kVndkNamespaceName);
-      LOG_ALWAYS_FATAL_IF(vndk_ns == nullptr,
-                          "Cannot find \"%s\" namespace for vendor apks", kVndkNamespaceName);
+      LOG_ALWAYS_FATAL_IF(vndk_ns == nullptr, "Cannot find \"%s\" namespace for %s apks",
+                          kVndkNamespaceName, origin_partition);
 
       // Different name is useful for debugging
       namespace_name = kVendorClassloaderNamespaceName;
-      ALOGD("classloader namespace configured for unbundled vendor apk. library_path=%s", library_path.c_str());
+      ALOGD("classloader namespace configured for unbundled %s apk. library_path=%s",
+            origin_partition, library_path.c_str());
     } else {
       // oem and product public libraries are NOT available to vendor apks, otherwise it
       // would be system->vendor violation.
@@ -265,8 +310,19 @@
 
     NativeLoaderNamespace native_loader_ns;
     if (!is_native_bridge) {
-      android_namespace_t* android_parent_ns =
-          parent_ns == nullptr ? nullptr : parent_ns->get_android_ns();
+      android_namespace_t* android_parent_ns;
+      if (parent_ns != nullptr) {
+        android_parent_ns = parent_ns->get_android_ns();
+      } else {
+        // Fall back to the platform namespace if no parent is found. It is
+        // called "default" for binaries in /system and "platform" for those in
+        // the Runtime APEX. Try "platform" first since "default" always exists.
+        android_parent_ns = android_get_exported_namespace(kPlatformNamespaceName);
+        if (android_parent_ns == nullptr) {
+          android_parent_ns = android_get_exported_namespace(kDefaultNamespaceName);
+        }
+      }
+
       android_namespace_t* ns = android_create_namespace(namespace_name,
                                                          nullptr,
                                                          library_path.c_str(),
@@ -315,8 +371,16 @@
 
       native_loader_ns = NativeLoaderNamespace(ns);
     } else {
-      native_bridge_namespace_t* native_bridge_parent_namespace =
-          parent_ns == nullptr ? nullptr : parent_ns->get_native_bridge_ns();
+      native_bridge_namespace_t* native_bridge_parent_namespace;
+      if (parent_ns != nullptr) {
+        native_bridge_parent_namespace = parent_ns->get_native_bridge_ns();
+      } else {
+        native_bridge_parent_namespace = NativeBridgeGetExportedNamespace(kPlatformNamespaceName);
+        if (native_bridge_parent_namespace == nullptr) {
+          native_bridge_parent_namespace = NativeBridgeGetExportedNamespace(kDefaultNamespaceName);
+        }
+      }
+
       native_bridge_namespace_t* ns = NativeBridgeCreateNamespace(namespace_name,
                                                                   nullptr,
                                                                   library_path.c_str(),
@@ -408,6 +472,14 @@
       }
     }
 
+    // Remove the public libs in the runtime namespace.
+    // These libs are listed in public.android.txt, but we don't want the rest of android
+    // in default namespace to dlopen the libs.
+    // For example, libicuuc.so is exposed to classloader namespace from runtime namespace.
+    // Unfortunately, it does not have stable C symbols, and default namespace should only use
+    // stable symbols in libandroidicu.so. http://b/120786417
+    removePublicLibsIfExistsInRuntimeApex(sonames);
+
     // android_init_namespaces() expects all the public libraries
     // to be loaded so that they can be found by soname alone.
     //
@@ -502,6 +574,27 @@
     }
   }
 
+  /**
+   * Remove the public libs in runtime namespace
+   */
+  void removePublicLibsIfExistsInRuntimeApex(std::vector<std::string>& sonames) {
+    for (const std::string& lib_name : kRuntimePublicLibraries) {
+      std::string path(kRuntimeApexLibPath);
+      path.append("/").append(lib_name);
+
+      struct stat s;
+      // Do nothing if the path in /apex does not exist.
+      // Runtime APEX must be mounted since libnativeloader is in the same APEX
+      if (stat(path.c_str(), &s) != 0) {
+        continue;
+      }
+
+      auto it = std::find(sonames.begin(), sonames.end(), lib_name);
+      if (it != sonames.end()) {
+        sonames.erase(it);
+      }
+    }
+  }
 
   bool ReadConfig(const std::string& configFile, std::vector<std::string>* sonames,
                   const std::function<bool(const std::string& /* soname */,
@@ -603,6 +696,28 @@
     return nullptr;
   }
 
+  ApkOrigin GetApkOriginFromDexPath(JNIEnv* env, jstring dex_path) {
+    ApkOrigin apk_origin = APK_ORIGIN_DEFAULT;
+
+    if (dex_path != nullptr) {
+      ScopedUtfChars dex_path_utf_chars(env, dex_path);
+
+      if (std::regex_search(dex_path_utf_chars.c_str(), kVendorDexPathRegex)) {
+        apk_origin = APK_ORIGIN_VENDOR;
+      }
+
+      if (std::regex_search(dex_path_utf_chars.c_str(), kProductDexPathRegex)) {
+        LOG_ALWAYS_FATAL_IF(apk_origin == APK_ORIGIN_VENDOR,
+                            "Dex path contains both vendor and product partition : %s",
+                            dex_path_utf_chars.c_str());
+
+        apk_origin = APK_ORIGIN_PRODUCT;
+      }
+    }
+
+    return apk_origin;
+  }
+
   bool initialized_;
   std::list<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
   std::string system_public_libraries_;
@@ -633,31 +748,20 @@
 #endif
 }
 
-jstring CreateClassLoaderNamespace(JNIEnv* env,
-                                   int32_t target_sdk_version,
-                                   jobject class_loader,
-                                   bool is_shared,
-                                   bool is_for_vendor,
-                                   jstring library_path,
+jstring CreateClassLoaderNamespace(JNIEnv* env, int32_t target_sdk_version, jobject class_loader,
+                                   bool is_shared, jstring dex_path, jstring library_path,
                                    jstring permitted_path) {
 #if defined(__ANDROID__)
   std::lock_guard<std::mutex> guard(g_namespaces_mutex);
 
   std::string error_msg;
-  bool success = g_namespaces->Create(env,
-                                      target_sdk_version,
-                                      class_loader,
-                                      is_shared,
-                                      is_for_vendor,
-                                      library_path,
-                                      permitted_path,
-                                      &error_msg) != nullptr;
+  bool success = g_namespaces->Create(env, target_sdk_version, class_loader, is_shared, dex_path,
+                                      library_path, permitted_path, &error_msg) != nullptr;
   if (!success) {
     return env->NewStringUTF(error_msg.c_str());
   }
 #else
-  UNUSED(env, target_sdk_version, class_loader, is_shared, is_for_vendor,
-         library_path, permitted_path);
+  UNUSED(env, target_sdk_version, class_loader, is_shared, dex_path, library_path, permitted_path);
 #endif
   return nullptr;
 }
@@ -722,8 +826,7 @@
     // In this case we create an isolated not-shared namespace for it.
     std::string create_error_msg;
     if ((ns = g_namespaces->Create(env, target_sdk_version, class_loader, false /* is_shared */,
-                                   false /* is_for_vendor */, library_path, nullptr,
-                                   &create_error_msg)) == nullptr) {
+                                   nullptr, library_path, nullptr, &create_error_msg)) == nullptr) {
       *error_msg = strdup(create_error_msg.c_str());
       return nullptr;
     }
diff --git a/libnativeloader/native_loader_lazy.cpp b/libnativeloader/native_loader_lazy.cpp
index 11ecc43..2eb1203 100644
--- a/libnativeloader/native_loader_lazy.cpp
+++ b/libnativeloader/native_loader_lazy.cpp
@@ -50,10 +50,10 @@
 }
 
 jstring CreateClassLoaderNamespace(JNIEnv* env, int32_t target_sdk_version, jobject class_loader,
-                                   bool is_shared, bool is_for_vendor, jstring library_path,
+                                   bool is_shared, jstring dex_path, jstring library_path,
                                    jstring permitted_path) {
   static auto f = GET_FUNC_PTR(CreateClassLoaderNamespace);
-  return f(env, target_sdk_version, class_loader, is_shared, is_for_vendor, library_path,
+  return f(env, target_sdk_version, class_loader, is_shared, dex_path, library_path,
            permitted_path);
 }
 
diff --git a/libpackagelistparser/include/packagelistparser/packagelistparser.h b/libpackagelistparser/include/packagelistparser/packagelistparser.h
index 8bcc1e2..3cb6b9a 100644
--- a/libpackagelistparser/include/packagelistparser/packagelistparser.h
+++ b/libpackagelistparser/include/packagelistparser/packagelistparser.h
@@ -54,6 +54,7 @@
     gid_list gids;
     void *private_data;
     bool profileable_from_shell;
+    long version_code;
 };
 
 /**
diff --git a/libpackagelistparser/packagelistparser.c b/libpackagelistparser/packagelistparser.c
index 4ce2363..edc533c 100644
--- a/libpackagelistparser/packagelistparser.c
+++ b/libpackagelistparser/packagelistparser.c
@@ -239,6 +239,15 @@
 
             pkg_info->profileable_from_shell = (bool)tmp;
         }
+        cur = strsep(&next, " \t\r\n");
+        if (cur) {
+            tmp = strtoul(cur, &endptr, 10);
+            if (*endptr != '\0') {
+                errmsg = "Could not convert field \"versionCode\" to integer value";
+                goto err;
+            }
+            pkg_info->version_code = tmp;
+        }
 
         rc = callback(pkg_info, userdata);
         if (rc == false) {
diff --git a/libprocessgroup/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp
index d094811..b3b497f 100644
--- a/libprocessgroup/cgroup_map.cpp
+++ b/libprocessgroup/cgroup_map.cpp
@@ -45,6 +45,7 @@
 using android::base::unique_fd;
 
 static constexpr const char* CGROUPS_DESC_FILE = "/etc/cgroups.json";
+static constexpr const char* CGROUPS_DESC_VENDOR_FILE = "/vendor/etc/cgroups.json";
 
 static constexpr const char* CGROUP_PROCS_FILE = "/cgroup.procs";
 static constexpr const char* CGROUP_TASKS_FILE = "/tasks";
@@ -110,12 +111,13 @@
     return true;
 }
 
-static bool ReadDescriptors(std::map<std::string, CgroupDescriptor>* descriptors) {
+static bool ReadDescriptorsFromFile(const std::string& file_name,
+                                    std::map<std::string, CgroupDescriptor>* descriptors) {
     std::vector<CgroupDescriptor> result;
     std::string json_doc;
 
-    if (!android::base::ReadFileToString(CGROUPS_DESC_FILE, &json_doc)) {
-        LOG(ERROR) << "Failed to read task profiles from " << CGROUPS_DESC_FILE;
+    if (!android::base::ReadFileToString(file_name, &json_doc)) {
+        PLOG(ERROR) << "Failed to read task profiles from " << file_name;
         return false;
     }
 
@@ -130,21 +132,46 @@
         const Json::Value& cgroups = root["Cgroups"];
         for (Json::Value::ArrayIndex i = 0; i < cgroups.size(); ++i) {
             std::string name = cgroups[i]["Controller"].asString();
-            descriptors->emplace(std::make_pair(
-                    name,
-                    CgroupDescriptor(1, name, cgroups[i]["Path"].asString(),
+            auto iter = descriptors->find(name);
+            if (iter == descriptors->end()) {
+                descriptors->emplace(name, CgroupDescriptor(1, name, cgroups[i]["Path"].asString(),
                                      std::strtoul(cgroups[i]["Mode"].asString().c_str(), 0, 8),
-                                     cgroups[i]["UID"].asString(), cgroups[i]["GID"].asString())));
+                                     cgroups[i]["UID"].asString(), cgroups[i]["GID"].asString()));
+            } else {
+                iter->second = CgroupDescriptor(1, name, cgroups[i]["Path"].asString(),
+                                     std::strtoul(cgroups[i]["Mode"].asString().c_str(), 0, 8),
+                                     cgroups[i]["UID"].asString(), cgroups[i]["GID"].asString());
+            }
         }
     }
 
     if (root.isMember("Cgroups2")) {
         const Json::Value& cgroups2 = root["Cgroups2"];
-        descriptors->emplace(std::make_pair(
-                CGROUPV2_CONTROLLER_NAME,
-                CgroupDescriptor(2, CGROUPV2_CONTROLLER_NAME, cgroups2["Path"].asString(),
+        auto iter = descriptors->find(CGROUPV2_CONTROLLER_NAME);
+        if (iter == descriptors->end()) {
+            descriptors->emplace(CGROUPV2_CONTROLLER_NAME, CgroupDescriptor(2, CGROUPV2_CONTROLLER_NAME, cgroups2["Path"].asString(),
                                  std::strtoul(cgroups2["Mode"].asString().c_str(), 0, 8),
-                                 cgroups2["UID"].asString(), cgroups2["GID"].asString())));
+                                 cgroups2["UID"].asString(), cgroups2["GID"].asString()));
+        } else {
+            iter->second = CgroupDescriptor(2, CGROUPV2_CONTROLLER_NAME, cgroups2["Path"].asString(),
+                                 std::strtoul(cgroups2["Mode"].asString().c_str(), 0, 8),
+                                 cgroups2["UID"].asString(), cgroups2["GID"].asString());
+        }
+    }
+
+    return true;
+}
+
+static bool ReadDescriptors(std::map<std::string, CgroupDescriptor>* descriptors) {
+    // load system cgroup descriptors
+    if (!ReadDescriptorsFromFile(CGROUPS_DESC_FILE, descriptors)) {
+        return false;
+    }
+
+    // load vendor cgroup descriptors if the file exists
+    if (!access(CGROUPS_DESC_VENDOR_FILE, F_OK) &&
+        !ReadDescriptorsFromFile(CGROUPS_DESC_VENDOR_FILE, descriptors)) {
+        return false;
     }
 
     return true;
@@ -158,7 +185,7 @@
 
     // mkdir <path> [mode] [owner] [group]
     if (!Mkdir(controller->path(), descriptor.mode(), descriptor.uid(), descriptor.gid())) {
-        PLOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
+        LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
         return false;
     }
 
@@ -262,7 +289,7 @@
     std::string file_name = StringPrintf("/proc/%d/cgroup", tid);
     std::string content;
     if (!android::base::ReadFileToString(file_name, &content)) {
-        LOG(ERROR) << "Failed to read " << file_name;
+        PLOG(ERROR) << "Failed to read " << file_name;
         return false;
     }
 
@@ -296,7 +323,7 @@
 
 CgroupMap::CgroupMap() : cg_file_data_(nullptr), cg_file_size_(0) {
     if (!LoadRcFile()) {
-        PLOG(ERROR) << "CgroupMap::LoadRcFile called for [" << getpid() << "] failed";
+        LOG(ERROR) << "CgroupMap::LoadRcFile called for [" << getpid() << "] failed";
     }
 }
 
@@ -309,8 +336,10 @@
 }
 
 CgroupMap& CgroupMap::GetInstance() {
-    static CgroupMap instance;
-    return instance;
+    // Deliberately leak this object to avoid a race between destruction on
+    // process exit and concurrent access from another thread.
+    static auto* instance = new CgroupMap;
+    return *instance;
 }
 
 bool CgroupMap::LoadRcFile() {
@@ -333,27 +362,42 @@
         return false;
     }
 
-    cg_file_size_ = sb.st_size;
-    if (cg_file_size_ < sizeof(CgroupFile)) {
-        PLOG(ERROR) << "Invalid file format " << cgroup_rc_path;
+    size_t file_size = sb.st_size;
+    if (file_size < sizeof(CgroupFile)) {
+        LOG(ERROR) << "Invalid file format " << cgroup_rc_path;
         return false;
     }
 
-    cg_file_data_ = (CgroupFile*)mmap(nullptr, cg_file_size_, PROT_READ, MAP_SHARED, fd, 0);
-    if (cg_file_data_ == MAP_FAILED) {
+    CgroupFile* file_data = (CgroupFile*)mmap(nullptr, file_size, PROT_READ, MAP_SHARED, fd, 0);
+    if (file_data == MAP_FAILED) {
         PLOG(ERROR) << "Failed to mmap " << cgroup_rc_path;
         return false;
     }
 
-    if (cg_file_data_->version_ != CgroupFile::FILE_CURR_VERSION) {
-        PLOG(ERROR) << cgroup_rc_path << " file version mismatch";
+    if (file_data->version_ != CgroupFile::FILE_CURR_VERSION) {
+        LOG(ERROR) << cgroup_rc_path << " file version mismatch";
+        munmap(file_data, file_size);
         return false;
     }
 
+    if (file_size != sizeof(CgroupFile) + file_data->controller_count_ * sizeof(CgroupController)) {
+        LOG(ERROR) << cgroup_rc_path << " file has invalid size";
+        munmap(file_data, file_size);
+        return false;
+    }
+
+    cg_file_data_ = file_data;
+    cg_file_size_ = file_size;
+
     return true;
 }
 
-void CgroupMap::Print() {
+void CgroupMap::Print() const {
+    if (!cg_file_data_) {
+        LOG(ERROR) << "CgroupMap::Print called for [" << getpid()
+                   << "] failed, RC file was not initialized properly";
+        return;
+    }
     LOG(INFO) << "File version = " << cg_file_data_->version_;
     LOG(INFO) << "File controller count = " << cg_file_data_->controller_count_;
 
@@ -370,7 +414,7 @@
 
     // load cgroups.json file
     if (!ReadDescriptors(&descriptors)) {
-        PLOG(ERROR) << "Failed to load cgroup description file";
+        LOG(ERROR) << "Failed to load cgroup description file";
         return false;
     }
 
@@ -385,7 +429,7 @@
 
     // mkdir <CGROUPS_RC_DIR> 0711 system system
     if (!Mkdir(CGROUPS_RC_DIR, 0711, "system", "system")) {
-        PLOG(ERROR) << "Failed to create directory for <CGROUPS_RC_FILE> file";
+        LOG(ERROR) << "Failed to create directory for <CGROUPS_RC_FILE> file";
         return false;
     }
 
@@ -401,7 +445,7 @@
     std::string cgroup_rc_path = StringPrintf("%s/%s", CGROUPS_RC_DIR, CGROUPS_RC_FILE);
     // chmod 0644 <cgroup_rc_path>
     if (fchmodat(AT_FDCWD, cgroup_rc_path.c_str(), 0644, AT_SYMLINK_NOFOLLOW) < 0) {
-        LOG(ERROR) << "fchmodat() failed";
+        PLOG(ERROR) << "fchmodat() failed";
         return false;
     }
 
@@ -410,6 +454,8 @@
 
 const CgroupController* CgroupMap::FindController(const std::string& name) const {
     if (!cg_file_data_) {
+        LOG(ERROR) << "CgroupMap::FindController called for [" << getpid()
+                   << "] failed, RC file was not initialized properly";
         return nullptr;
     }
 
diff --git a/libprocessgroup/cgroup_map.h b/libprocessgroup/cgroup_map.h
index ba2caf7..1c355cd 100644
--- a/libprocessgroup/cgroup_map.h
+++ b/libprocessgroup/cgroup_map.h
@@ -92,5 +92,5 @@
     ~CgroupMap();
 
     bool LoadRcFile();
-    void Print();
+    void Print() const;
 };
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index e9dec12..8884650 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -170,8 +170,9 @@
     return ret;
 }
 
-static void RemoveUidProcessGroups(const std::string& uid_path) {
+static bool RemoveUidProcessGroups(const std::string& uid_path) {
     std::unique_ptr<DIR, decltype(&closedir)> uid(opendir(uid_path.c_str()), closedir);
+    bool empty = true;
     if (uid != NULL) {
         dirent* dir;
         while ((dir = readdir(uid.get())) != nullptr) {
@@ -185,9 +186,15 @@
 
             auto path = StringPrintf("%s/%s", uid_path.c_str(), dir->d_name);
             LOG(VERBOSE) << "Removing " << path;
-            if (rmdir(path.c_str()) == -1) PLOG(WARNING) << "Failed to remove " << path;
+            if (rmdir(path.c_str()) == -1) {
+                if (errno != EBUSY) {
+                    PLOG(WARNING) << "Failed to remove " << path;
+                }
+                empty = false;
+            }
         }
     }
+    return empty;
 }
 
 void removeAllProcessGroups() {
@@ -200,7 +207,7 @@
         cgroups.push_back(path);
     }
     if (CgroupGetControllerPath("memory", &path)) {
-        cgroups.push_back(path);
+        cgroups.push_back(path + "/apps");
     }
 
     for (std::string cgroup_root_path : cgroups) {
@@ -219,9 +226,14 @@
                 }
 
                 auto path = StringPrintf("%s/%s", cgroup_root_path.c_str(), dir->d_name);
-                RemoveUidProcessGroups(path);
+                if (!RemoveUidProcessGroups(path)) {
+                    LOG(VERBOSE) << "Skip removing " << path;
+                    continue;
+                }
                 LOG(VERBOSE) << "Removing " << path;
-                if (rmdir(path.c_str()) == -1) PLOG(WARNING) << "Failed to remove " << path;
+                if (rmdir(path.c_str()) == -1 && errno != EBUSY) {
+                    PLOG(WARNING) << "Failed to remove " << path;
+                }
             }
         }
     }
@@ -249,6 +261,10 @@
     auto path = ConvertUidPidToPath(cgroup, uid, initialPid) + PROCESSGROUP_CGROUP_PROCS_FILE;
     std::unique_ptr<FILE, decltype(&fclose)> fd(fopen(path.c_str(), "re"), fclose);
     if (!fd) {
+        if (errno == ENOENT) {
+            // This happens when process is already dead
+            return 0;
+        }
         PLOG(WARNING) << "Failed to open process cgroup uid " << uid << " pid " << initialPid;
         return -1;
     }
@@ -293,7 +309,7 @@
         LOG(VERBOSE) << "Killing process group " << -pgid << " in uid " << uid
                      << " as part of process cgroup " << initialPid;
 
-        if (kill(-pgid, signal) == -1) {
+        if (kill(-pgid, signal) == -1 && errno != ESRCH) {
             PLOG(WARNING) << "kill(" << -pgid << ", " << signal << ") failed";
         }
     }
@@ -303,7 +319,7 @@
         LOG(VERBOSE) << "Killing pid " << pid << " in uid " << uid << " as part of process cgroup "
                      << initialPid;
 
-        if (kill(pid, signal) == -1) {
+        if (kill(pid, signal) == -1 && errno != ESRCH) {
             PLOG(WARNING) << "kill(" << pid << ", " << signal << ") failed";
         }
     }
@@ -317,6 +333,7 @@
 
     CgroupGetControllerPath("cpuacct", &cpuacct_path);
     CgroupGetControllerPath("memory", &memory_path);
+    memory_path += "/apps";
 
     const char* cgroup =
             (!access(ConvertUidPidToPath(cpuacct_path.c_str(), uid, initialPid).c_str(), F_OK))
@@ -380,6 +397,7 @@
     std::string cgroup;
     if (isMemoryCgroupSupported() && (memControl || UsePerAppMemcg())) {
         CgroupGetControllerPath("memory", &cgroup);
+        cgroup += "/apps";
     } else {
         CgroupGetControllerPath("cpuacct", &cgroup);
     }
diff --git a/libprocessgroup/profiles/Android.bp b/libprocessgroup/profiles/Android.bp
new file mode 100644
index 0000000..e05a690
--- /dev/null
+++ b/libprocessgroup/profiles/Android.bp
@@ -0,0 +1,108 @@
+// Copyright (C) 2019 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.
+
+prebuilt_etc {
+    name: "cgroups.json",
+    src: "cgroups.json",
+}
+
+prebuilt_etc {
+    name: "cgroups.recovery.json",
+    filename: "cgroups.json",
+    recovery: true,
+    src: "cgroups.recovery.json",
+}
+
+prebuilt_etc {
+    name: "task_profiles.json",
+    src: "task_profiles.json",
+}
+
+cc_defaults {
+    name: "libprocessgroup_test_defaults",
+    cflags: [
+        "-Wall",
+        "-Werror",
+
+        // Needed for headers from libprotobuf.
+        "-Wno-unused-parameter",
+    ],
+}
+
+cc_library_static {
+    name: "libprocessgroup_proto",
+    host_supported: true,
+    defaults: ["libprocessgroup_test_defaults"],
+    srcs: [
+        "cgroups.proto",
+        "task_profiles.proto",
+    ],
+    proto: {
+        type: "full",
+        export_proto_headers: true,
+    },
+}
+
+cc_test_host {
+    name: "libprocessgroup_proto_test",
+    defaults: ["libprocessgroup_test_defaults"],
+    srcs: [
+        "test.cpp",
+    ],
+    static_libs: [
+        "libbase",
+        "libgmock",
+        "liblog",
+        "libjsoncpp",
+        "libjsonpbverify",
+        "libjsonpbparse",
+        "libprocessgroup_proto",
+    ],
+    shared_libs: [
+        "libprotobuf-cpp-full",
+    ],
+    data: [
+        "cgroups.json",
+        "cgroups.recovery.json",
+        "task_profiles.json",
+    ],
+    test_suites: [
+        "general-tests",
+    ],
+}
+
+cc_test {
+    name: "vts_processgroup_validate_test",
+    defaults: ["libprocessgroup_test_defaults"],
+    srcs: [
+        "test_vendor.cpp",
+    ],
+    static_libs: [
+        "libgmock",
+        "libjsonpbverify",
+        "libjsonpbparse",
+        "libprocessgroup_proto",
+    ],
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libjsoncpp",
+        "libprotobuf-cpp-full",
+    ],
+    target: {
+        android: {
+            test_config: "vts_processgroup_validate_test.xml",
+        },
+    },
+}
diff --git a/libprocessgroup/profiles/Android.mk b/libprocessgroup/profiles/Android.mk
new file mode 100644
index 0000000..eab96d4
--- /dev/null
+++ b/libprocessgroup/profiles/Android.mk
@@ -0,0 +1,21 @@
+#
+# Copyright (C) 2019 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 := VtsProcessgroupValidateTest
+-include test/vts/tools/build/Android.host_config.mk
diff --git a/libprocessgroup/profiles/TEST_MAPPING b/libprocessgroup/profiles/TEST_MAPPING
new file mode 100644
index 0000000..5ff4112
--- /dev/null
+++ b/libprocessgroup/profiles/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+  "presubmit": [
+    {
+      "name": "libprocessgroup_proto_test",
+      "host": true
+    }
+  ]
+}
diff --git a/rootdir/cgroups.json b/libprocessgroup/profiles/cgroups.json
similarity index 84%
rename from rootdir/cgroups.json
rename to libprocessgroup/profiles/cgroups.json
index aa71956..5871a63 100644
--- a/rootdir/cgroups.json
+++ b/libprocessgroup/profiles/cgroups.json
@@ -1,6 +1,13 @@
 {
   "Cgroups": [
     {
+      "Controller": "blkio",
+      "Path": "/dev/blkio",
+      "Mode": "0755",
+      "UID": "system",
+      "GID": "system"
+    },
+    {
       "Controller": "cpu",
       "Path": "/dev/cpuctl",
       "Mode": "0755",
diff --git a/libprocessgroup/profiles/cgroups.proto b/libprocessgroup/profiles/cgroups.proto
new file mode 100644
index 0000000..f4070c5
--- /dev/null
+++ b/libprocessgroup/profiles/cgroups.proto
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+syntax = "proto3";
+
+package android.profiles;
+
+// Next: 3
+message Cgroups {
+    repeated Cgroup cgroups = 1 [json_name = "Cgroups"];
+    Cgroups2 cgroups2 = 2 [json_name = "Cgroups2"];
+}
+
+// Next: 6
+message Cgroup {
+    string controller = 1 [json_name = "Controller"];
+    string path = 2 [json_name = "Path"];
+    string mode = 3 [json_name = "Mode"];
+    string uid = 4 [json_name = "UID"];
+    string gid = 5 [json_name = "GID"];
+}
+
+// Next: 5
+message Cgroups2 {
+    string path = 1 [json_name = "Path"];
+    string mode = 2 [json_name = "Mode"];
+    string uid = 3 [json_name = "UID"];
+    string gid = 4 [json_name = "GID"];
+}
diff --git a/rootdir/cgroups.recovery.json b/libprocessgroup/profiles/cgroups.recovery.json
similarity index 100%
rename from rootdir/cgroups.recovery.json
rename to libprocessgroup/profiles/cgroups.recovery.json
diff --git a/libprocessgroup/profiles/cgroups_test.h b/libprocessgroup/profiles/cgroups_test.h
new file mode 100644
index 0000000..1309957
--- /dev/null
+++ b/libprocessgroup/profiles/cgroups_test.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <gmock/gmock.h>
+#include <jsonpb/json_schema_test.h>
+
+#include "cgroups.pb.h"
+
+using ::testing::MatchesRegex;
+
+namespace android {
+namespace profiles {
+
+class CgroupsTest : public jsonpb::JsonSchemaTest {
+  public:
+    void SetUp() override {
+        JsonSchemaTest::SetUp();
+        cgroups_ = static_cast<Cgroups*>(message());
+    }
+    Cgroups* cgroups_;
+};
+
+TEST_P(CgroupsTest, CgroupRequiredFields) {
+    for (int i = 0; i < cgroups_->cgroups_size(); ++i) {
+        auto&& cgroup = cgroups_->cgroups(i);
+        EXPECT_FALSE(cgroup.controller().empty())
+                << "No controller name for cgroup #" << i << " in " << file_path_;
+        EXPECT_FALSE(cgroup.path().empty()) << "No path for cgroup #" << i << " in " << file_path_;
+    }
+}
+
+TEST_P(CgroupsTest, Cgroup2RequiredFields) {
+    if (cgroups_->has_cgroups2()) {
+        EXPECT_FALSE(cgroups_->cgroups2().path().empty())
+                << "No path for cgroup2 in " << file_path_;
+    }
+}
+
+// "Mode" field must be in the format of "0xxx".
+static inline constexpr const char* REGEX_MODE = "(0[0-7]{3})?";
+TEST_P(CgroupsTest, CgroupMode) {
+    for (int i = 0; i < cgroups_->cgroups_size(); ++i) {
+        EXPECT_THAT(cgroups_->cgroups(i).mode(), MatchesRegex(REGEX_MODE))
+                << "For cgroup controller #" << i << " in " << file_path_;
+    }
+}
+
+TEST_P(CgroupsTest, Cgroup2Mode) {
+    EXPECT_THAT(cgroups_->cgroups2().mode(), MatchesRegex(REGEX_MODE))
+            << "For cgroups2 in " << file_path_;
+}
+
+}  // namespace profiles
+}  // namespace android
diff --git a/rootdir/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
similarity index 60%
rename from rootdir/task_profiles.json
rename to libprocessgroup/profiles/task_profiles.json
index 5a090c5..74a39cd 100644
--- a/rootdir/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -56,10 +56,10 @@
   "Profiles": [
     {
       "Name": "HighEnergySaving",
-      "Actions" : [
+      "Actions": [
         {
-          "Name" : "JoinCgroup",
-          "Params" :
+          "Name": "JoinCgroup",
+          "Params":
           {
             "Controller": "schedtune",
             "Path": "background"
@@ -69,10 +69,10 @@
     },
     {
       "Name": "NormalPerformance",
-      "Actions" : [
+      "Actions": [
         {
-          "Name" : "JoinCgroup",
-          "Params" :
+          "Name": "JoinCgroup",
+          "Params":
           {
             "Controller": "schedtune",
             "Path": ""
@@ -82,10 +82,10 @@
     },
     {
       "Name": "HighPerformance",
-      "Actions" : [
+      "Actions": [
         {
-          "Name" : "JoinCgroup",
-          "Params" :
+          "Name": "JoinCgroup",
+          "Params":
           {
             "Controller": "schedtune",
             "Path": "foreground"
@@ -95,10 +95,10 @@
     },
     {
       "Name": "MaxPerformance",
-      "Actions" : [
+      "Actions": [
         {
-          "Name" : "JoinCgroup",
-          "Params" :
+          "Name": "JoinCgroup",
+          "Params":
           {
             "Controller": "schedtune",
             "Path": "top-app"
@@ -108,10 +108,10 @@
     },
     {
       "Name": "RealtimePerformance",
-      "Actions" : [
+      "Actions": [
         {
-          "Name" : "JoinCgroup",
-          "Params" :
+          "Name": "JoinCgroup",
+          "Params":
           {
             "Controller": "schedtune",
             "Path": "rt"
@@ -122,26 +122,26 @@
 
     {
       "Name": "CpuPolicySpread",
-      "Actions" : [
+      "Actions": [
         {
-          "Name" : "SetAttribute",
-          "Params" :
+          "Name": "SetAttribute",
+          "Params":
           {
-            "Name" : "STunePreferIdle",
-            "Value" : "1"
+            "Name": "STunePreferIdle",
+            "Value": "1"
           }
         }
       ]
     },
     {
       "Name": "CpuPolicyPack",
-      "Actions" : [
+      "Actions": [
         {
-          "Name" : "SetAttribute",
-          "Params" :
+          "Name": "SetAttribute",
+          "Params":
           {
-            "Name" : "STunePreferIdle",
-            "Value" : "0"
+            "Name": "STunePreferIdle",
+            "Value": "0"
           }
         }
       ]
@@ -149,10 +149,10 @@
 
     {
       "Name": "VrKernelCapacity",
-      "Actions" : [
+      "Actions": [
         {
-          "Name" : "JoinCgroup",
-          "Params" :
+          "Name": "JoinCgroup",
+          "Params":
           {
             "Controller": "cpuset",
             "Path": ""
@@ -162,10 +162,10 @@
     },
     {
       "Name": "VrServiceCapacityLow",
-      "Actions" : [
+      "Actions": [
         {
-          "Name" : "JoinCgroup",
-          "Params" :
+          "Name": "JoinCgroup",
+          "Params":
           {
             "Controller": "cpuset",
             "Path": "system/background"
@@ -175,10 +175,10 @@
     },
     {
       "Name": "VrServiceCapacityNormal",
-      "Actions" : [
+      "Actions": [
         {
-          "Name" : "JoinCgroup",
-          "Params" :
+          "Name": "JoinCgroup",
+          "Params":
           {
             "Controller": "cpuset",
             "Path": "system"
@@ -188,10 +188,10 @@
     },
     {
       "Name": "VrServiceCapacityHigh",
-      "Actions" : [
+      "Actions": [
         {
-          "Name" : "JoinCgroup",
-          "Params" :
+          "Name": "JoinCgroup",
+          "Params":
           {
             "Controller": "cpuset",
             "Path": "system/performance"
@@ -201,10 +201,10 @@
     },
     {
       "Name": "VrProcessCapacityLow",
-      "Actions" : [
+      "Actions": [
         {
-          "Name" : "JoinCgroup",
-          "Params" :
+          "Name": "JoinCgroup",
+          "Params":
           {
             "Controller": "cpuset",
             "Path": "application/background"
@@ -214,10 +214,10 @@
     },
     {
       "Name": "VrProcessCapacityNormal",
-      "Actions" : [
+      "Actions": [
         {
-          "Name" : "JoinCgroup",
-          "Params" :
+          "Name": "JoinCgroup",
+          "Params":
           {
             "Controller": "cpuset",
             "Path": "application"
@@ -227,10 +227,10 @@
     },
     {
       "Name": "VrProcessCapacityHigh",
-      "Actions" : [
+      "Actions": [
         {
-          "Name" : "JoinCgroup",
-          "Params" :
+          "Name": "JoinCgroup",
+          "Params":
           {
             "Controller": "cpuset",
             "Path": "application/performance"
@@ -241,10 +241,10 @@
 
     {
       "Name": "ProcessCapacityLow",
-      "Actions" : [
+      "Actions": [
         {
-          "Name" : "JoinCgroup",
-          "Params" :
+          "Name": "JoinCgroup",
+          "Params":
           {
             "Controller": "cpuset",
             "Path": "background"
@@ -254,10 +254,10 @@
     },
     {
       "Name": "ProcessCapacityNormal",
-      "Actions" : [
+      "Actions": [
         {
-          "Name" : "JoinCgroup",
-          "Params" :
+          "Name": "JoinCgroup",
+          "Params":
           {
             "Controller": "cpuset",
             "Path": ""
@@ -267,10 +267,10 @@
     },
     {
       "Name": "ProcessCapacityHigh",
-      "Actions" : [
+      "Actions": [
         {
-          "Name" : "JoinCgroup",
-          "Params" :
+          "Name": "JoinCgroup",
+          "Params":
           {
             "Controller": "cpuset",
             "Path": "foreground"
@@ -280,10 +280,10 @@
     },
     {
       "Name": "ProcessCapacityMax",
-      "Actions" : [
+      "Actions": [
         {
-          "Name" : "JoinCgroup",
-          "Params" :
+          "Name": "JoinCgroup",
+          "Params":
           {
             "Controller": "cpuset",
             "Path": "top-app"
@@ -294,10 +294,10 @@
 
     {
       "Name": "ServiceCapacityLow",
-      "Actions" : [
+      "Actions": [
         {
-          "Name" : "JoinCgroup",
-          "Params" :
+          "Name": "JoinCgroup",
+          "Params":
           {
             "Controller": "cpuset",
             "Path": "system-background"
@@ -307,10 +307,10 @@
     },
     {
       "Name": "ServiceCapacityRestricted",
-      "Actions" : [
+      "Actions": [
         {
-          "Name" : "JoinCgroup",
-          "Params" :
+          "Name": "JoinCgroup",
+          "Params":
           {
             "Controller": "cpuset",
             "Path": "restricted"
@@ -321,10 +321,10 @@
 
     {
       "Name": "CameraServiceCapacity",
-      "Actions" : [
+      "Actions": [
         {
-          "Name" : "JoinCgroup",
-          "Params" :
+          "Name": "JoinCgroup",
+          "Params":
           {
             "Controller": "cpuset",
             "Path": "camera-daemon"
@@ -334,11 +334,64 @@
     },
 
     {
-      "Name": "TimerSlackHigh",
-      "Actions" : [
+      "Name": "LowIoPriority",
+      "Actions": [
         {
-          "Name" : "SetTimerSlack",
-          "Params" :
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "blkio",
+            "Path": "background"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "NormalIoPriority",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "blkio",
+            "Path": ""
+          }
+        }
+      ]
+    },
+    {
+      "Name": "HighIoPriority",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "blkio",
+            "Path": ""
+          }
+        }
+      ]
+    },
+    {
+      "Name": "MaxIoPriority",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "blkio",
+            "Path": ""
+          }
+        }
+      ]
+    },
+
+    {
+      "Name": "TimerSlackHigh",
+      "Actions": [
+        {
+          "Name": "SetTimerSlack",
+          "Params":
           {
             "Slack": "40000000"
           }
@@ -347,10 +400,10 @@
     },
     {
       "Name": "TimerSlackNormal",
-      "Actions" : [
+      "Actions": [
         {
-          "Name" : "SetTimerSlack",
-          "Params" :
+          "Name": "SetTimerSlack",
+          "Params":
           {
             "Slack": "50000"
           }
@@ -360,26 +413,26 @@
 
     {
       "Name": "PerfBoost",
-      "Actions" : [
+      "Actions": [
         {
-          "Name" : "SetClamps",
-          "Params" :
+          "Name": "SetClamps",
+          "Params":
           {
-            "Boost" : "50%",
-            "Clamp" : "0"
+            "Boost": "50%",
+            "Clamp": "0"
           }
         }
       ]
     },
     {
       "Name": "PerfClamp",
-      "Actions" : [
+      "Actions": [
         {
-          "Name" : "SetClamps",
-          "Params" :
+          "Name": "SetClamps",
+          "Params":
           {
-            "Boost" : "0",
-            "Clamp" : "30%"
+            "Boost": "0",
+            "Clamp": "30%"
           }
         }
       ]
@@ -387,21 +440,21 @@
 
     {
       "Name": "LowMemoryUsage",
-      "Actions" : [
+      "Actions": [
         {
-          "Name" : "SetAttribute",
-          "Params" :
+          "Name": "SetAttribute",
+          "Params":
           {
-            "Name" : "MemSoftLimit",
-            "Value" : "16MB"
+            "Name": "MemSoftLimit",
+            "Value": "16MB"
           }
         },
         {
-          "Name" : "SetAttribute",
-          "Params" :
+          "Name": "SetAttribute",
+          "Params":
           {
-            "Name" : "MemSwappiness",
-            "Value" : "150"
+            "Name": "MemSwappiness",
+            "Value": "150"
 
           }
         }
@@ -409,31 +462,31 @@
     },
     {
       "Name": "HighMemoryUsage",
-      "Actions" : [
+      "Actions": [
         {
-          "Name" : "SetAttribute",
-          "Params" :
+          "Name": "SetAttribute",
+          "Params":
           {
-            "Name" : "MemSoftLimit",
-            "Value" : "512MB"
+            "Name": "MemSoftLimit",
+            "Value": "512MB"
           }
         },
         {
-          "Name" : "SetAttribute",
-          "Params" :
+          "Name": "SetAttribute",
+          "Params":
           {
-            "Name" : "MemSwappiness",
-            "Value" : "100"
+            "Name": "MemSwappiness",
+            "Value": "100"
           }
         }
       ]
     },
     {
       "Name": "SystemMemoryProcess",
-      "Actions" : [
+      "Actions": [
         {
-          "Name" : "JoinCgroup",
-          "Params" :
+          "Name": "JoinCgroup",
+          "Params":
           {
             "Controller": "memory",
             "Path": "system"
diff --git a/libprocessgroup/profiles/task_profiles.proto b/libprocessgroup/profiles/task_profiles.proto
new file mode 100644
index 0000000..578f0d3
--- /dev/null
+++ b/libprocessgroup/profiles/task_profiles.proto
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+syntax = "proto3";
+
+package android.profiles;
+
+// Next: 3
+message TaskProfiles {
+    repeated Attribute attributes = 1 [json_name = "Attributes"];
+    repeated Profile profiles = 2 [json_name = "Profiles"];
+}
+
+// Next: 4
+message Attribute {
+    string name = 1 [json_name = "Name"];
+    string controller = 2 [json_name = "Controller"];
+    string file = 3 [json_name = "File"];
+}
+
+// Next: 3
+message Profile {
+    string name = 1 [json_name = "Name"];
+    repeated Action actions = 2 [json_name = "Actions"];
+}
+
+// Next: 3
+message Action {
+    string name = 1 [json_name = "Name"];
+    map<string, string> params = 2 [json_name = "Params"];
+}
diff --git a/libprocessgroup/profiles/task_profiles_test.h b/libprocessgroup/profiles/task_profiles_test.h
new file mode 100644
index 0000000..32f122d
--- /dev/null
+++ b/libprocessgroup/profiles/task_profiles_test.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <gmock/gmock.h>
+#include <jsonpb/json_schema_test.h>
+
+#include "task_profiles.pb.h"
+
+namespace android {
+namespace profiles {
+
+class TaskProfilesTest : public jsonpb::JsonSchemaTest {
+  public:
+    void SetUp() override {
+        JsonSchemaTest::SetUp();
+        task_profiles_ = static_cast<TaskProfiles*>(message());
+    }
+    TaskProfiles* task_profiles_;
+};
+
+TEST_P(TaskProfilesTest, AttributeRequiredFields) {
+    for (int i = 0; i < task_profiles_->attributes_size(); ++i) {
+        auto&& attribute = task_profiles_->attributes(i);
+        EXPECT_FALSE(attribute.name().empty())
+                << "No name for attribute #" << i << " in " << file_path_;
+        EXPECT_FALSE(attribute.controller().empty())
+                << "No controller for attribute #" << i << " in " << file_path_;
+        EXPECT_FALSE(attribute.file().empty())
+                << "No file for attribute #" << i << " in " << file_path_;
+    }
+}
+
+TEST_P(TaskProfilesTest, ProfileRequiredFields) {
+    for (int profile_idx = 0; profile_idx < task_profiles_->profiles_size(); ++profile_idx) {
+        auto&& profile = task_profiles_->profiles(profile_idx);
+        EXPECT_FALSE(profile.name().empty())
+                << "No name for profile #" << profile_idx << " in " << file_path_;
+        for (int action_idx = 0; action_idx < profile.actions_size(); ++action_idx) {
+            auto&& action = profile.actions(action_idx);
+            EXPECT_FALSE(action.name().empty())
+                    << "No name for profiles[" << profile_idx << "].actions[" << action_idx
+                    << "] in " << file_path_;
+        }
+    }
+}
+
+}  // namespace profiles
+}  // namespace android
diff --git a/libprocessgroup/profiles/test.cpp b/libprocessgroup/profiles/test.cpp
new file mode 100644
index 0000000..bc9aade
--- /dev/null
+++ b/libprocessgroup/profiles/test.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 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 <android-base/file.h>
+#include <gtest/gtest.h>
+#include <jsonpb/json_schema_test.h>
+
+#include "cgroups_test.h"
+#include "task_profiles_test.h"
+
+using namespace ::android::jsonpb;
+using ::android::base::GetExecutableDirectory;
+
+namespace android {
+namespace profiles {
+
+template <typename T>
+JsonSchemaTestConfigFactory MakeTestParam(const std::string& path) {
+    return jsonpb::MakeTestParam<T>(GetExecutableDirectory() + path);
+}
+
+// Test suite instantiations
+INSTANTIATE_TEST_SUITE_P(, JsonSchemaTest,
+                         ::testing::Values(MakeTestParam<Cgroups>("/cgroups.json"),
+                                           MakeTestParam<Cgroups>("/cgroups.recovery.json"),
+                                           MakeTestParam<TaskProfiles>("/task_profiles.json")));
+INSTANTIATE_TEST_SUITE_P(, CgroupsTest,
+                         ::testing::Values(MakeTestParam<Cgroups>("/cgroups.json"),
+                                           MakeTestParam<Cgroups>("/cgroups.recovery.json")));
+INSTANTIATE_TEST_SUITE_P(, TaskProfilesTest,
+                         ::testing::Values(MakeTestParam<TaskProfiles>("/task_profiles.json")));
+
+}  // namespace profiles
+}  // namespace android
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/libprocessgroup/profiles/test_vendor.cpp b/libprocessgroup/profiles/test_vendor.cpp
new file mode 100644
index 0000000..3ec7fcf
--- /dev/null
+++ b/libprocessgroup/profiles/test_vendor.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 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 <android-base/file.h>
+#include <gtest/gtest.h>
+#include <jsonpb/json_schema_test.h>
+
+#include "cgroups_test.h"
+#include "task_profiles_test.h"
+
+using ::android::base::GetExecutableDirectory;
+using namespace ::android::jsonpb;
+
+namespace android {
+namespace profiles {
+
+static constexpr const char* kVendorCgroups = "/vendor/etc/cgroups.json";
+static constexpr const char* kVendorTaskProfiles = "/vendor/etc/task_profiles.json";
+
+template <typename T>
+class TestConfig : public JsonSchemaTestConfig {
+  public:
+    TestConfig(const std::string& path) : file_path_(path){};
+    std::unique_ptr<google::protobuf::Message> CreateMessage() const override {
+        return std::make_unique<T>();
+    }
+    std::string file_path() const override { return file_path_; }
+    bool optional() const override {
+        // Ignore when vendor JSON files are missing.
+        return true;
+    }
+
+  private:
+    std::string file_path_;
+};
+
+template <typename T>
+JsonSchemaTestConfigFactory MakeTestParam(const std::string& path) {
+    return [path]() { return std::make_unique<TestConfig<T>>(path); };
+}
+
+INSTANTIATE_TEST_SUITE_P(VendorCgroups, JsonSchemaTest,
+                         ::testing::Values(MakeTestParam<Cgroups>(kVendorCgroups)));
+INSTANTIATE_TEST_SUITE_P(VendorCgroups, CgroupsTest,
+                         ::testing::Values(MakeTestParam<Cgroups>(kVendorCgroups)));
+
+INSTANTIATE_TEST_SUITE_P(VendorTaskProfiles, JsonSchemaTest,
+                         ::testing::Values(MakeTestParam<TaskProfiles>(kVendorTaskProfiles)));
+INSTANTIATE_TEST_SUITE_P(VendorTaskProfiles, TaskProfilesTest,
+                         ::testing::Values(MakeTestParam<TaskProfiles>(kVendorTaskProfiles)));
+
+}  // namespace profiles
+}  // namespace android
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/libprocessgroup/profiles/vts_processgroup_validate_test.xml b/libprocessgroup/profiles/vts_processgroup_validate_test.xml
new file mode 100644
index 0000000..21d29cd
--- /dev/null
+++ b/libprocessgroup/profiles/vts_processgroup_validate_test.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+<configuration description="Config for VtsProcessgroupValidateTest">
+    <option name="config-descriptor:metadata" key="plan" value="vts-treble" />
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
+        <option name="abort-on-push-failure" value="false"/>
+        <option name="push-group" value="HostDrivenTest.push"/>
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
+        <option name="test-module-name" value="VtsProcessgroupValidateTest"/>
+        <option name="binary-test-working-directory" value="_32bit::/data/nativetest/" />
+        <option name="binary-test-working-directory" value="_64bit::/data/nativetest64/" />
+        <option name="binary-test-source" value="_32bit::DATA/nativetest/vts_processgroup_validate_test/vts_processgroup_validate_test" />
+        <option name="binary-test-source" value="_64bit::DATA/nativetest64/vts_processgroup_validate_test/vts_processgroup_validate_test" />
+        <option name="binary-test-type" value="gtest"/>
+        <option name="binary-test-disable-framework" value="false"/>
+        <option name="test-timeout" value="30s"/>
+    </test>
+</configuration>
diff --git a/libprocessgroup/sched_policy.cpp b/libprocessgroup/sched_policy.cpp
index 337b032..1eefada 100644
--- a/libprocessgroup/sched_policy.cpp
+++ b/libprocessgroup/sched_policy.cpp
@@ -46,20 +46,20 @@
 
     switch (policy) {
         case SP_BACKGROUND:
-            return SetTaskProfiles(tid,
-                                   {"HighEnergySaving", "ProcessCapacityLow", "TimerSlackHigh"})
+            return SetTaskProfiles(tid, {"HighEnergySaving", "ProcessCapacityLow", "LowIoPriority",
+                                         "TimerSlackHigh"})
                            ? 0
                            : -1;
         case SP_FOREGROUND:
         case SP_AUDIO_APP:
         case SP_AUDIO_SYS:
-            return SetTaskProfiles(tid,
-                                   {"HighPerformance", "ProcessCapacityHigh", "TimerSlackNormal"})
+            return SetTaskProfiles(tid, {"HighPerformance", "ProcessCapacityHigh", "HighIoPriority",
+                                         "TimerSlackNormal"})
                            ? 0
                            : -1;
         case SP_TOP_APP:
-            return SetTaskProfiles(tid,
-                                   {"MaxPerformance", "ProcessCapacityMax", "TimerSlackNormal"})
+            return SetTaskProfiles(tid, {"MaxPerformance", "ProcessCapacityMax", "MaxIoPriority",
+                                         "TimerSlackNormal"})
                            ? 0
                            : -1;
         case SP_SYSTEM:
@@ -126,15 +126,24 @@
 
     switch (policy) {
         case SP_BACKGROUND:
-            return SetTaskProfiles(tid, {"HighEnergySaving", "TimerSlackHigh"}) ? 0 : -1;
+            return SetTaskProfiles(tid, {"HighEnergySaving", "LowIoPriority", "TimerSlackHigh"})
+                           ? 0
+                           : -1;
         case SP_FOREGROUND:
         case SP_AUDIO_APP:
         case SP_AUDIO_SYS:
-            return SetTaskProfiles(tid, {"HighPerformance", "TimerSlackNormal"}) ? 0 : -1;
+            return SetTaskProfiles(tid, {"HighPerformance", "HighIoPriority", "TimerSlackNormal"})
+                           ? 0
+                           : -1;
         case SP_TOP_APP:
-            return SetTaskProfiles(tid, {"MaxPerformance", "TimerSlackNormal"}) ? 0 : -1;
+            return SetTaskProfiles(tid, {"MaxPerformance", "MaxIoPriority", "TimerSlackNormal"})
+                           ? 0
+                           : -1;
         case SP_RT_APP:
-            return SetTaskProfiles(tid, {"RealtimePerformance", "TimerSlackNormal"}) ? 0 : -1;
+            return SetTaskProfiles(tid,
+                                   {"RealtimePerformance", "MaxIoPriority", "TimerSlackNormal"})
+                           ? 0
+                           : -1;
         default:
             return SetTaskProfiles(tid, {"TimerSlackNormal"}) ? 0 : -1;
     }
@@ -158,7 +167,7 @@
     if (!controller) return -1;
 
     if (!controller->GetTaskGroup(tid, &subgroup)) {
-        PLOG(ERROR) << "Failed to find cgroup for tid " << tid;
+        LOG(ERROR) << "Failed to find cgroup for tid " << tid;
         return -1;
     }
     return 0;
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index b69103c..9362c03 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -42,6 +42,7 @@
 using android::base::WriteStringToFile;
 
 #define TASK_PROFILE_DB_FILE "/etc/task_profiles.json"
+#define TASK_PROFILE_DB_VENDOR_FILE "/vendor/etc/task_profiles.json"
 
 bool ProfileAttribute::GetPathForTask(int tid, std::string* path) const {
     std::string subgroup;
@@ -118,7 +119,7 @@
     std::string path;
 
     if (!attribute_->GetPathForTask(tid, &path)) {
-        PLOG(ERROR) << "Failed to find cgroup for tid " << tid;
+        LOG(ERROR) << "Failed to find cgroup for tid " << tid;
         return false;
     }
 
@@ -173,7 +174,7 @@
     if (TEMP_FAILURE_RETRY(write(fd, value.c_str(), value.length())) < 0) {
         // If the thread is in the process of exiting, don't flag an error
         if (errno != ESRCH) {
-            PLOG(ERROR) << "JoinGroup failed to write '" << value << "'; fd=" << fd;
+            PLOG(ERROR) << "AddTidToCgroup failed to write '" << value << "'; fd=" << fd;
             return false;
         }
     }
@@ -186,7 +187,7 @@
     if (fd_ >= 0) {
         // fd is cached, reuse it
         if (!AddTidToCgroup(pid, fd_)) {
-            PLOG(ERROR) << "Failed to add task into cgroup";
+            LOG(ERROR) << "Failed to add task into cgroup";
             return false;
         }
         return true;
@@ -198,27 +199,27 @@
     }
 
     // this is app-dependent path, file descriptor is not cached
-    std::string procs_path = controller_->GetProcsFilePath(path_.c_str(), uid, pid);
+    std::string procs_path = controller_->GetProcsFilePath(path_, uid, pid);
     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(procs_path.c_str(), O_WRONLY | O_CLOEXEC)));
     if (tmp_fd < 0) {
-        PLOG(WARNING) << "Failed to open " << procs_path << ": " << strerror(errno);
+        PLOG(WARNING) << "Failed to open " << procs_path;
         return false;
     }
     if (!AddTidToCgroup(pid, tmp_fd)) {
-        PLOG(ERROR) << "Failed to add task into cgroup";
+        LOG(ERROR) << "Failed to add task into cgroup";
         return false;
     }
 
     return true;
 #else
-    std::string procs_path = controller_->GetProcsFilePath(path_.c_str(), uid, pid);
+    std::string procs_path = controller_->GetProcsFilePath(path_, uid, pid);
     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(procs_path.c_str(), O_WRONLY | O_CLOEXEC)));
     if (tmp_fd < 0) {
         // no permissions to access the file, ignore
         return true;
     }
     if (!AddTidToCgroup(pid, tmp_fd)) {
-        PLOG(ERROR) << "Failed to add task into cgroup";
+        LOG(ERROR) << "Failed to add task into cgroup";
         return false;
     }
 
@@ -231,7 +232,7 @@
     if (fd_ >= 0) {
         // fd is cached, reuse it
         if (!AddTidToCgroup(tid, fd_)) {
-            PLOG(ERROR) << "Failed to add task into cgroup";
+            LOG(ERROR) << "Failed to add task into cgroup";
             return false;
         }
         return true;
@@ -243,17 +244,17 @@
     }
 
     // application-dependent path can't be used with tid
-    PLOG(ERROR) << "Application profile can't be applied to a thread";
+    LOG(ERROR) << "Application profile can't be applied to a thread";
     return false;
 #else
-    std::string tasks_path = controller_->GetTasksFilePath(path_.c_str());
+    std::string tasks_path = controller_->GetTasksFilePath(path_);
     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC)));
     if (tmp_fd < 0) {
         // no permissions to access the file, ignore
         return true;
     }
     if (!AddTidToCgroup(tid, tmp_fd)) {
-        PLOG(ERROR) << "Failed to add task into cgroup";
+        LOG(ERROR) << "Failed to add task into cgroup";
         return false;
     }
 
@@ -283,21 +284,31 @@
 }
 
 TaskProfiles& TaskProfiles::GetInstance() {
-    static TaskProfiles instance;
-    return instance;
+    // Deliberately leak this object to avoid a race between destruction on
+    // process exit and concurrent access from another thread.
+    static auto* instance = new TaskProfiles;
+    return *instance;
 }
 
 TaskProfiles::TaskProfiles() {
-    if (!Load(CgroupMap::GetInstance())) {
-        LOG(ERROR) << "TaskProfiles::Load for [" << getpid() << "] failed";
+    // load system task profiles
+    if (!Load(CgroupMap::GetInstance(), TASK_PROFILE_DB_FILE)) {
+        LOG(ERROR) << "Loading " << TASK_PROFILE_DB_FILE << " for [" << getpid() << "] failed";
+    }
+
+    // load vendor task profiles if the file exists
+    if (!access(TASK_PROFILE_DB_VENDOR_FILE, F_OK) &&
+        !Load(CgroupMap::GetInstance(), TASK_PROFILE_DB_VENDOR_FILE)) {
+        LOG(ERROR) << "Loading " << TASK_PROFILE_DB_VENDOR_FILE << " for [" << getpid()
+                   << "] failed";
     }
 }
 
-bool TaskProfiles::Load(const CgroupMap& cg_map) {
+bool TaskProfiles::Load(const CgroupMap& cg_map, const std::string& file_name) {
     std::string json_doc;
 
-    if (!android::base::ReadFileToString(TASK_PROFILE_DB_FILE, &json_doc)) {
-        LOG(ERROR) << "Failed to read task profiles from " << TASK_PROFILE_DB_FILE;
+    if (!android::base::ReadFileToString(file_name, &json_doc)) {
+        LOG(ERROR) << "Failed to read task profiles from " << file_name;
         return false;
     }
 
@@ -308,18 +319,18 @@
         return false;
     }
 
-    Json::Value attr = root["Attributes"];
+    const Json::Value& attr = root["Attributes"];
     for (Json::Value::ArrayIndex i = 0; i < attr.size(); ++i) {
         std::string name = attr[i]["Name"].asString();
-        std::string ctrlName = attr[i]["Controller"].asString();
-        std::string file_name = attr[i]["File"].asString();
+        std::string controller_name = attr[i]["Controller"].asString();
+        std::string file_attr = attr[i]["File"].asString();
 
         if (attributes_.find(name) == attributes_.end()) {
-            const CgroupController* controller = cg_map.FindController(ctrlName.c_str());
+            const CgroupController* controller = cg_map.FindController(controller_name);
             if (controller) {
-                attributes_[name] = std::make_unique<ProfileAttribute>(controller, file_name);
+                attributes_[name] = std::make_unique<ProfileAttribute>(controller, file_attr);
             } else {
-                LOG(WARNING) << "Controller " << ctrlName << " is not found";
+                LOG(WARNING) << "Controller " << controller_name << " is not found";
             }
         } else {
             LOG(WARNING) << "Attribute " << name << " is already defined";
@@ -328,72 +339,72 @@
 
     std::map<std::string, std::string> params;
 
-    Json::Value profilesVal = root["Profiles"];
-    for (Json::Value::ArrayIndex i = 0; i < profilesVal.size(); ++i) {
-        Json::Value profileVal = profilesVal[i];
+    const Json::Value& profiles_val = root["Profiles"];
+    for (Json::Value::ArrayIndex i = 0; i < profiles_val.size(); ++i) {
+        const Json::Value& profile_val = profiles_val[i];
 
-        std::string profileName = profileVal["Name"].asString();
-        Json::Value actions = profileVal["Actions"];
+        std::string profile_name = profile_val["Name"].asString();
+        const Json::Value& actions = profile_val["Actions"];
         auto profile = std::make_unique<TaskProfile>();
 
-        for (Json::Value::ArrayIndex actIdx = 0; actIdx < actions.size(); ++actIdx) {
-            Json::Value actionVal = actions[actIdx];
-            std::string actionName = actionVal["Name"].asString();
-            Json::Value paramsVal = actionVal["Params"];
-            if (actionName == "JoinCgroup") {
-                std::string ctrlName = paramsVal["Controller"].asString();
-                std::string path = paramsVal["Path"].asString();
+        for (Json::Value::ArrayIndex act_idx = 0; act_idx < actions.size(); ++act_idx) {
+            const Json::Value& action_val = actions[act_idx];
+            std::string action_name = action_val["Name"].asString();
+            const Json::Value& params_val = action_val["Params"];
+            if (action_name == "JoinCgroup") {
+                std::string controller_name = params_val["Controller"].asString();
+                std::string path = params_val["Path"].asString();
 
-                const CgroupController* controller = cg_map.FindController(ctrlName.c_str());
+                const CgroupController* controller = cg_map.FindController(controller_name);
                 if (controller) {
                     profile->Add(std::make_unique<SetCgroupAction>(controller, path));
                 } else {
-                    LOG(WARNING) << "JoinCgroup: controller " << ctrlName << " is not found";
+                    LOG(WARNING) << "JoinCgroup: controller " << controller_name << " is not found";
                 }
-            } else if (actionName == "SetTimerSlack") {
-                std::string slackValue = paramsVal["Slack"].asString();
+            } else if (action_name == "SetTimerSlack") {
+                std::string slack_value = params_val["Slack"].asString();
                 char* end;
                 unsigned long slack;
 
-                slack = strtoul(slackValue.c_str(), &end, 10);
-                if (end > slackValue.c_str()) {
+                slack = strtoul(slack_value.c_str(), &end, 10);
+                if (end > slack_value.c_str()) {
                     profile->Add(std::make_unique<SetTimerSlackAction>(slack));
                 } else {
-                    LOG(WARNING) << "SetTimerSlack: invalid parameter: " << slackValue;
+                    LOG(WARNING) << "SetTimerSlack: invalid parameter: " << slack_value;
                 }
-            } else if (actionName == "SetAttribute") {
-                std::string attrName = paramsVal["Name"].asString();
-                std::string attrValue = paramsVal["Value"].asString();
+            } else if (action_name == "SetAttribute") {
+                std::string attr_name = params_val["Name"].asString();
+                std::string attr_value = params_val["Value"].asString();
 
-                auto iter = attributes_.find(attrName);
+                auto iter = attributes_.find(attr_name);
                 if (iter != attributes_.end()) {
                     profile->Add(
-                            std::make_unique<SetAttributeAction>(iter->second.get(), attrValue));
+                            std::make_unique<SetAttributeAction>(iter->second.get(), attr_value));
                 } else {
-                    LOG(WARNING) << "SetAttribute: unknown attribute: " << attrName;
+                    LOG(WARNING) << "SetAttribute: unknown attribute: " << attr_name;
                 }
-            } else if (actionName == "SetClamps") {
-                std::string boostValue = paramsVal["Boost"].asString();
-                std::string clampValue = paramsVal["Clamp"].asString();
+            } else if (action_name == "SetClamps") {
+                std::string boost_value = params_val["Boost"].asString();
+                std::string clamp_value = params_val["Clamp"].asString();
                 char* end;
                 unsigned long boost;
 
-                boost = strtoul(boostValue.c_str(), &end, 10);
-                if (end > boostValue.c_str()) {
-                    unsigned long clamp = strtoul(clampValue.c_str(), &end, 10);
-                    if (end > clampValue.c_str()) {
+                boost = strtoul(boost_value.c_str(), &end, 10);
+                if (end > boost_value.c_str()) {
+                    unsigned long clamp = strtoul(clamp_value.c_str(), &end, 10);
+                    if (end > clamp_value.c_str()) {
                         profile->Add(std::make_unique<SetClampsAction>(boost, clamp));
                     } else {
-                        LOG(WARNING) << "SetClamps: invalid parameter " << clampValue;
+                        LOG(WARNING) << "SetClamps: invalid parameter " << clamp_value;
                     }
                 } else {
-                    LOG(WARNING) << "SetClamps: invalid parameter: " << boostValue;
+                    LOG(WARNING) << "SetClamps: invalid parameter: " << boost_value;
                 }
             } else {
-                LOG(WARNING) << "Unknown profile action: " << actionName;
+                LOG(WARNING) << "Unknown profile action: " << action_name;
             }
         }
-        profiles_[profileName] = std::move(profile);
+        profiles_[profile_name] = std::move(profile);
     }
 
     return true;
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index b2e39f9..9ee81c1 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -152,5 +152,5 @@
 
     TaskProfiles();
 
-    bool Load(const CgroupMap& cg_map);
+    bool Load(const CgroupMap& cg_map, const std::string& file_name);
 };
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index 27cda93..15f03d0 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -31,7 +31,6 @@
     vndk: {
         enabled: true,
     },
-    double_loadable: true,
     host_supported: true,
     srcs: [
         "process.cpp",
diff --git a/libprocinfo/include/procinfo/process_map.h b/libprocinfo/include/procinfo/process_map.h
index 981241e..b6ec3cb 100644
--- a/libprocinfo/include/procinfo/process_map.h
+++ b/libprocinfo/include/procinfo/process_map.h
@@ -36,6 +36,7 @@
   uint64_t end_addr;
   uint16_t flags;
   uint64_t pgoff;
+  ino_t inode;
   char* next_line = content;
   char* p;
 
@@ -124,18 +125,25 @@
       return false;
     }
     // inode
-    if (!pass_xdigit() || (*p != '\0' && !pass_space())) {
+    inode = strtoull(p, &end, 10);
+    if (end == p) {
       return false;
     }
+    p = end;
+
+    if (*p != '\0' && !pass_space()) {
+      return false;
+    }
+
     // filename
-    callback(start_addr, end_addr, flags, pgoff, p);
+    callback(start_addr, end_addr, flags, pgoff, inode, p);
   }
   return true;
 }
 
-inline bool ReadMapFile(
-    const std::string& map_file,
-    const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, const char*)>& callback) {
+inline bool ReadMapFile(const std::string& map_file,
+                        const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, ino_t,
+                                                 const char*)>& callback) {
   std::string content;
   if (!android::base::ReadFileToString(map_file, &content)) {
     return false;
@@ -143,9 +151,9 @@
   return ReadMapFileContent(&content[0], callback);
 }
 
-inline bool ReadProcessMaps(
-    pid_t pid,
-    const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, const char*)>& callback) {
+inline bool ReadProcessMaps(pid_t pid,
+                            const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, ino_t,
+                                                     const char*)>& callback) {
   return ReadMapFile("/proc/" + std::to_string(pid) + "/maps", callback);
 }
 
@@ -154,17 +162,18 @@
   uint64_t end;
   uint16_t flags;
   uint64_t pgoff;
+  ino_t inode;
   std::string name;
 
-  MapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name)
-      : start(start), end(end), flags(flags), pgoff(pgoff), name(name) {}
+  MapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+          const char* name)
+      : start(start), end(end), flags(flags), pgoff(pgoff), inode(inode), name(name) {}
 };
 
 inline bool ReadProcessMaps(pid_t pid, std::vector<MapInfo>* maps) {
   return ReadProcessMaps(
-      pid, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name) {
-        maps->emplace_back(start, end, flags, pgoff, name);
-      });
+      pid, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+               const char* name) { maps->emplace_back(start, end, flags, pgoff, inode, name); });
 }
 
 } /* namespace procinfo */
diff --git a/libprocinfo/process_map_benchmark.cpp b/libprocinfo/process_map_benchmark.cpp
index 04995d4..eba4fd0 100644
--- a/libprocinfo/process_map_benchmark.cpp
+++ b/libprocinfo/process_map_benchmark.cpp
@@ -17,6 +17,7 @@
 #include <procinfo/process_map.h>
 
 #include <string.h>
+#include <sys/types.h>
 
 #include <string>
 
@@ -31,9 +32,10 @@
   std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
   for (auto _ : state) {
     std::vector<android::procinfo::MapInfo> maps;
-    android::procinfo::ReadMapFile(
-        map_file, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
-                      const char* name) { maps.emplace_back(start, end, flags, pgoff, name); });
+    android::procinfo::ReadMapFile(map_file, [&](uint64_t start, uint64_t end, uint16_t flags,
+                                                 uint64_t pgoff, ino_t inode, const char* name) {
+      maps.emplace_back(start, end, flags, pgoff, inode, name);
+    });
     CHECK_EQ(maps.size(), 2043u);
   }
 }
diff --git a/libprocinfo/process_map_test.cpp b/libprocinfo/process_map_test.cpp
index 170a806..562d864 100644
--- a/libprocinfo/process_map_test.cpp
+++ b/libprocinfo/process_map_test.cpp
@@ -26,23 +26,27 @@
   std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
   std::vector<android::procinfo::MapInfo> maps;
   ASSERT_TRUE(android::procinfo::ReadMapFile(
-      map_file, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
-                    const char* name) { maps.emplace_back(start, end, flags, pgoff, name); }));
+      map_file,
+      [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+          const char* name) { maps.emplace_back(start, end, flags, pgoff, inode, name); }));
   ASSERT_EQ(2043u, maps.size());
   ASSERT_EQ(maps[0].start, 0x12c00000ULL);
   ASSERT_EQ(maps[0].end, 0x2ac00000ULL);
   ASSERT_EQ(maps[0].flags, PROT_READ | PROT_WRITE);
   ASSERT_EQ(maps[0].pgoff, 0ULL);
+  ASSERT_EQ(maps[0].inode, 10267643UL);
   ASSERT_EQ(maps[0].name, "[anon:dalvik-main space (region space)]");
   ASSERT_EQ(maps[876].start, 0x70e6c4f000ULL);
   ASSERT_EQ(maps[876].end, 0x70e6c6b000ULL);
   ASSERT_EQ(maps[876].flags, PROT_READ | PROT_EXEC);
   ASSERT_EQ(maps[876].pgoff, 0ULL);
+  ASSERT_EQ(maps[876].inode, 2407UL);
   ASSERT_EQ(maps[876].name, "/system/lib64/libutils.so");
   ASSERT_EQ(maps[1260].start, 0x70e96fa000ULL);
   ASSERT_EQ(maps[1260].end, 0x70e96fb000ULL);
   ASSERT_EQ(maps[1260].flags, PROT_READ);
   ASSERT_EQ(maps[1260].pgoff, 0ULL);
+  ASSERT_EQ(maps[1260].inode, 10266154UL);
   ASSERT_EQ(maps[1260].name,
             "[anon:dalvik-classes.dex extracted in memory from "
             "/data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/base.apk]");
@@ -51,8 +55,9 @@
 TEST(process_map, ReadProcessMaps) {
   std::vector<android::procinfo::MapInfo> maps;
   ASSERT_TRUE(android::procinfo::ReadProcessMaps(
-      getpid(), [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
-                    const char* name) { maps.emplace_back(start, end, flags, pgoff, name); }));
+      getpid(),
+      [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+          const char* name) { maps.emplace_back(start, end, flags, pgoff, inode, name); }));
   ASSERT_GT(maps.size(), 0u);
   maps.clear();
   ASSERT_TRUE(android::procinfo::ReadProcessMaps(getpid(), &maps));
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index c90f5b2..b7650a1 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -95,7 +95,6 @@
                 "DexFiles.cpp",
             ],
             exclude_shared_libs: [
-                "libdexfile_external",
                 "libdexfile_support",
             ],
         },
@@ -106,7 +105,6 @@
                 "DexFiles.cpp",
             ],
             exclude_shared_libs: [
-                "libdexfile_external",
                 "libdexfile_support",
             ],
         },
@@ -137,7 +135,6 @@
 
     shared_libs: [
         "libbase",
-        "libdexfile_external",
         "libdexfile_support",
         "liblog",
         "liblzma",
@@ -251,6 +248,7 @@
         "tests/files/offline/offset_arm/*",
         "tests/files/offline/shared_lib_in_apk_arm64/*",
         "tests/files/offline/shared_lib_in_apk_memory_only_arm64/*",
+        "tests/files/offline/shared_lib_in_apk_single_map_arm64/*",
         "tests/files/offline/straddle_arm/*",
         "tests/files/offline/straddle_arm64/*",
     ],
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 2f5eed9..4b93abb 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -93,9 +93,12 @@
   valid_ = false;
 }
 
-bool Elf::GetSoname(std::string* name) {
+std::string Elf::GetSoname() {
   std::lock_guard<std::mutex> guard(lock_);
-  return valid_ && interface_->GetSoname(name);
+  if (!valid_) {
+    return "";
+  }
+  return interface_->GetSoname();
 }
 
 uint64_t Elf::GetRelPc(uint64_t pc, const MapInfo* map_info) {
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index c1b98d9..12efb94 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -374,13 +374,12 @@
 }
 
 template <typename DynType>
-bool ElfInterface::GetSonameWithTemplate(std::string* soname) {
+std::string ElfInterface::GetSonameWithTemplate() {
   if (soname_type_ == SONAME_INVALID) {
-    return false;
+    return "";
   }
   if (soname_type_ == SONAME_VALID) {
-    *soname = soname_;
-    return true;
+    return soname_;
   }
 
   soname_type_ = SONAME_INVALID;
@@ -397,7 +396,7 @@
     if (!memory_->ReadFully(offset, &dyn, sizeof(dyn))) {
       last_error_.code = ERROR_MEMORY_INVALID;
       last_error_.address = offset;
-      return false;
+      return "";
     }
 
     if (dyn.d_tag == DT_STRTAB) {
@@ -416,17 +415,16 @@
     if (entry.first == strtab_addr) {
       soname_offset = entry.second + soname_offset;
       if (soname_offset >= entry.second + strtab_size) {
-        return false;
+        return "";
       }
       if (!memory_->ReadString(soname_offset, &soname_)) {
-        return false;
+        return "";
       }
       soname_type_ = SONAME_VALID;
-      *soname = soname_;
-      return true;
+      return soname_;
     }
   }
-  return false;
+  return "";
 }
 
 template <typename SymType>
@@ -653,8 +651,8 @@
 template std::string ElfInterface::ReadBuildID<Elf32_Nhdr>();
 template std::string ElfInterface::ReadBuildID<Elf64_Nhdr>();
 
-template bool ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(std::string*);
-template bool ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(std::string*);
+template std::string ElfInterface::GetSonameWithTemplate<Elf32_Dyn>();
+template std::string ElfInterface::GetSonameWithTemplate<Elf64_Dyn>();
 
 template bool ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(uint64_t, std::string*,
                                                                    uint64_t*);
diff --git a/libunwindstack/Global.cpp b/libunwindstack/Global.cpp
index fdfd705..a20be00 100644
--- a/libunwindstack/Global.cpp
+++ b/libunwindstack/Global.cpp
@@ -77,7 +77,7 @@
   //   f0000-f2000 0 r-- /system/lib/libc.so
   //   f2000-f3000 2000 rw- /system/lib/libc.so
   MapInfo* map_start = nullptr;
-  for (MapInfo* info : *maps) {
+  for (const auto& info : *maps) {
     if (map_start != nullptr) {
       if (map_start->name == info->name) {
         if (info->offset != 0 &&
@@ -96,7 +96,7 @@
     }
     if (map_start == nullptr && (info->flags & PROT_READ) && info->offset == 0 &&
         !info->name.empty()) {
-      map_start = info;
+      map_start = info.get();
     }
   }
 }
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 89a6a79..28373b2 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -88,6 +88,7 @@
   // Check if the start of this map is an embedded elf.
   uint64_t max_size = 0;
   if (Elf::GetInfo(memory.get(), &max_size)) {
+    elf_start_offset = offset;
     if (max_size > map_size) {
       if (memory->Init(name, offset, max_size)) {
         return memory.release();
@@ -96,6 +97,7 @@
       if (memory->Init(name, offset, map_size)) {
         return memory.release();
       }
+      elf_start_offset = 0;
       return nullptr;
     }
     return memory.release();
@@ -186,44 +188,57 @@
 }
 
 Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, ArchEnum expected_arch) {
-  // Make sure no other thread is trying to add the elf to this map.
-  std::lock_guard<std::mutex> guard(mutex_);
+  {
+    // Make sure no other thread is trying to add the elf to this map.
+    std::lock_guard<std::mutex> guard(mutex_);
 
-  if (elf.get() != nullptr) {
-    return elf.get();
-  }
-
-  bool locked = false;
-  if (Elf::CachingEnabled() && !name.empty()) {
-    Elf::CacheLock();
-    locked = true;
-    if (Elf::CacheGet(this)) {
-      Elf::CacheUnlock();
+    if (elf.get() != nullptr) {
       return elf.get();
     }
+
+    bool locked = false;
+    if (Elf::CachingEnabled() && !name.empty()) {
+      Elf::CacheLock();
+      locked = true;
+      if (Elf::CacheGet(this)) {
+        Elf::CacheUnlock();
+        return elf.get();
+      }
+    }
+
+    Memory* memory = CreateMemory(process_memory);
+    if (locked) {
+      if (Elf::CacheAfterCreateMemory(this)) {
+        delete memory;
+        Elf::CacheUnlock();
+        return elf.get();
+      }
+    }
+    elf.reset(new Elf(memory));
+    // If the init fails, keep the elf around as an invalid object so we
+    // don't try to reinit the object.
+    elf->Init();
+    if (elf->valid() && expected_arch != elf->arch()) {
+      // Make the elf invalid, mismatch between arch and expected arch.
+      elf->Invalidate();
+    }
+
+    if (locked) {
+      Elf::CacheAdd(this);
+      Elf::CacheUnlock();
+    }
   }
 
-  Memory* memory = CreateMemory(process_memory);
-  if (locked) {
-    if (Elf::CacheAfterCreateMemory(this)) {
-      delete memory;
-      Elf::CacheUnlock();
-      return elf.get();
+  // If there is a read-only map then a read-execute map that represents the
+  // same elf object, make sure the previous map is using the same elf
+  // object if it hasn't already been set.
+  if (prev_map != nullptr && elf_start_offset != offset && prev_map->offset == elf_start_offset &&
+      prev_map->name == name) {
+    std::lock_guard<std::mutex> guard(prev_map->mutex_);
+    if (prev_map->elf.get() == nullptr) {
+      prev_map->elf = elf;
     }
   }
-  elf.reset(new Elf(memory));
-  // If the init fails, keep the elf around as an invalid object so we
-  // don't try to reinit the object.
-  elf->Init();
-  if (elf->valid() && expected_arch != elf->arch()) {
-    // Make the elf invalid, mismatch between arch and expected arch.
-    elf->Invalidate();
-  }
-
-  if (locked) {
-    Elf::CacheAdd(this);
-    Elf::CacheUnlock();
-  }
   return elf.get();
 }
 
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index c90e383..5da73e4 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -47,9 +47,9 @@
   size_t last = maps_.size();
   while (first < last) {
     size_t index = (first + last) / 2;
-    MapInfo* cur = maps_[index];
+    const auto& cur = maps_[index];
     if (pc >= cur->start && pc < cur->end) {
-      return cur;
+      return cur.get();
     } else if (pc < cur->start) {
       last = index;
     } else {
@@ -62,39 +62,36 @@
 bool Maps::Parse() {
   return android::procinfo::ReadMapFile(
       GetMapsFile(),
-      [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name) {
+      [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t, const char* name) {
         // Mark a device map in /dev/ and not in /dev/ashmem/ specially.
         if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
           flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
         }
-        maps_.push_back(
-            new MapInfo(maps_.empty() ? nullptr : maps_.back(), start, end, pgoff, flags, name));
+        maps_.emplace_back(
+            new MapInfo(maps_.empty() ? nullptr : maps_.back().get(), start, end, pgoff,
+                        flags, name));
       });
 }
 
 void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
                const std::string& name, uint64_t load_bias) {
-  MapInfo* map_info =
-      new MapInfo(maps_.empty() ? nullptr : maps_.back(), start, end, offset, flags, name);
+  auto map_info =
+      std::make_unique<MapInfo>(maps_.empty() ? nullptr : maps_.back().get(), start, end, offset,
+                                flags, name);
   map_info->load_bias = load_bias;
-  maps_.push_back(map_info);
+  maps_.emplace_back(std::move(map_info));
 }
 
 void Maps::Sort() {
   std::sort(maps_.begin(), maps_.end(),
-            [](const MapInfo* a, const MapInfo* b) { return a->start < b->start; });
+            [](const std::unique_ptr<MapInfo>& a, const std::unique_ptr<MapInfo>& b) {
+              return a->start < b->start; });
 
   // Set the prev_map values on the info objects.
   MapInfo* prev_map = nullptr;
-  for (MapInfo* map_info : maps_) {
+  for (const auto& map_info : maps_) {
     map_info->prev_map = prev_map;
-    prev_map = map_info;
-  }
-}
-
-Maps::~Maps() {
-  for (auto& map : maps_) {
-    delete map;
+    prev_map = map_info.get();
   }
 }
 
@@ -102,13 +99,14 @@
   std::string content(buffer_);
   return android::procinfo::ReadMapFileContent(
       &content[0],
-      [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name) {
+      [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t, const char* name) {
         // Mark a device map in /dev/ and not in /dev/ashmem/ specially.
         if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
           flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
         }
-        maps_.push_back(
-            new MapInfo(maps_.empty() ? nullptr : maps_.back(), start, end, pgoff, flags, name));
+        maps_.emplace_back(
+            new MapInfo(maps_.empty() ? nullptr : maps_.back().get(), start, end, pgoff,
+                        flags, name));
       });
 }
 
@@ -124,10 +122,6 @@
   // New maps will be added at the end without deleting the old ones.
   size_t last_map_idx = maps_.size();
   if (!Parse()) {
-    // Delete any maps added by the Parse call.
-    for (size_t i = last_map_idx; i < maps_.size(); i++) {
-      delete maps_[i];
-    }
     maps_.resize(last_map_idx);
     return false;
   }
@@ -135,17 +129,16 @@
   size_t total_entries = maps_.size();
   size_t search_map_idx = 0;
   for (size_t new_map_idx = last_map_idx; new_map_idx < maps_.size(); new_map_idx++) {
-    MapInfo* new_map_info = maps_[new_map_idx];
+    auto& new_map_info = maps_[new_map_idx];
     uint64_t start = new_map_info->start;
     uint64_t end = new_map_info->end;
     uint64_t flags = new_map_info->flags;
     std::string* name = &new_map_info->name;
     for (size_t old_map_idx = search_map_idx; old_map_idx < last_map_idx; old_map_idx++) {
-      MapInfo* info = maps_[old_map_idx];
+      auto& info = maps_[old_map_idx];
       if (start == info->start && end == info->end && flags == info->flags && *name == info->name) {
         // No need to check
         search_map_idx = old_map_idx + 1;
-        delete new_map_info;
         maps_[new_map_idx] = nullptr;
         total_entries--;
         break;
@@ -158,7 +151,7 @@
       // Never delete these maps, they may be in use. The assumption is
       // that there will only every be a handfull of these so waiting
       // to destroy them is not too expensive.
-      saved_maps_.push_back(info);
+      saved_maps_.emplace_back(std::move(info));
       maps_[old_map_idx] = nullptr;
       total_entries--;
     }
@@ -169,14 +162,14 @@
 
   // Now move out any of the maps that never were found.
   for (size_t i = search_map_idx; i < last_map_idx; i++) {
-    saved_maps_.push_back(maps_[i]);
+    saved_maps_.emplace_back(std::move(maps_[i]));
     maps_[i] = nullptr;
     total_entries--;
   }
 
   // Sort all of the values such that the nullptrs wind up at the end, then
   // resize them away.
-  std::sort(maps_.begin(), maps_.end(), [](const auto* a, const auto* b) {
+  std::sort(maps_.begin(), maps_.end(), [](const auto& a, const auto& b) {
     if (a == nullptr) {
       return false;
     } else if (b == nullptr) {
@@ -189,10 +182,4 @@
   return true;
 }
 
-LocalUpdatableMaps::~LocalUpdatableMaps() {
-  for (auto map_info : saved_maps_) {
-    delete map_info;
-  }
-}
-
 }  // namespace unwindstack
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 2734cf8..a1c58dd 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -105,6 +105,12 @@
 
   if (resolve_names_) {
     frame->map_name = map_info->name;
+    if (embedded_soname_ && map_info->elf_start_offset != 0 && !frame->map_name.empty()) {
+      std::string soname = elf->GetSoname();
+      if (!soname.empty()) {
+        frame->map_name += '!' + soname;
+      }
+    }
   }
   frame->map_elf_start_offset = map_info->elf_start_offset;
   frame->map_exact_offset = map_info->offset;
diff --git a/libunwindstack/benchmarks/unwind_benchmarks.cpp b/libunwindstack/benchmarks/unwind_benchmarks.cpp
index 8caecc7..de9137a 100644
--- a/libunwindstack/benchmarks/unwind_benchmarks.cpp
+++ b/libunwindstack/benchmarks/unwind_benchmarks.cpp
@@ -92,9 +92,9 @@
 
   // Find the libc.so share library and use that for benchmark purposes.
   *build_id_map_info = nullptr;
-  for (unwindstack::MapInfo* map_info : maps) {
+  for (auto& map_info : maps) {
     if (map_info->offset == 0 && map_info->GetBuildID() != "") {
-      *build_id_map_info = map_info;
+      *build_id_map_info = map_info.get();
       break;
     }
   }
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index 00a249f..ac94f10 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -59,7 +59,7 @@
 
   void Invalidate();
 
-  bool GetSoname(std::string* name);
+  std::string GetSoname();
 
   bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset);
 
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index d41bb13..dbd917d 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -56,7 +56,7 @@
 
   virtual void InitHeaders(uint64_t load_bias) = 0;
 
-  virtual bool GetSoname(std::string* name) = 0;
+  virtual std::string GetSoname() = 0;
 
   virtual bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* offset) = 0;
 
@@ -117,7 +117,7 @@
   void ReadSectionHeaders(const EhdrType& ehdr);
 
   template <typename DynType>
-  bool GetSonameWithTemplate(std::string* soname);
+  std::string GetSonameWithTemplate();
 
   template <typename SymType>
   bool GetFunctionNameWithTemplate(uint64_t addr, std::string* name, uint64_t* func_offset);
@@ -183,9 +183,7 @@
     ElfInterface::InitHeadersWithTemplate<uint32_t>(load_bias);
   }
 
-  bool GetSoname(std::string* soname) override {
-    return ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(soname);
-  }
+  std::string GetSoname() override { return ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(); }
 
   bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
     return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, name, func_offset);
@@ -215,9 +213,7 @@
     ElfInterface::InitHeadersWithTemplate<uint64_t>(load_bias);
   }
 
-  bool GetSoname(std::string* soname) override {
-    return ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(soname);
-  }
+  std::string GetSoname() override { return ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(); }
 
   bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
     return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, name, func_offset);
diff --git a/libunwindstack/include/unwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h
index 67fbed2..1784394 100644
--- a/libunwindstack/include/unwindstack/Maps.h
+++ b/libunwindstack/include/unwindstack/Maps.h
@@ -20,6 +20,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -37,8 +38,16 @@
 
 class Maps {
  public:
+  virtual ~Maps() = default;
+
   Maps() = default;
-  virtual ~Maps();
+
+  // Maps are not copyable but movable, because they own pointers to MapInfo
+  // objects.
+  Maps(const Maps&) = delete;
+  Maps& operator=(const Maps&) = delete;
+  Maps(Maps&&) = default;
+  Maps& operator=(Maps&&) = default;
 
   MapInfo* Find(uint64_t pc);
 
@@ -51,11 +60,11 @@
 
   void Sort();
 
-  typedef std::vector<MapInfo*>::iterator iterator;
+  typedef std::vector<std::unique_ptr<MapInfo>>::iterator iterator;
   iterator begin() { return maps_.begin(); }
   iterator end() { return maps_.end(); }
 
-  typedef std::vector<MapInfo*>::const_iterator const_iterator;
+  typedef std::vector<std::unique_ptr<MapInfo>>::const_iterator const_iterator;
   const_iterator begin() const { return maps_.begin(); }
   const_iterator end() const { return maps_.end(); }
 
@@ -63,11 +72,11 @@
 
   MapInfo* Get(size_t index) {
     if (index >= maps_.size()) return nullptr;
-    return maps_[index];
+    return maps_[index].get();
   }
 
  protected:
-  std::vector<MapInfo*> maps_;
+  std::vector<std::unique_ptr<MapInfo>> maps_;
 };
 
 class RemoteMaps : public Maps {
@@ -90,14 +99,14 @@
 class LocalUpdatableMaps : public Maps {
  public:
   LocalUpdatableMaps() : Maps() {}
-  virtual ~LocalUpdatableMaps();
+  virtual ~LocalUpdatableMaps() = default;
 
   bool Reparse();
 
   const std::string GetMapsFile() const override;
 
  private:
-  std::vector<MapInfo*> saved_maps_;
+  std::vector<std::unique_ptr<MapInfo>> saved_maps_;
 };
 
 class BufferMaps : public Maps {
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index dba41d1..b3beb6e 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -101,7 +101,7 @@
 
   size_t Size() { return size_; }
 
-  void Clear();
+  void Clear() override;
 
  protected:
   size_t size_ = 0;
diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
index 878ced3..1c2a81c 100644
--- a/libunwindstack/include/unwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -18,6 +18,7 @@
 #define _LIBUNWINDSTACK_REGS_H
 
 #include <stdint.h>
+#include <unistd.h>
 
 #include <functional>
 #include <string>
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index ddda7fd..a0554e2 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -100,6 +100,11 @@
   // set to an empty string and the function offset being set to zero.
   void SetResolveNames(bool resolve) { resolve_names_ = resolve; }
 
+  // Enable/disable soname printing the soname for a map name if the elf is
+  // embedded in a file. This is enabled by default.
+  // NOTE: This does nothing unless resolving names is enabled.
+  void SetEmbeddedSoname(bool embedded_soname) { embedded_soname_ = embedded_soname; }
+
 #if !defined(NO_LIBDEXFILE_SUPPORT)
   void SetDexFiles(DexFiles* dex_files, ArchEnum arch);
 #endif
@@ -124,6 +129,7 @@
   DexFiles* dex_files_ = nullptr;
 #endif
   bool resolve_names_ = true;
+  bool embedded_soname_ = true;
   ErrorData last_error_;
 };
 
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index 946bc3c..bd3083c 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -68,7 +68,7 @@
 
   bool Init(uint64_t*) override { return false; }
   void InitHeaders(uint64_t) override {}
-  bool GetSoname(std::string*) override { return false; }
+  std::string GetSoname() override { return fake_soname_; }
 
   bool GetFunctionName(uint64_t, std::string*, uint64_t*) override;
   bool GetGlobalVariable(const std::string&, uint64_t*) override;
@@ -83,6 +83,8 @@
   void FakeSetBuildID(std::string& build_id) { fake_build_id_ = build_id; }
   void FakeSetBuildID(const char* build_id) { fake_build_id_ = build_id; }
 
+  void FakeSetSoname(const char* soname) { fake_soname_ = soname; }
+
   static void FakePushFunctionData(const FunctionData data) { functions_.push_back(data); }
   static void FakePushStepData(const StepData data) { steps_.push_back(data); }
 
@@ -98,6 +100,7 @@
  private:
   std::unordered_map<std::string, uint64_t> globals_;
   std::string fake_build_id_;
+  std::string fake_soname_;
 
   static std::deque<FunctionData> functions_;
   static std::deque<StepData> steps_;
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index 7239749..d895863 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -555,9 +555,7 @@
   ASSERT_TRUE(elf->Init(&load_bias));
   EXPECT_EQ(0U, load_bias);
 
-  std::string name;
-  ASSERT_TRUE(elf->GetSoname(&name));
-  ASSERT_STREQ("fake_soname.so", name.c_str());
+  ASSERT_EQ("fake_soname.so", elf->GetSoname());
 }
 
 TEST_F(ElfInterfaceTest, elf32_soname) {
@@ -578,8 +576,7 @@
   ASSERT_TRUE(elf->Init(&load_bias));
   EXPECT_EQ(0U, load_bias);
 
-  std::string name;
-  ASSERT_FALSE(elf->GetSoname(&name));
+  ASSERT_EQ("", elf->GetSoname());
 }
 
 TEST_F(ElfInterfaceTest, elf32_soname_after_dt_null) {
@@ -600,8 +597,7 @@
   ASSERT_TRUE(elf->Init(&load_bias));
   EXPECT_EQ(0U, load_bias);
 
-  std::string name;
-  ASSERT_FALSE(elf->GetSoname(&name));
+  ASSERT_EQ("", elf->GetSoname());
 }
 
 TEST_F(ElfInterfaceTest, elf32_soname_size) {
@@ -624,8 +620,7 @@
   ASSERT_TRUE(elf->Init(&load_bias));
   EXPECT_EQ(0U, load_bias);
 
-  std::string name;
-  ASSERT_FALSE(elf->GetSoname(&name));
+  ASSERT_EQ("", elf->GetSoname());
 }
 
 TEST_F(ElfInterfaceTest, elf32_soname_missing_map) {
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 1ff2306..23c9cf8 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -126,9 +126,9 @@
   ASSERT_FALSE(elf.valid());
   ASSERT_TRUE(elf.interface() == nullptr);
 
-  std::string name;
-  ASSERT_FALSE(elf.GetSoname(&name));
+  ASSERT_EQ("", elf.GetSoname());
 
+  std::string name;
   uint64_t func_offset;
   ASSERT_FALSE(elf.GetFunctionName(0, &name, &func_offset));
 
@@ -309,7 +309,7 @@
 
   bool Init(uint64_t*) override { return false; }
   void InitHeaders(uint64_t) override {}
-  bool GetSoname(std::string*) override { return false; }
+  std::string GetSoname() override { return ""; }
   bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { return false; }
   std::string GetBuildID() override { return ""; }
 
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index a66685a..2ddadef 100644
--- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -178,7 +178,7 @@
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
   ASSERT_EQ(0U, info.elf_offset);
-  EXPECT_EQ(0U, info.elf_start_offset);
+  EXPECT_EQ(0x1000U, info.elf_start_offset);
 
   // Read the valid part of the file.
   std::vector<uint8_t> buffer(0x100);
@@ -202,7 +202,7 @@
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
   ASSERT_EQ(0U, info.elf_offset);
-  EXPECT_EQ(0U, info.elf_start_offset);
+  EXPECT_EQ(0x1000U, info.elf_start_offset);
 
   // Verify the memory is a valid elf.
   uint8_t e_ident[SELFMAG + 1];
@@ -219,7 +219,7 @@
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
   ASSERT_EQ(0U, info.elf_offset);
-  EXPECT_EQ(0U, info.elf_start_offset);
+  EXPECT_EQ(0x2000U, info.elf_start_offset);
 
   // Verify the memory is a valid elf.
   uint8_t e_ident[SELFMAG + 1];
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp
index d7b8485..d60b8b1 100644
--- a/libunwindstack/tests/MapInfoGetElfTest.cpp
+++ b/libunwindstack/tests/MapInfoGetElfTest.cpp
@@ -371,4 +371,35 @@
   }
 }
 
+// Verify that previous maps don't automatically get the same elf object.
+TEST_F(MapInfoGetElfTest, prev_map_elf_not_set) {
+  MapInfo info1(nullptr, 0x1000, 0x2000, 0, PROT_READ, "/not/present");
+  MapInfo info2(&info1, 0x2000, 0x3000, 0, PROT_READ, elf_.path);
+
+  Elf32_Ehdr ehdr;
+  TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+  memory_->SetMemory(0x2000, &ehdr, sizeof(ehdr));
+  Elf* elf = info2.GetElf(process_memory_, ARCH_ARM);
+  ASSERT_TRUE(elf != nullptr);
+  ASSERT_TRUE(elf->valid());
+
+  ASSERT_NE(elf, info1.GetElf(process_memory_, ARCH_ARM));
+}
+
+// Verify that a read-only map followed by a read-execute map will result
+// in the same elf object in both maps.
+TEST_F(MapInfoGetElfTest, read_only_followed_by_read_exec_share_elf) {
+  MapInfo r_info(nullptr, 0x1000, 0x2000, 0, PROT_READ, elf_.path);
+  MapInfo rw_info(&r_info, 0x2000, 0x3000, 0x1000, PROT_READ | PROT_EXEC, elf_.path);
+
+  Elf32_Ehdr ehdr;
+  TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+  memory_->SetMemory(0x1000, &ehdr, sizeof(ehdr));
+  Elf* elf = rw_info.GetElf(process_memory_, ARCH_ARM);
+  ASSERT_TRUE(elf != nullptr);
+  ASSERT_TRUE(elf->valid());
+
+  ASSERT_EQ(elf, r_info.GetElf(process_memory_, ARCH_ARM));
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
index b4197f2..9e7a6ab 100644
--- a/libunwindstack/tests/MapsTest.cpp
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -61,6 +61,26 @@
   ASSERT_EQ(0U, info->load_bias.load());
 }
 
+TEST(MapsTest, map_move) {
+  Maps maps;
+
+  maps.Add(0x1000, 0x2000, 0, PROT_READ, "fake_map", 0);
+  maps.Add(0x3000, 0x4000, 0x10, 0, "", 0x1234);
+  maps.Add(0x5000, 0x6000, 1, 2, "fake_map2", static_cast<uint64_t>(-1));
+
+  Maps maps2 = std::move(maps);
+
+  ASSERT_EQ(3U, maps2.Total());
+  MapInfo* info = maps2.Get(0);
+  ASSERT_EQ(0x1000U, info->start);
+  ASSERT_EQ(0x2000U, info->end);
+  ASSERT_EQ(0U, info->offset);
+  ASSERT_EQ(PROT_READ, info->flags);
+  ASSERT_EQ("fake_map", info->name);
+  ASSERT_EQ(0U, info->elf_offset);
+  ASSERT_EQ(0U, info->load_bias.load());
+}
+
 TEST(MapsTest, verify_parse_line) {
   MapInfo info(nullptr, 0, 0, 0, 0, "");
 
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index b5feb38..655579e 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -300,7 +300,7 @@
   EXPECT_EQ(
       "  #00 pc 00068fb8  libarttestd.so (art::CauseSegfault()+72)\n"
       "  #01 pc 00067f00  libarttestd.so (Java_Main_unwindInProcess+10032)\n"
-      "  #02 pc 000021a8  137-cfi.odex (offset 0x2000) (boolean Main.unwindInProcess(boolean, int, "
+      "  #02 pc 000021a8  137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
       "boolean)+136)\n"
       "  #03 pc 0000fe80  anonymous:ee74c000 (boolean Main.bar(boolean)+64)\n"
       "  #04 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)\n"
@@ -601,7 +601,7 @@
   ASSERT_EQ(76U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
   EXPECT_EQ(
       "  #00 pc 00018a5e  libarttestd.so (Java_Main_unwindInProcess+866)\n"
-      "  #01 pc 0000212d  137-cfi.odex (offset 0x2000) (boolean Main.unwindInProcess(boolean, int, "
+      "  #01 pc 0000212d  137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
       "boolean)+92)\n"
       "  #02 pc 00011cb1  anonymous:e2796000 (boolean Main.bar(boolean)+72)\n"
       "  #03 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)\n"
@@ -1312,7 +1312,8 @@
       "  #02 pc 00000000000008bc  vdso.so\n"
       "  #03 pc 00000000000846f4  libc.so (abort+172)\n"
       "  #04 pc 0000000000084ad4  libc.so (__assert2+36)\n"
-      "  #05 pc 000000000003d5b4  ANGLEPrebuilt.apk (offset 0x4000) (ANGLEGetUtilityAPI+56)\n"
+      "  #05 pc 000000000003d5b4  ANGLEPrebuilt.apk!libfeature_support_angle.so (offset 0x4000) "
+      "(ANGLEGetUtilityAPI+56)\n"
       "  #06 pc 000000000007fe68  libc.so (__libc_init)\n",
       frame_info);
 
@@ -1372,4 +1373,56 @@
   // Ignore top frame since the test code was modified to end in __libc_init.
 }
 
+TEST_F(UnwindOfflineTest, shared_lib_in_apk_single_map_arm64) {
+  ASSERT_NO_FATAL_FAILURE(Init("shared_lib_in_apk_single_map_arm64/", ARCH_ARM64));
+
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.Unwind();
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(13U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 00000000000814bc  libc.so (syscall+28)\n"
+      "  #01 pc 00000000008cdf5c  test.apk (offset 0x5000)\n"
+      "  #02 pc 00000000008cde9c  test.apk (offset 0x5000)\n"
+      "  #03 pc 00000000008cdd70  test.apk (offset 0x5000)\n"
+      "  #04 pc 00000000008ce408  test.apk (offset 0x5000)\n"
+      "  #05 pc 00000000008ce8d8  test.apk (offset 0x5000)\n"
+      "  #06 pc 00000000008ce814  test.apk (offset 0x5000)\n"
+      "  #07 pc 00000000008bcf60  test.apk (offset 0x5000)\n"
+      "  #08 pc 0000000000133024  test.apk (offset 0x5000)\n"
+      "  #09 pc 0000000000134ad0  test.apk (offset 0x5000)\n"
+      "  #10 pc 0000000000134b64  test.apk (offset 0x5000)\n"
+      "  #11 pc 00000000000e406c  libc.so (__pthread_start(void*)+36)\n"
+      "  #12 pc 0000000000085e18  libc.so (__start_thread+64)\n",
+      frame_info);
+
+  EXPECT_EQ(0x7cbe0b14bcULL, unwinder.frames()[0].pc);
+  EXPECT_EQ(0x7be4f077d0ULL, unwinder.frames()[0].sp);
+  EXPECT_EQ(0x7be6715f5cULL, unwinder.frames()[1].pc);
+  EXPECT_EQ(0x7be4f077d0ULL, unwinder.frames()[1].sp);
+  EXPECT_EQ(0x7be6715e9cULL, unwinder.frames()[2].pc);
+  EXPECT_EQ(0x7be4f07800ULL, unwinder.frames()[2].sp);
+  EXPECT_EQ(0x7be6715d70ULL, unwinder.frames()[3].pc);
+  EXPECT_EQ(0x7be4f07840ULL, unwinder.frames()[3].sp);
+  EXPECT_EQ(0x7be6716408ULL, unwinder.frames()[4].pc);
+  EXPECT_EQ(0x7be4f07860ULL, unwinder.frames()[4].sp);
+  EXPECT_EQ(0x7be67168d8ULL, unwinder.frames()[5].pc);
+  EXPECT_EQ(0x7be4f07880ULL, unwinder.frames()[5].sp);
+  EXPECT_EQ(0x7be6716814ULL, unwinder.frames()[6].pc);
+  EXPECT_EQ(0x7be4f078f0ULL, unwinder.frames()[6].sp);
+  EXPECT_EQ(0x7be6704f60ULL, unwinder.frames()[7].pc);
+  EXPECT_EQ(0x7be4f07910ULL, unwinder.frames()[7].sp);
+  EXPECT_EQ(0x7be5f7b024ULL, unwinder.frames()[8].pc);
+  EXPECT_EQ(0x7be4f07950ULL, unwinder.frames()[8].sp);
+  EXPECT_EQ(0x7be5f7cad0ULL, unwinder.frames()[9].pc);
+  EXPECT_EQ(0x7be4f07aa0ULL, unwinder.frames()[9].sp);
+  EXPECT_EQ(0x7be5f7cb64ULL, unwinder.frames()[10].pc);
+  EXPECT_EQ(0x7be4f07ce0ULL, unwinder.frames()[10].sp);
+  EXPECT_EQ(0x7cbe11406cULL, unwinder.frames()[11].pc);
+  EXPECT_EQ(0x7be4f07d00ULL, unwinder.frames()[11].sp);
+  EXPECT_EQ(0x7cbe0b5e18ULL, unwinder.frames()[12].pc);
+  EXPECT_EQ(0x7be4f07d20ULL, unwinder.frames()[12].sp);
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index d88531f..504b57a 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -49,7 +49,7 @@
     std::string str_name(name);
     maps_->Add(start, end, offset, flags, name, static_cast<uint64_t>(-1));
     if (elf != nullptr) {
-      MapInfo* map_info = *--maps_->end();
+      const auto& map_info = *--maps_->end();
       map_info->elf.reset(elf);
     }
   }
@@ -79,13 +79,18 @@
     AddMapInfo(0x33000, 0x34000, 0, PROT_READ | PROT_WRITE, "/fake/compressed.so", elf);
 
     elf = new ElfFake(new MemoryFake);
-    elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+    ElfInterfaceFake* interface = new ElfInterfaceFake(nullptr);
+    interface->FakeSetSoname("lib_fake.so");
+    elf->FakeSetInterface(interface);
     AddMapInfo(0x43000, 0x44000, 0x1d000, PROT_READ | PROT_WRITE, "/fake/fake.apk", elf);
+    MapInfo* map_info = maps_->Find(0x43000);
+    ASSERT_TRUE(map_info != nullptr);
+    map_info->elf_start_offset = 0x1d000;
 
     AddMapInfo(0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
 
     AddMapInfo(0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.vdex");
-    MapInfo* info = *--maps_->end();
+    const auto& info = *--maps_->end();
     info->load_bias = 0;
 
     elf = new ElfFake(new MemoryFake);
@@ -98,8 +103,8 @@
     elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
     AddMapInfo(0xa7000, 0xa8000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake_offset.oat",
                elf);
-    info = *--maps_->end();
-    info->elf_offset = 0x8000;
+    const auto& info2 = *--maps_->end();
+    info2->elf_offset = 0x8000;
 
     process_memory_.reset(new MemoryFake);
   }
@@ -324,8 +329,38 @@
   EXPECT_EQ(0x10000U, frame->sp);
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/fake/fake.apk!lib_fake.so", frame->map_name);
+  EXPECT_EQ(0x1d000U, frame->map_elf_start_offset);
+  EXPECT_EQ(0x1d000U, frame->map_exact_offset);
+  EXPECT_EQ(0x43000U, frame->map_start);
+  EXPECT_EQ(0x44000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, disable_embedded_soname) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+  regs_.set_pc(0x43000);
+  regs_.set_sp(0x10000);
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+  unwinder.SetEmbeddedSoname(false);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x43000U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
   EXPECT_EQ("/fake/fake.apk", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0x1d000U, frame->map_elf_start_offset);
   EXPECT_EQ(0x1d000U, frame->map_exact_offset);
   EXPECT_EQ(0x43000U, frame->map_start);
   EXPECT_EQ(0x44000U, frame->map_end);
@@ -813,8 +848,8 @@
   EXPECT_EQ(0x10010U, frame->sp);
   EXPECT_EQ("Frame1", frame->function_name);
   EXPECT_EQ(1U, frame->function_offset);
-  EXPECT_EQ("/fake/fake.apk", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ("/fake/fake.apk!lib_fake.so", frame->map_name);
+  EXPECT_EQ(0x1d000U, frame->map_elf_start_offset);
   EXPECT_EQ(0x1d000U, frame->map_exact_offset);
   EXPECT_EQ(0x43000U, frame->map_start);
   EXPECT_EQ(0x44000U, frame->map_end);
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt b/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
index 3cd9d40..4043122 100644
--- a/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
@@ -1,5 +1,6 @@
 ab0d3000-ab0d8000 r-xp 0 00:00 0   dalvikvm32
 dfe4e000-dfe7b000 r-xp 0 00:00 0   libarttestd.so
+e0445000-e0447000 r--p 0 00:00 0   137-cfi.odex
 e0447000-e0448000 r-xp 2000 00:00 0   137-cfi.odex
 e2796000-e4796000 r-xp 0 00:00 0   anonymous:e2796000
 e648e000-e690f000 r-xp 0 00:00 0  libart.so
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt b/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
index a8d215c..f255a44 100644
--- a/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
@@ -1,5 +1,6 @@
 56573000-56577000 r-xp 0 00:00 0   dalvikvm32
 eb833000-eb8cc000 r-xp 0 00:00 0   libarttestd.so
+ec604000-ec606000 r--p 0 00:00 0   137-cfi.odex
 ec606000-ec607000 r-xp 2000 00:00 0   137-cfi.odex
 ee74c000-f074c000 r-xp 0 00:00 0   anonymous:ee74c000
 f6be1000-f732b000 r-xp 0 00:00 0   libartd.so
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/libc.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/libc.so
new file mode 100644
index 0000000..cac1dd9
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/maps.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/maps.txt
new file mode 100644
index 0000000..2c5ca62
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/maps.txt
@@ -0,0 +1,3 @@
+7be5e48000-7be6b2b000 r-xp 5000 00:00 0   test.apk
+7cbe030000-7cbe070000 r--p 0 00:00 0   libc.so
+7cbe070000-7cbe11a000 r-xp 40000 00:00 0   libc.so
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/regs.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/regs.txt
new file mode 100644
index 0000000..090aeda
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/regs.txt
@@ -0,0 +1,33 @@
+x0: 7c326f6568
+x1: 80
+x2: 0
+x3: 0
+x4: 0
+x5: 0
+x6: 0
+x7: 7f7f7f7f7f7f7f7f
+x8: 62
+x9: 1
+x10: 1
+x11: 0
+x12: ffffffffc4653600
+x13: 17645696f
+x14: 2742ed97ca77a3
+x15: 3ab49084
+x16: 7be6b6bdb8
+x17: 7cbe0b14a0
+x18: 7c2b02a000
+x19: 0
+x20: 7c326f6568
+x21: 7be69c827c
+x22: 7be69c8272
+x23: 1
+x24: 7be74f7100
+x25: 881
+x26: 7be4f07a00
+x27: c479c000
+x28: 7be4f07998
+x29: 7be4f079b4
+sp: 7be4f077d0
+lr: 7be6715f60
+pc: 7cbe0b14bc
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/stack.data b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/stack.data
new file mode 100644
index 0000000..27d5bf3
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/test.apk b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/test.apk
new file mode 100644
index 0000000..70a9c71
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/test.apk
Binary files differ
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index 19982d8..92e5c0a 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -118,8 +118,8 @@
     return 1;
   }
 
-  std::string soname;
-  if (elf.GetSoname(&soname)) {
+  std::string soname(elf.GetSoname());
+  if (!soname.empty()) {
     printf("Soname: %s\n", soname.c_str());
   }
 
diff --git a/libunwindstack/tools/unwind_reg_info.cpp b/libunwindstack/tools/unwind_reg_info.cpp
index 4b6f49a..b77a86b 100644
--- a/libunwindstack/tools/unwind_reg_info.cpp
+++ b/libunwindstack/tools/unwind_reg_info.cpp
@@ -185,8 +185,8 @@
     return 1;
   }
 
-  std::string soname;
-  if (elf.GetSoname(&soname)) {
+  std::string soname(elf.GetSoname());
+  if (!soname.empty()) {
     printf("Soname: %s\n\n", soname.c_str());
   }
 
diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp
index 9128430..b0a4dd0 100644
--- a/libunwindstack/tools/unwind_symbols.cpp
+++ b/libunwindstack/tools/unwind_symbols.cpp
@@ -71,8 +71,8 @@
     return 1;
   }
 
-  std::string soname;
-  if (elf.GetSoname(&soname)) {
+  std::string soname(elf.GetSoname());
+  if (!soname.empty()) {
     printf("Soname: %s\n\n", soname.c_str());
   }
 
diff --git a/libusbhost/Android.bp b/libusbhost/Android.bp
index 39bf3a5..fc6f305 100644
--- a/libusbhost/Android.bp
+++ b/libusbhost/Android.bp
@@ -20,7 +20,6 @@
     vndk: {
         enabled: true,
     },
-    double_loadable: true,
     host_supported: true,
     srcs: ["usbhost.c"],
     cflags: ["-Werror"],
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 93aa1e6..c67ff8f 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -205,7 +205,6 @@
         "LruCache_test.cpp",
         "Mutex_test.cpp",
         "SharedBuffer_test.cpp",
-        "Singleton_test.cpp",
         "String8_test.cpp",
         "StrongPointer_test.cpp",
         "Unicode_test.cpp",
@@ -240,17 +239,33 @@
         },
     },
 
-    required: [
-        "libutils_test_singleton1",
-        "libutils_test_singleton2",
-    ],
-
     cflags: [
         "-Wall",
         "-Wextra",
         "-Werror",
         "-Wthread-safety",
     ],
+
+    test_suites: ["device-tests"],
+}
+
+// TODO: the test infrastructure isn't yet capable of running this,
+// so it's broken out into its own test so that the main libutils_tests
+// can be in presubmit even if this can't.
+
+cc_test {
+    name: "libutils_singleton_test",
+    srcs: ["Singleton_test.cpp"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: ["libbase"],
+
+    required: [
+        ":libutils_test_singleton1",
+        ":libutils_test_singleton2",
+    ],
 }
 
 cc_test_library {
diff --git a/libutils/RefBase_test.cpp b/libutils/RefBase_test.cpp
index 2e0cf6e..c9b4894 100644
--- a/libutils/RefBase_test.cpp
+++ b/libutils/RefBase_test.cpp
@@ -45,6 +45,44 @@
     bool* mDeleted;
 };
 
+// A version of Foo that ensures that all objects are allocated at the same
+// address. No more than one can be allocated at a time. Thread-hostile.
+class FooFixedAlloc : public RefBase {
+public:
+    static void* operator new(size_t size) {
+        if (mAllocCount != 0) {
+            abort();
+        }
+        mAllocCount = 1;
+        if (theMemory == nullptr) {
+            theMemory = malloc(size);
+        }
+        return theMemory;
+    }
+
+    static void operator delete(void *p) {
+        if (mAllocCount != 1 || p != theMemory) {
+            abort();
+        }
+        mAllocCount = 0;
+    }
+
+    FooFixedAlloc(bool* deleted_check) : mDeleted(deleted_check) {
+        *mDeleted = false;
+    }
+
+    ~FooFixedAlloc() {
+        *mDeleted = true;
+    }
+private:
+    bool* mDeleted;
+    static int mAllocCount;
+    static void* theMemory;
+};
+
+int FooFixedAlloc::mAllocCount(0);
+void* FooFixedAlloc::theMemory(nullptr);
+
 TEST(RefBase, StrongMoves) {
     bool isDeleted;
     Foo* foo = new Foo(&isDeleted);
@@ -90,6 +128,118 @@
     ASSERT_FALSE(isDeleted) << "Deletion on wp destruction should no longer occur";
 }
 
+TEST(RefBase, Comparisons) {
+    bool isDeleted, isDeleted2, isDeleted3;
+    Foo* foo = new Foo(&isDeleted);
+    Foo* foo2 = new Foo(&isDeleted2);
+    sp<Foo> sp1(foo);
+    sp<Foo> sp2(foo2);
+    wp<Foo> wp1(sp1);
+    wp<Foo> wp2(sp1);
+    wp<Foo> wp3(sp2);
+    ASSERT_TRUE(wp1 == wp2);
+    ASSERT_TRUE(wp1 == sp1);
+    ASSERT_TRUE(wp3 == sp2);
+    ASSERT_TRUE(wp1 != sp2);
+    ASSERT_TRUE(wp1 <= wp2);
+    ASSERT_TRUE(wp1 >= wp2);
+    ASSERT_FALSE(wp1 != wp2);
+    ASSERT_FALSE(wp1 > wp2);
+    ASSERT_FALSE(wp1 < wp2);
+    ASSERT_FALSE(sp1 == sp2);
+    ASSERT_TRUE(sp1 != sp2);
+    bool sp1_smaller = sp1 < sp2;
+    wp<Foo>wp_smaller = sp1_smaller ? wp1 : wp3;
+    wp<Foo>wp_larger = sp1_smaller ? wp3 : wp1;
+    ASSERT_TRUE(wp_smaller < wp_larger);
+    ASSERT_TRUE(wp_smaller != wp_larger);
+    ASSERT_TRUE(wp_smaller <= wp_larger);
+    ASSERT_FALSE(wp_smaller == wp_larger);
+    ASSERT_FALSE(wp_smaller > wp_larger);
+    ASSERT_FALSE(wp_smaller >= wp_larger);
+    sp2 = nullptr;
+    ASSERT_TRUE(isDeleted2);
+    ASSERT_FALSE(isDeleted);
+    ASSERT_FALSE(wp3 == sp2);
+    // Comparison results on weak pointers should not be affected.
+    ASSERT_TRUE(wp_smaller < wp_larger);
+    ASSERT_TRUE(wp_smaller != wp_larger);
+    ASSERT_TRUE(wp_smaller <= wp_larger);
+    ASSERT_FALSE(wp_smaller == wp_larger);
+    ASSERT_FALSE(wp_smaller > wp_larger);
+    ASSERT_FALSE(wp_smaller >= wp_larger);
+    wp2 = nullptr;
+    ASSERT_FALSE(wp1 == wp2);
+    ASSERT_TRUE(wp1 != wp2);
+    wp1.clear();
+    ASSERT_TRUE(wp1 == wp2);
+    ASSERT_FALSE(wp1 != wp2);
+    wp3.clear();
+    ASSERT_TRUE(wp1 == wp3);
+    ASSERT_FALSE(wp1 != wp3);
+    ASSERT_FALSE(isDeleted);
+    sp1.clear();
+    ASSERT_TRUE(isDeleted);
+    ASSERT_TRUE(sp1 == sp2);
+    // Try to check that null pointers are properly initialized.
+    {
+        // Try once with non-null, to maximize chances of getting junk on the
+        // stack.
+        sp<Foo> sp3(new Foo(&isDeleted3));
+        wp<Foo> wp4(sp3);
+        wp<Foo> wp5;
+        ASSERT_FALSE(wp4 == wp5);
+        ASSERT_TRUE(wp4 != wp5);
+        ASSERT_FALSE(sp3 == wp5);
+        ASSERT_FALSE(wp5 == sp3);
+        ASSERT_TRUE(sp3 != wp5);
+        ASSERT_TRUE(wp5 != sp3);
+        ASSERT_TRUE(sp3 == wp4);
+    }
+    {
+        sp<Foo> sp3;
+        wp<Foo> wp4(sp3);
+        wp<Foo> wp5;
+        ASSERT_TRUE(wp4 == wp5);
+        ASSERT_FALSE(wp4 != wp5);
+        ASSERT_TRUE(sp3 == wp5);
+        ASSERT_TRUE(wp5 == sp3);
+        ASSERT_FALSE(sp3 != wp5);
+        ASSERT_FALSE(wp5 != sp3);
+        ASSERT_TRUE(sp3 == wp4);
+    }
+}
+
+// Check whether comparison against dead wp works, even if the object referenced
+// by the new wp happens to be at the same address.
+TEST(RefBase, ReplacedComparison) {
+    bool isDeleted, isDeleted2;
+    FooFixedAlloc* foo = new FooFixedAlloc(&isDeleted);
+    sp<FooFixedAlloc> sp1(foo);
+    wp<FooFixedAlloc> wp1(sp1);
+    ASSERT_TRUE(wp1 == sp1);
+    sp1.clear();  // Deallocates the object.
+    ASSERT_TRUE(isDeleted);
+    FooFixedAlloc* foo2 = new FooFixedAlloc(&isDeleted2);
+    ASSERT_FALSE(isDeleted2);
+    ASSERT_EQ(foo, foo2);  // Not technically a legal comparison, but ...
+    sp<FooFixedAlloc> sp2(foo2);
+    wp<FooFixedAlloc> wp2(sp2);
+    ASSERT_TRUE(sp2 == wp2);
+    ASSERT_FALSE(sp2 != wp2);
+    ASSERT_TRUE(sp2 != wp1);
+    ASSERT_FALSE(sp2 == wp1);
+    ASSERT_FALSE(sp2 == sp1);  // sp1 is null.
+    ASSERT_FALSE(wp1 == wp2);  // wp1 refers to old object.
+    ASSERT_TRUE(wp1 != wp2);
+    ASSERT_TRUE(wp1 > wp2 || wp1 < wp2);
+    ASSERT_TRUE(wp1 >= wp2 || wp1 <= wp2);
+    ASSERT_FALSE(wp1 >= wp2 && wp1 <= wp2);
+    ASSERT_FALSE(wp1 == nullptr);
+    wp1 = sp2;
+    ASSERT_TRUE(wp1 == wp2);
+    ASSERT_FALSE(wp1 != wp2);
+}
 
 // Set up a situation in which we race with visit2AndRremove() to delete
 // 2 strong references.  Bar destructor checks that there are no early
diff --git a/libutils/include/utils/RefBase.h b/libutils/include/utils/RefBase.h
index 1780cf2..a105474 100644
--- a/libutils/include/utils/RefBase.h
+++ b/libutils/include/utils/RefBase.h
@@ -171,6 +171,8 @@
 #define ANDROID_REF_BASE_H
 
 #include <atomic>
+#include <functional>
+#include <type_traits>  // for common_type.
 
 #include <stdint.h>
 #include <sys/types.h>
@@ -192,19 +194,26 @@
 // ---------------------------------------------------------------------------
 
 #define COMPARE_WEAK(_op_)                                      \
-inline bool operator _op_ (const sp<T>& o) const {              \
-    return m_ptr _op_ o.m_ptr;                                  \
-}                                                               \
-inline bool operator _op_ (const T* o) const {                  \
-    return m_ptr _op_ o;                                        \
-}                                                               \
-template<typename U>                                            \
-inline bool operator _op_ (const sp<U>& o) const {              \
-    return m_ptr _op_ o.m_ptr;                                  \
-}                                                               \
 template<typename U>                                            \
 inline bool operator _op_ (const U* o) const {                  \
     return m_ptr _op_ o;                                        \
+}                                                               \
+/* Needed to handle type inference for nullptr: */              \
+inline bool operator _op_ (const T* o) const {                  \
+    return m_ptr _op_ o;                                        \
+}
+
+template<template<typename C> class comparator, typename T, typename U>
+static inline bool _wp_compare_(T* a, U* b) {
+    return comparator<typename std::common_type<T*, U*>::type>()(a, b);
+}
+
+// Use std::less and friends to avoid undefined behavior when ordering pointers
+// to different objects.
+#define COMPARE_WEAK_FUNCTIONAL(_op_, _compare_)                 \
+template<typename U>                                             \
+inline bool operator _op_ (const U* o) const {                   \
+    return _wp_compare_<_compare_>(m_ptr, o);                    \
 }
 
 // ---------------------------------------------------------------------------
@@ -354,7 +363,7 @@
 public:
     typedef typename RefBase::weakref_type weakref_type;
 
-    inline wp() : m_ptr(nullptr) { }
+    inline wp() : m_ptr(nullptr), m_refs(nullptr) { }
 
     wp(T* other);  // NOLINT(implicit)
     wp(const wp<T>& other);
@@ -395,39 +404,51 @@
 
     COMPARE_WEAK(==)
     COMPARE_WEAK(!=)
-    COMPARE_WEAK(>)
-    COMPARE_WEAK(<)
-    COMPARE_WEAK(<=)
-    COMPARE_WEAK(>=)
+    COMPARE_WEAK_FUNCTIONAL(>, std::greater)
+    COMPARE_WEAK_FUNCTIONAL(<, std::less)
+    COMPARE_WEAK_FUNCTIONAL(<=, std::less_equal)
+    COMPARE_WEAK_FUNCTIONAL(>=, std::greater_equal)
 
-    inline bool operator == (const wp<T>& o) const {
-        return (m_ptr == o.m_ptr) && (m_refs == o.m_refs);
-    }
     template<typename U>
     inline bool operator == (const wp<U>& o) const {
-        return m_ptr == o.m_ptr;
+        return m_refs == o.m_refs;  // Implies m_ptr == o.mptr; see invariants below.
     }
 
-    inline bool operator > (const wp<T>& o) const {
-        return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
+    template<typename U>
+    inline bool operator == (const sp<U>& o) const {
+        // Just comparing m_ptr fields is often dangerous, since wp<> may refer to an older
+        // object at the same address.
+        if (o == nullptr) {
+          return m_ptr == nullptr;
+        } else {
+          return m_refs == o->getWeakRefs();  // Implies m_ptr == o.mptr.
+        }
     }
+
+    template<typename U>
+    inline bool operator != (const sp<U>& o) const {
+        return !(*this == o);
+    }
+
     template<typename U>
     inline bool operator > (const wp<U>& o) const {
-        return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
+        if (m_ptr == o.m_ptr) {
+            return _wp_compare_<std::greater>(m_refs, o.m_refs);
+        } else {
+            return _wp_compare_<std::greater>(m_ptr, o.m_ptr);
+        }
     }
 
-    inline bool operator < (const wp<T>& o) const {
-        return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr);
-    }
     template<typename U>
     inline bool operator < (const wp<U>& o) const {
-        return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr);
+        if (m_ptr == o.m_ptr) {
+            return _wp_compare_<std::less>(m_refs, o.m_refs);
+        } else {
+            return _wp_compare_<std::less>(m_ptr, o.m_ptr);
+        }
     }
-                         inline bool operator != (const wp<T>& o) const { return m_refs != o.m_refs; }
     template<typename U> inline bool operator != (const wp<U>& o) const { return !operator == (o); }
-                         inline bool operator <= (const wp<T>& o) const { return !operator > (o); }
     template<typename U> inline bool operator <= (const wp<U>& o) const { return !operator > (o); }
-                         inline bool operator >= (const wp<T>& o) const { return !operator < (o); }
     template<typename U> inline bool operator >= (const wp<U>& o) const { return !operator < (o); }
 
 private:
@@ -446,11 +467,27 @@
 // ---------------------------------------------------------------------------
 // No user serviceable parts below here.
 
+// Implementation invariants:
+// Either
+// 1) m_ptr and m_refs are both null, or
+// 2) m_refs == m_ptr->mRefs, or
+// 3) *m_ptr is no longer live, and m_refs points to the weakref_type object that corresponded
+//    to m_ptr while it was live. *m_refs remains live while a wp<> refers to it.
+//
+// The m_refs field in a RefBase object is allocated on construction, unique to that RefBase
+// object, and never changes. Thus if two wp's have identical m_refs fields, they are either both
+// null or point to the same object. If two wp's have identical m_ptr fields, they either both
+// point to the same live object and thus have the same m_ref fields, or at least one of the
+// objects is no longer live.
+//
+// Note that the above comparison operations go out of their way to provide an ordering consistent
+// with ordinary pointer comparison; otherwise they could ignore m_ptr, and just compare m_refs.
+
 template<typename T>
 wp<T>::wp(T* other)
     : m_ptr(other)
 {
-    if (other) m_refs = other->createWeak(this);
+    m_refs = other ? m_refs = other->createWeak(this) : nullptr;
 }
 
 template<typename T>
@@ -464,16 +501,14 @@
 wp<T>::wp(const sp<T>& other)
     : m_ptr(other.m_ptr)
 {
-    if (m_ptr) {
-        m_refs = m_ptr->createWeak(this);
-    }
+    m_refs = m_ptr ? m_ptr->createWeak(this) : nullptr;
 }
 
 template<typename T> template<typename U>
 wp<T>::wp(U* other)
     : m_ptr(other)
 {
-    if (other) m_refs = other->createWeak(this);
+    m_refs = other ? other->createWeak(this) : nullptr;
 }
 
 template<typename T> template<typename U>
@@ -483,6 +518,8 @@
     if (m_ptr) {
         m_refs = other.m_refs;
         m_refs->incWeak(this);
+    } else {
+        m_refs = nullptr;
     }
 }
 
@@ -490,9 +527,7 @@
 wp<T>::wp(const sp<U>& other)
     : m_ptr(other.m_ptr)
 {
-    if (m_ptr) {
-        m_refs = m_ptr->createWeak(this);
-    }
+    m_refs = m_ptr ? m_ptr->createWeak(this) : nullptr;
 }
 
 template<typename T>
@@ -595,6 +630,7 @@
 {
     if (m_ptr) {
         m_refs->decWeak(this);
+        m_refs = 0;
         m_ptr = 0;
     }
 }
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
index 1571129..9cd7c75 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -17,6 +17,9 @@
 #ifndef ANDROID_STRONG_POINTER_H
 #define ANDROID_STRONG_POINTER_H
 
+#include <functional>
+#include <type_traits>  // for common_type.
+
 // ---------------------------------------------------------------------------
 namespace android {
 
@@ -24,13 +27,12 @@
 
 // ---------------------------------------------------------------------------
 
-#define COMPARE(_op_)                                           \
-inline bool operator _op_ (const sp<T>& o) const {              \
-    return m_ptr _op_ o.m_ptr;                                  \
-}                                                               \
-inline bool operator _op_ (const T* o) const {                  \
-    return m_ptr _op_ o;                                        \
-}                                                               \
+// TODO: Maybe remove sp<> ? wp<> comparison? These are dangerous: If the wp<>
+// was created before the sp<>, and they point to different objects, they may
+// compare equal even if they are entirely unrelated. E.g. CameraService
+// currently performa such comparisons.
+
+#define COMPARE_STRONG(_op_)                                           \
 template<typename U>                                            \
 inline bool operator _op_ (const sp<U>& o) const {              \
     return m_ptr _op_ o.m_ptr;                                  \
@@ -39,14 +41,27 @@
 inline bool operator _op_ (const U* o) const {                  \
     return m_ptr _op_ o;                                        \
 }                                                               \
-inline bool operator _op_ (const wp<T>& o) const {              \
-    return m_ptr _op_ o.m_ptr;                                  \
-}                                                               \
-template<typename U>                                            \
-inline bool operator _op_ (const wp<U>& o) const {              \
-    return m_ptr _op_ o.m_ptr;                                  \
+/* Needed to handle type inference for nullptr: */              \
+inline bool operator _op_ (const T* o) const {                  \
+    return m_ptr _op_ o;                                        \
 }
 
+template<template<typename C> class comparator, typename T, typename U>
+static inline bool _sp_compare_(T* a, U* b) {
+    return comparator<typename std::common_type<T*, U*>::type>()(a, b);
+}
+
+// Use std::less and friends to avoid undefined behavior when ordering pointers
+// to different objects.
+#define COMPARE_STRONG_FUNCTIONAL(_op_, _compare_)               \
+template<typename U>                                             \
+inline bool operator _op_ (const sp<U>& o) const {               \
+    return _sp_compare_<_compare_>(m_ptr, o.m_ptr);              \
+}                                                                \
+template<typename U>                                             \
+inline bool operator _op_ (const U* o) const {                   \
+    return _sp_compare_<_compare_>(m_ptr, o);                    \
+}
 // ---------------------------------------------------------------------------
 
 template<typename T>
@@ -89,12 +104,23 @@
 
     // Operators
 
-    COMPARE(==)
-    COMPARE(!=)
-    COMPARE(>)
-    COMPARE(<)
-    COMPARE(<=)
-    COMPARE(>=)
+    COMPARE_STRONG(==)
+    COMPARE_STRONG(!=)
+    COMPARE_STRONG_FUNCTIONAL(>, std::greater)
+    COMPARE_STRONG_FUNCTIONAL(<, std::less)
+    COMPARE_STRONG_FUNCTIONAL(<=, std::less_equal)
+    COMPARE_STRONG_FUNCTIONAL(>=, std::greater_equal)
+
+    // Punt these to the wp<> implementation.
+    template<typename U>
+    inline bool operator == (const wp<U>& o) const {
+        return o == *this;
+    }
+
+    template<typename U>
+    inline bool operator != (const wp<U>& o) const {
+        return o != *this;
+    }
 
 private:    
     template<typename Y> friend class sp;
diff --git a/libvndksupport/linker.c b/libvndksupport/linker.c
index 821940a..84c2132 100644
--- a/libvndksupport/linker.c
+++ b/libvndksupport/linker.c
@@ -20,6 +20,8 @@
 
 #define LOG_TAG "vndksupport"
 #include <log/log.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 __attribute__((weak)) extern struct android_namespace_t* android_get_exported_namespace(const char*);
 __attribute__((weak)) extern void* android_dlopen_ext(const char*, int, const android_dlextinfo*);
@@ -46,6 +48,11 @@
 }
 
 int android_is_in_vendor_process() {
+    // Special case init, since when init runs, ld.config.<ver>.txt hasn't been
+    // loaded (sysprop service isn't up for init to know <ver>).
+    if (getpid() == 1) {
+        return 0;
+    }
     if (android_get_exported_namespace == NULL) {
         ALOGD("android_get_exported_namespace() not available. Assuming system process.");
         return 0;
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index cbca1ce..9538bba 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -126,7 +126,6 @@
         },
     },
     test_suites: ["device-tests"],
-    test_config: "ziparchive-tests.xml",  // TODO: Remove after b/117891984.
 }
 
 // Performance benchmarks.
diff --git a/libziparchive/ziparchive-tests.xml b/libziparchive/ziparchive-tests.xml
deleted file mode 100644
index 2be0a99..0000000
--- a/libziparchive/ziparchive-tests.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 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.
--->
-<!-- Derived from auto-generated config. b/117891984 & b/124515549. -->
-<configuration description="Runs ziparchive-tests.">
-    <option name="test-suite-tag" value="apct" />
-    <option name="test-suite-tag" value="apct-native" />
-    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
-        <option name="cleanup" value="true" />
-        <option name="push" value="ziparchive-tests->/data/local/tmp/ziparchive-tests/ziparchive-tests" />
-        <option name="push" value="testdata->/data/local/tmp/ziparchive-tests/testdata" />
-    </target_preparer>
-
-    <test class="com.android.tradefed.testtype.GTest" >
-        <option name="native-test-device-path" value="/data/local/tmp/ziparchive-tests" />
-        <option name="module-name" value="ziparchive-tests" />
-    </test>
-</configuration>
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 562e578..2d3fbfc 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -110,7 +110,7 @@
  */
 #define PSI_WINDOW_SIZE_MS 1000
 /* Polling period after initial PSI signal */
-#define PSI_POLL_PERIOD_MS 200
+#define PSI_POLL_PERIOD_MS 40
 /* Poll for the duration of one window after initial PSI signal */
 #define PSI_POLL_COUNT (PSI_WINDOW_SIZE_MS / PSI_POLL_PERIOD_MS)
 
@@ -1329,7 +1329,7 @@
 static int last_killed_pid = -1;
 
 /* Kill one process specified by procp.  Returns the size of the process killed */
-static int kill_one_process(struct proc* procp) {
+static int kill_one_process(struct proc* procp, int min_oom_score) {
     int pid = procp->pid;
     uid_t uid = procp->uid;
     char *taskname;
@@ -1340,6 +1340,9 @@
 #ifdef LMKD_LOG_STATS
     struct memory_stat mem_st = {};
     int memory_stat_parse_result = -1;
+#else
+    /* To prevent unused parameter warning */
+    (void)(min_oom_score);
 #endif
 
     taskname = proc_get_name(pid);
@@ -1385,10 +1388,12 @@
         if (memory_stat_parse_result == 0) {
             stats_write_lmk_kill_occurred(log_ctx, LMK_KILL_OCCURRED, uid, taskname,
                     procp->oomadj, mem_st.pgfault, mem_st.pgmajfault, mem_st.rss_in_bytes,
-                    mem_st.cache_in_bytes, mem_st.swap_in_bytes, mem_st.process_start_time_ns);
+                    mem_st.cache_in_bytes, mem_st.swap_in_bytes, mem_st.process_start_time_ns,
+                    min_oom_score);
         } else if (enable_stats_log) {
             stats_write_lmk_kill_occurred(log_ctx, LMK_KILL_OCCURRED, uid, taskname, procp->oomadj,
-                                          -1, -1, tasksize * BYTES_IN_KILOBYTE, -1, -1, -1);
+                                          -1, -1, tasksize * BYTES_IN_KILOBYTE, -1, -1, -1,
+                                          min_oom_score);
         }
 #endif
         result = tasksize;
@@ -1425,7 +1430,7 @@
             if (!procp)
                 break;
 
-            killed_size = kill_one_process(procp);
+            killed_size = kill_one_process(procp, min_score_adj);
             if (killed_size >= 0) {
 #ifdef LMKD_LOG_STATS
                 if (enable_stats_log && !lmk_state_change_start) {
@@ -1694,16 +1699,6 @@
         static unsigned long report_skip_count = 0;
 
         if (!use_minfree_levels) {
-            /* If pressure level is less than critical and enough free swap then ignore */
-            if (level < VMPRESS_LEVEL_CRITICAL &&
-                mi.field.free_swap > low_pressure_mem.max_nr_free_pages) {
-                if (debug_process_killing) {
-                    ALOGI("Ignoring pressure since %" PRId64
-                          " swap pages are available ",
-                          mi.field.free_swap);
-                }
-                return;
-            }
             /* Free up enough memory to downgrate the memory pressure to low level */
             if (mi.field.nr_free_pages >= low_pressure_mem.max_nr_free_pages) {
                 if (debug_process_killing) {
diff --git a/lmkd/statslog.c b/lmkd/statslog.c
index 689e8ae..0c230ae 100644
--- a/lmkd/statslog.c
+++ b/lmkd/statslog.c
@@ -65,7 +65,8 @@
 stats_write_lmk_kill_occurred(android_log_context ctx, int32_t code, int32_t uid,
                               char const* process_name, int32_t oom_score, int64_t pgfault,
                               int64_t pgmajfault, int64_t rss_in_bytes, int64_t cache_in_bytes,
-                              int64_t swap_in_bytes, int64_t process_start_time_ns) {
+                              int64_t swap_in_bytes, int64_t process_start_time_ns,
+                              int32_t min_oom_score) {
     assert(ctx != NULL);
     int ret = -EINVAL;
     if (!ctx) {
@@ -117,5 +118,9 @@
         return ret;
     }
 
+    if ((ret = android_log_write_int32(ctx, min_oom_score)) < 0) {
+        return ret;
+    }
+
     return write_to_logger(ctx, LOG_ID_STATS);
 }
diff --git a/lmkd/statslog.h b/lmkd/statslog.h
index f3abe11..2edba7a 100644
--- a/lmkd/statslog.h
+++ b/lmkd/statslog.h
@@ -88,7 +88,8 @@
 stats_write_lmk_kill_occurred(android_log_context ctx, int32_t code, int32_t uid,
                               char const* process_name, int32_t oom_score, int64_t pgfault,
                               int64_t pgmajfault, int64_t rss_in_bytes, int64_t cache_in_bytes,
-                              int64_t swap_in_bytes, int64_t process_start_time_ns);
+                              int64_t swap_in_bytes, int64_t process_start_time_ns,
+                              int32_t min_oom_score);
 
 __END_DECLS
 
diff --git a/mkbootimg/unpack_bootimg.py b/mkbootimg/unpack_bootimg.py
index 6b5d5d0..789bf5e 100755
--- a/mkbootimg/unpack_bootimg.py
+++ b/mkbootimg/unpack_bootimg.py
@@ -48,12 +48,12 @@
     print('boot_magic: %s' % boot_magic)
     kernel_ramdisk_second_info = unpack('10I', args.boot_img.read(10 * 4))
     print('kernel_size: %s' % kernel_ramdisk_second_info[0])
-    print('kernel load address: %s' % kernel_ramdisk_second_info[1])
+    print('kernel load address: %#x' % kernel_ramdisk_second_info[1])
     print('ramdisk size: %s' % kernel_ramdisk_second_info[2])
-    print('ramdisk load address: %s' % kernel_ramdisk_second_info[3])
+    print('ramdisk load address: %#x' % kernel_ramdisk_second_info[3])
     print('second bootloader size: %s' % kernel_ramdisk_second_info[4])
-    print('second bootloader load address: %s' % kernel_ramdisk_second_info[5])
-    print('kernel tags load address: %s' % kernel_ramdisk_second_info[6])
+    print('second bootloader load address: %#x' % kernel_ramdisk_second_info[5])
+    print('kernel tags load address: %#x' % kernel_ramdisk_second_info[6])
     print('page size: %s' % kernel_ramdisk_second_info[7])
     print('boot image header version: %s' % kernel_ramdisk_second_info[8])
     print('os version and patch level: %s' % kernel_ramdisk_second_info[9])
@@ -77,7 +77,7 @@
         recovery_dtbo_size = unpack('I', args.boot_img.read(1 * 4))[0]
         print('recovery dtbo size: %s' % recovery_dtbo_size)
         recovery_dtbo_offset = unpack('Q', args.boot_img.read(8))[0]
-        print('recovery dtbo offset: %s' % recovery_dtbo_offset)
+        print('recovery dtbo offset: %#x' % recovery_dtbo_offset)
         boot_header_size = unpack('I', args.boot_img.read(4))[0]
         print('boot header size: %s' % boot_header_size)
     else:
@@ -86,7 +86,7 @@
         dtb_size = unpack('I', args.boot_img.read(4))[0]
         print('dtb size: %s' % dtb_size)
         dtb_load_address = unpack('Q', args.boot_img.read(8))[0]
-        print('dtb address: %s' % dtb_load_address)
+        print('dtb address: %#x' % dtb_load_address)
     else:
         dtb_size = 0
 
@@ -103,10 +103,11 @@
                                  ) # header + kernel
     image_info_list.append((ramdisk_offset, ramdisk_size, 'ramdisk'))
 
-    second_offset = page_size * (
-        num_header_pages + num_kernel_pages + num_ramdisk_pages
-    )  # header + kernel + ramdisk
-    image_info_list.append((second_offset, second_size, 'second'))
+    if second_size > 0:
+        second_offset = page_size * (
+                num_header_pages + num_kernel_pages + num_ramdisk_pages
+                )  # header + kernel + ramdisk
+        image_info_list.append((second_offset, second_size, 'second'))
 
     if recovery_dtbo_size > 0:
         image_info_list.append((recovery_dtbo_offset, recovery_dtbo_size,
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index b5e5a71..54b019e 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -24,75 +24,6 @@
 LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/init
 
-# Start of runtime APEX compatibility.
-#
-# Meta-comment:
-# The placing of this section is somewhat arbitrary. The LOCAL_POST_INSTALL_CMD
-# entries need to be associated with something that goes into /system.
-# init-debug.rc qualifies but it could be anything else in /system until soong
-# supports creation of symlinks. http://b/123333111
-#
-# Keeping the appearance of files/dirs having old locations for apps that have
-# come to rely on them.
-
-# http://b/121248172 - create a link from /system/usr/icu to
-# /apex/com.android.runtime/etc/icu so that apps can find the ICU .dat file.
-# A symlink can't overwrite a directory and the /system/usr/icu directory once
-# existed so the required structure must be created whatever we find.
-LOCAL_POST_INSTALL_CMD = mkdir -p $(TARGET_OUT)/usr && rm -rf $(TARGET_OUT)/usr/icu
-LOCAL_POST_INSTALL_CMD += ; ln -sf /apex/com.android.runtime/etc/icu $(TARGET_OUT)/usr/icu
-
-# TODO(b/124106384): Clean up compat symlinks for ART binaries.
-ART_BINARIES= \
-  dalvikvm32 \
-  dalvikvm64 \
-  dex2oat \
-  dexdiag \
-  dexdump \
-  dexlist \
-  dexoptanalyzer \
-  oatdump \
-  profman \
-
-$(foreach b,$(ART_BINARIES), \
-  $(eval LOCAL_POST_INSTALL_CMD += \
-    ; ln -sf /apex/com.android.runtime/bin/$(b) $(TARGET_OUT)/bin/$(b)) \
-)
-
-# End of runtime APEX compatibilty.
-
-include $(BUILD_PREBUILT)
-
-#######################################
-# cgroups.json
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := cgroups.json
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-
-include $(BUILD_PREBUILT)
-
-#######################################
-# cgroups.json for recovery
-include $(CLEAR_VARS)
-LOCAL_MODULE := cgroups.recovery.json
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/etc
-LOCAL_MODULE_STEM := cgroups.json
-include $(BUILD_PREBUILT)
-
-#######################################
-# task_profiles.json
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := task_profiles.json
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-
 include $(BUILD_PREBUILT)
 
 #######################################
@@ -222,6 +153,7 @@
 	@echo "Generate: $< -> $@"
 	@mkdir -p $(dir $@)
 	$(hide) sed -e 's?%BOOTCLASSPATH%?$(PRODUCT_BOOTCLASSPATH)?g' $< >$@
+	$(hide) sed -i -e 's?%DEX2OATBOOTCLASSPATH%?$(PRODUCT_DEX2OAT_BOOTCLASSPATH)?g' $@
 	$(hide) sed -i -e 's?%SYSTEMSERVERCLASSPATH%?$(PRODUCT_SYSTEM_SERVER_CLASSPATH)?g' $@
 	$(hide) sed -i -e 's?%EXPORT_GLOBAL_ASAN_OPTIONS%?$(EXPORT_GLOBAL_ASAN_OPTIONS)?g' $@
 	$(hide) sed -i -e 's?%EXPORT_GLOBAL_GCOV_OPTIONS%?$(EXPORT_GLOBAL_GCOV_OPTIONS)?g' $@
@@ -272,6 +204,45 @@
 LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
 
+# Start of runtime APEX compatibility.
+#
+# Meta-comment:
+# The placing of this section is somewhat arbitrary. The LOCAL_POST_INSTALL_CMD
+# entries need to be associated with something that goes into /system.
+# ld.config.txt qualifies but it could be anything else in /system until soong
+# supports creation of symlinks. http://b/123333111
+#
+# Keeping the appearance of files/dirs having old locations for apps that have
+# come to rely on them.
+
+# http://b/121248172 - create a link from /system/usr/icu to
+# /apex/com.android.runtime/etc/icu so that apps can find the ICU .dat file.
+# A symlink can't overwrite a directory and the /system/usr/icu directory once
+# existed so the required structure must be created whatever we find.
+LOCAL_POST_INSTALL_CMD = mkdir -p $(TARGET_OUT)/usr && rm -rf $(TARGET_OUT)/usr/icu
+LOCAL_POST_INSTALL_CMD += && ln -sf /apex/com.android.runtime/etc/icu $(TARGET_OUT)/usr/icu
+
+# TODO(b/124106384): Clean up compat symlinks for ART binaries.
+ART_BINARIES := \
+  dalvikvm \
+  dalvikvm32 \
+  dalvikvm64 \
+  dex2oat \
+  dexdiag \
+  dexdump \
+  dexlist \
+  dexoptanalyzer \
+  oatdump \
+  profman \
+
+LOCAL_POST_INSTALL_CMD += && mkdir -p $(TARGET_OUT)/bin
+$(foreach b,$(ART_BINARIES), \
+  $(eval LOCAL_POST_INSTALL_CMD += \
+    && ln -sf /apex/com.android.runtime/bin/$(b) $(TARGET_OUT)/bin/$(b)) \
+)
+
+# End of runtime APEX compatibilty.
+
 ifeq ($(_enforce_vndk_at_runtime),true)
 
 # for VNDK enforced devices
diff --git a/rootdir/etc/TEST_MAPPING b/rootdir/etc/TEST_MAPPING
index af2ec0f..e4d3d5e 100644
--- a/rootdir/etc/TEST_MAPPING
+++ b/rootdir/etc/TEST_MAPPING
@@ -1,7 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "bionic-unit-tests"
+      "name": "CtsBionicTestCases"
     }
   ]
 }
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index e897d81..fa46334 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -11,6 +11,11 @@
 dir.legacy = /odm
 dir.legacy = /sbin
 
+dir.legacy = /data/nativetest
+dir.legacy = /data/nativetest64
+dir.legacy = /data/benchmarktest
+dir.legacy = /data/benchmarktest64
+
 # Except for /postinstall, where only /system and /product are searched
 dir.postinstall = /postinstall
 
@@ -40,7 +45,7 @@
 # Keep in sync with ld.config.txt in the com.android.runtime APEX.
 # If a shared library or an executable requests a shared library that
 # cannot be loaded into the default namespace, the dynamic linker tries
-# to load the shared library from the runtime namespace.And then, if the
+# to load the shared library from the runtime namespace. And then, if the
 # shared library cannot be loaded from the runtime namespace either, the
 # dynamic linker tries to load the shared library from the resolv namespace.
 # Finally, if all attempts fail, the dynamic linker returns an error.
@@ -54,6 +59,10 @@
 namespace.default.link.runtime.shared_libs += libnativebridge.so
 namespace.default.link.runtime.shared_libs += libnativehelper.so
 namespace.default.link.runtime.shared_libs += libnativeloader.so
+namespace.default.link.runtime.shared_libs += libandroidicu.so
+
+# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+namespace.default.link.runtime.shared_libs += libpac.so
 
 # When libnetd_resolv.so can't be found in the default namespace, search for it
 # in the resolv namespace. Don't allow any other libraries from the resolv namespace
@@ -87,12 +96,24 @@
 namespace.media.search.paths = /apex/com.android.media/${LIB}
 namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
 
+namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
+
 namespace.media.links = default
-namespace.media.link.default.shared_libs  = %LLNDK_LIBRARIES%
-namespace.media.link.default.shared_libs += libandroid.so
+namespace.media.link.default.shared_libs  = libandroid.so
 namespace.media.link.default.shared_libs += libbinder_ndk.so
+namespace.media.link.default.shared_libs += libc.so
+namespace.media.link.default.shared_libs += libdl.so
+namespace.media.link.default.shared_libs += liblog.so
 namespace.media.link.default.shared_libs += libmediametrics.so
-namespace.media.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+namespace.media.link.default.shared_libs += libmediandk.so
+namespace.media.link.default.shared_libs += libm.so
+namespace.media.link.default.shared_libs += libvndksupport.so
+
+namespace.media.link.default.shared_libs += libclang_rt.asan-aarch64-android.so
+namespace.media.link.default.shared_libs += libclang_rt.asan-arm-android.so
+namespace.media.link.default.shared_libs += libclang_rt.asan-i686-android.so
+namespace.media.link.default.shared_libs += libclang_rt.asan-x86_64-android.so
+namespace.media.link.default.shared_libs += libclang_rt.hwasan-aarch64-android.so
 
 ###############################################################################
 # "conscrypt" APEX namespace
@@ -110,6 +131,7 @@
 namespace.conscrypt.link.default.shared_libs  = libc.so
 namespace.conscrypt.link.default.shared_libs += libm.so
 namespace.conscrypt.link.default.shared_libs += libdl.so
+namespace.conscrypt.link.default.shared_libs += liblog.so
 
 ###############################################################################
 # "resolv" APEX namespace
@@ -126,6 +148,7 @@
 namespace.resolv.link.default.shared_libs += libm.so
 namespace.resolv.link.default.shared_libs += libdl.so
 namespace.resolv.link.default.shared_libs += libbinder_ndk.so
+namespace.resolv.link.default.shared_libs += liblog.so
 namespace.resolv.link.default.shared_libs += libvndksupport.so
 
 ###############################################################################
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index df9abbd..927587b 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -68,6 +68,9 @@
 namespace.default.permitted.paths += /vendor/framework
 namespace.default.permitted.paths += /vendor/app
 namespace.default.permitted.paths += /vendor/priv-app
+namespace.default.permitted.paths += /system/vendor/framework
+namespace.default.permitted.paths += /system/vendor/app
+namespace.default.permitted.paths += /system/vendor/priv-app
 namespace.default.permitted.paths += /odm/framework
 namespace.default.permitted.paths += /odm/app
 namespace.default.permitted.paths += /odm/priv-app
@@ -80,7 +83,7 @@
 namespace.default.permitted.paths += /%PRODUCT_SERVICES%/priv-app
 namespace.default.permitted.paths += /data
 namespace.default.permitted.paths += /mnt/expand
-namespace.default.permitted.paths += /bionic/${LIB}
+namespace.default.permitted.paths += /apex/com.android.runtime/${LIB}/bionic
 namespace.default.permitted.paths += /system/${LIB}/bootstrap
 
 namespace.default.asan.search.paths  = /data/asan/system/${LIB}
@@ -100,6 +103,9 @@
 namespace.default.asan.permitted.paths += /vendor/framework
 namespace.default.asan.permitted.paths += /vendor/app
 namespace.default.asan.permitted.paths += /vendor/priv-app
+namespace.default.asan.permitted.paths += /system/vendor/framework
+namespace.default.asan.permitted.paths += /system/vendor/app
+namespace.default.asan.permitted.paths += /system/vendor/priv-app
 namespace.default.asan.permitted.paths += /odm/framework
 namespace.default.asan.permitted.paths += /odm/app
 namespace.default.asan.permitted.paths += /odm/priv-app
@@ -113,13 +119,13 @@
 namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/app
 namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/priv-app
 namespace.default.asan.permitted.paths += /mnt/expand
-namespace.default.asan.permitted.paths += /bionic/${LIB}
+namespace.default.asan.permitted.paths += /apex/com.android.runtime/${LIB}/bionic
 namespace.default.asan.permitted.paths += /system/${LIB}/bootstrap
 
 # Keep in sync with ld.config.txt in the com.android.runtime APEX.
 # If a shared library or an executable requests a shared library that
 # cannot be loaded into the default namespace, the dynamic linker tries
-# to load the shared library from the runtime namespace.And then, if the
+# to load the shared library from the runtime namespace. And then, if the
 # shared library cannot be loaded from the runtime namespace either, the
 # dynamic linker tries to load the shared library from the resolv namespace.
 # Finally, if all attempts fail, the dynamic linker returns an error.
@@ -132,6 +138,10 @@
 namespace.default.link.runtime.shared_libs += libnativebridge.so
 namespace.default.link.runtime.shared_libs += libnativehelper.so
 namespace.default.link.runtime.shared_libs += libnativeloader.so
+namespace.default.link.runtime.shared_libs += libandroidicu.so
+
+# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+namespace.default.link.runtime.shared_libs += libpac.so
 
 # When libnetd_resolv.so can't be found in the default namespace, search for it
 # in the resolv namespace. Don't allow any other libraries from the resolv namespace
@@ -165,6 +175,8 @@
 namespace.media.search.paths = /apex/com.android.media/${LIB}
 namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
 
+namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
+
 namespace.media.links = default
 namespace.media.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.media.link.default.shared_libs += libandroid.so
@@ -188,6 +200,7 @@
 namespace.conscrypt.link.default.shared_libs  = libc.so
 namespace.conscrypt.link.default.shared_libs += libm.so
 namespace.conscrypt.link.default.shared_libs += libdl.so
+namespace.conscrypt.link.default.shared_libs += liblog.so
 
 ###############################################################################
 # "resolv" APEX namespace
@@ -204,6 +217,7 @@
 namespace.resolv.link.default.shared_libs += libm.so
 namespace.resolv.link.default.shared_libs += libdl.so
 namespace.resolv.link.default.shared_libs += libbinder_ndk.so
+namespace.resolv.link.default.shared_libs += liblog.so
 namespace.resolv.link.default.shared_libs += libvndksupport.so
 
 ###############################################################################
@@ -228,6 +242,7 @@
 
 namespace.sphal.permitted.paths  = /odm/${LIB}
 namespace.sphal.permitted.paths += /vendor/${LIB}
+namespace.sphal.permitted.paths += /system/vendor/${LIB}
 
 namespace.sphal.asan.search.paths  = /data/asan/odm/${LIB}
 namespace.sphal.asan.search.paths +=           /odm/${LIB}
@@ -270,6 +285,7 @@
 
 namespace.rs.permitted.paths  = /odm/${LIB}
 namespace.rs.permitted.paths += /vendor/${LIB}
+namespace.rs.permitted.paths += /system/vendor/${LIB}
 namespace.rs.permitted.paths += /data
 
 namespace.rs.asan.search.paths  = /data/asan/odm/${LIB}/vndk-sp
@@ -315,6 +331,8 @@
 namespace.vndk.permitted.paths += /odm/${LIB}/egl
 namespace.vndk.permitted.paths += /vendor/${LIB}/hw
 namespace.vndk.permitted.paths += /vendor/${LIB}/egl
+namespace.vndk.permitted.paths += /system/vendor/${LIB}/hw
+namespace.vndk.permitted.paths += /system/vendor/${LIB}/egl
 # This is exceptionally required since android.hidl.memory@1.0-impl.so is here
 namespace.vndk.permitted.paths += /system/${LIB}/vndk-sp%VNDK_VER%/hw
 
@@ -359,7 +377,7 @@
 # (LL-NDK only) access.
 ###############################################################################
 [vendor]
-additional.namespaces = runtime,system,vndk
+additional.namespaces = runtime,system,vndk%VNDK_IN_SYSTEM_NS%
 
 ###############################################################################
 # "default" namespace
@@ -382,6 +400,7 @@
 
 namespace.default.permitted.paths  = /odm
 namespace.default.permitted.paths += /vendor
+namespace.default.permitted.paths += /system/vendor
 #VNDK27#namespace.default.search.paths += /vendor/${LIB}/hw
 #VNDK27#namespace.default.search.paths += /vendor/${LIB}/egl
 
@@ -399,8 +418,9 @@
 namespace.default.asan.permitted.paths += /data/asan/vendor
 namespace.default.asan.permitted.paths +=           /vendor
 
-namespace.default.links = system,vndk
+namespace.default.links = system,vndk%VNDK_IN_SYSTEM_NS%
 namespace.default.link.system.shared_libs = %LLNDK_LIBRARIES%
+namespace.default.link.vndk_in_system.shared_libs = %VNDK_USING_CORE_VARIANT_LIBRARIES%
 namespace.default.link.vndk.shared_libs  = %VNDK_SAMEPROCESS_LIBRARIES%
 namespace.default.link.vndk.shared_libs += %VNDK_CORE_LIBRARIES%
 
@@ -449,14 +469,18 @@
 
 # When these NDK libs are required inside this namespace, then it is redirected
 # to the system namespace. This is possible since their ABI is stable across
-# Android releases.
-namespace.vndk.links = system,default
+# Android releases.  The links here should be identical to that of the
+# 'vndk_in_system' namespace, except for the link between 'vndk' and
+# 'vndk_in_system'.
+namespace.vndk.links = system,default%VNDK_IN_SYSTEM_NS%
 
 namespace.vndk.link.system.shared_libs  = %LLNDK_LIBRARIES%
 namespace.vndk.link.system.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
 
 namespace.vndk.link.default.allow_all_shared_libs = true
 
+namespace.vndk.link.vndk_in_system.shared_libs = %VNDK_USING_CORE_VARIANT_LIBRARIES%
+
 ###############################################################################
 # "system" namespace
 #
@@ -481,6 +505,52 @@
 namespace.system.link.runtime.shared_libs += libnativebridge.so
 namespace.system.link.runtime.shared_libs += libnativehelper.so
 namespace.system.link.runtime.shared_libs += libnativeloader.so
+# Workaround for b/124772622
+namespace.system.link.runtime.shared_libs += libandroidicu.so
+
+###############################################################################
+# "vndk_in_system" namespace
+#
+# This namespace is where no-vendor-variant VNDK libraries are loaded for a
+# vendor process.  Note that we do not simply export these libraries from
+# "system" namespace, because in some case both the core variant and the
+# vendor variant of a VNDK library may be loaded.  In such case, we do not
+# want to eliminate double-loading because doing so means the global states
+# of the library would be shared.
+#
+# Only the no-vendor-variant VNDK libraries are whitelisted in this namespace.
+# This is to ensure that we do not load libraries needed by no-vendor-variant
+# VNDK libraries into vndk_in_system namespace.
+###############################################################################
+namespace.vndk_in_system.isolated = true
+namespace.vndk_in_system.visible = true
+
+# The search paths here should be kept the same as that of the 'system'
+# namespace.
+namespace.vndk_in_system.search.paths  = /system/${LIB}
+namespace.vndk_in_system.search.paths += /%PRODUCT%/${LIB}
+namespace.vndk_in_system.search.paths += /%PRODUCT_SERVICES%/${LIB}
+
+namespace.vndk_in_system.asan.search.paths  = /data/asan/system/${LIB}
+namespace.vndk_in_system.asan.search.paths +=           /system/${LIB}
+namespace.vndk_in_system.asan.search.paths += /data/asan/product/${LIB}
+namespace.vndk_in_system.asan.search.paths +=           /%PRODUCT%/${LIB}
+namespace.vndk_in_system.asan.search.paths += /data/asan/product_services/${LIB}
+namespace.vndk_in_system.asan.search.paths +=           /%PRODUCT_SERVICES%/${LIB}
+
+namespace.vndk_in_system.whitelisted = %VNDK_USING_CORE_VARIANT_LIBRARIES%
+
+# The links here should be identical to that of the 'vndk' namespace, with the
+# following exception:
+#   1. 'vndk_in_system' needs to be freely linked back to 'vndk'.
+#   2. 'vndk_in_system' does not need to link to 'default', as any library that
+#      requires anything vendor would not be a vndk_in_system library.
+namespace.vndk_in_system.links = vndk,system
+
+namespace.vndk_in_system.link.system.shared_libs  = %LLNDK_LIBRARIES%
+namespace.vndk_in_system.link.system.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+namespace.vndk_in_system.link.vndk.allow_all_shared_libs = true
 
 
 ###############################################################################
@@ -546,6 +616,8 @@
 namespace.media.search.paths = /apex/com.android.media/${LIB}
 namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
 
+namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
+
 namespace.media.links = default
 namespace.media.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.media.link.default.shared_libs += libandroid.so
@@ -569,6 +641,7 @@
 namespace.conscrypt.link.default.shared_libs  = libc.so
 namespace.conscrypt.link.default.shared_libs += libm.so
 namespace.conscrypt.link.default.shared_libs += libdl.so
+namespace.conscrypt.link.default.shared_libs += liblog.so
 
 ###############################################################################
 # "resolv" APEX namespace
@@ -585,6 +658,7 @@
 namespace.resolv.link.default.shared_libs += libm.so
 namespace.resolv.link.default.shared_libs += libdl.so
 namespace.resolv.link.default.shared_libs += libbinder_ndk.so
+namespace.resolv.link.default.shared_libs += liblog.so
 
 
 ###############################################################################
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 3c97a49..beb6125 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -20,6 +20,13 @@
 dir.vendor = /data/benchmarktest/vendor
 dir.vendor = /data/benchmarktest64/vendor
 
+dir.unrestricted = /data/nativetest/unrestricted
+dir.unrestricted = /data/nativetest64/unrestricted
+
+# TODO(b/123864775): Ensure tests are run from /data/nativetest{,64} or (if
+# necessary) the unrestricted subdirs above. Then clean this up.
+dir.unrestricted = /data/local/tmp
+
 dir.system = /data/nativetest
 dir.system = /data/nativetest64
 dir.system = /data/benchmarktest
@@ -59,7 +66,7 @@
 # ld.config.txt.
 # If a shared library or an executable requests a shared library that
 # cannot be loaded into the default namespace, the dynamic linker tries
-# to load the shared library from the runtime namespace.And then, if the
+# to load the shared library from the runtime namespace. And then, if the
 # shared library cannot be loaded from the runtime namespace either, the
 # dynamic linker tries to load the shared library from the resolv namespace.
 # Finally, if all attempts fail, the dynamic linker returns an error.
@@ -72,6 +79,10 @@
 namespace.default.link.runtime.shared_libs += libnativebridge.so
 namespace.default.link.runtime.shared_libs += libnativehelper.so
 namespace.default.link.runtime.shared_libs += libnativeloader.so
+namespace.default.link.runtime.shared_libs += libandroidicu.so
+
+# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+namespace.default.link.runtime.shared_libs += libpac.so
 
 # When libnetd_resolv.so can't be found in the default namespace, search for it
 # in the resolv namespace. Don't allow any other libraries from the resolv namespace
@@ -106,6 +117,8 @@
 namespace.media.search.paths = /apex/com.android.media/${LIB}
 namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
 
+namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
+
 namespace.media.links = default
 namespace.media.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.media.link.default.shared_libs += libandroid.so
@@ -129,6 +142,7 @@
 namespace.conscrypt.link.default.shared_libs  = libc.so
 namespace.conscrypt.link.default.shared_libs += libm.so
 namespace.conscrypt.link.default.shared_libs += libdl.so
+namespace.conscrypt.link.default.shared_libs += liblog.so
 
 ###############################################################################
 # "resolv" APEX namespace
@@ -145,6 +159,7 @@
 namespace.resolv.link.default.shared_libs += libm.so
 namespace.resolv.link.default.shared_libs += libdl.so
 namespace.resolv.link.default.shared_libs += libbinder_ndk.so
+namespace.resolv.link.default.shared_libs += liblog.so
 namespace.resolv.link.default.shared_libs += libvndksupport.so
 
 ###############################################################################
@@ -169,6 +184,7 @@
 
 namespace.sphal.permitted.paths  = /odm/${LIB}
 namespace.sphal.permitted.paths += /vendor/${LIB}
+namespace.sphal.permitted.paths += /system/vendor/${LIB}
 
 namespace.sphal.asan.search.paths  = /data/asan/odm/${LIB}
 namespace.sphal.asan.search.paths +=           /odm/${LIB}
@@ -211,6 +227,7 @@
 
 namespace.rs.permitted.paths  = /odm/${LIB}
 namespace.rs.permitted.paths += /vendor/${LIB}
+namespace.rs.permitted.paths += /system/vendor/${LIB}
 namespace.rs.permitted.paths += /data
 
 namespace.rs.asan.search.paths  = /data/asan/odm/${LIB}/vndk-sp
@@ -256,6 +273,7 @@
 namespace.vndk.permitted.paths += /odm/${LIB}/egl
 namespace.vndk.permitted.paths += /vendor/${LIB}/hw
 namespace.vndk.permitted.paths += /vendor/${LIB}/egl
+namespace.vndk.permitted.paths += /system/vendor/${LIB}/egl
 # This is exceptionally required since android.hidl.memory@1.0-impl.so is here
 namespace.vndk.permitted.paths += /system/${LIB}/vndk-sp%VNDK_VER%/hw
 
@@ -340,6 +358,8 @@
 namespace.default.link.runtime.shared_libs += libnativebridge.so
 namespace.default.link.runtime.shared_libs += libnativehelper.so
 namespace.default.link.runtime.shared_libs += libnativeloader.so
+# Workaround for b/124772622
+namespace.default.link.runtime.shared_libs += libandroidicu.so
 
 ###############################################################################
 # "runtime" APEX namespace
@@ -357,6 +377,111 @@
 namespace.runtime.link.default.allow_all_shared_libs = true
 
 ###############################################################################
+# Namespace config for native tests that need access to both system and vendor
+# libraries. This replicates the default linker config (done by
+# init_default_namespace_no_config in bionic/linker/linker.cpp), except that it
+# includes the requisite namespace setup for APEXes.
+###############################################################################
+[unrestricted]
+additional.namespaces = runtime,media,conscrypt,resolv
+
+namespace.default.search.paths  = /system/${LIB}
+namespace.default.search.paths += /odm/${LIB}
+namespace.default.search.paths += /vendor/${LIB}
+
+namespace.default.asan.search.paths  = /data/asan/system/${LIB}
+namespace.default.asan.search.paths +=           /system/${LIB}
+namespace.default.asan.search.paths += /data/asan/odm/${LIB}
+namespace.default.asan.search.paths +=           /odm/${LIB}
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.default.asan.search.paths +=           /vendor/${LIB}
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+namespace.default.links = runtime,resolv
+namespace.default.visible = true
+
+namespace.default.link.runtime.shared_libs  = libart.so:libartd.so
+namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libnativebridge.so
+namespace.default.link.runtime.shared_libs += libnativehelper.so
+namespace.default.link.runtime.shared_libs += libnativeloader.so
+namespace.default.link.runtime.shared_libs += libandroidicu.so
+
+# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+namespace.default.link.runtime.shared_libs += libpac.so
+
+namespace.default.link.resolv.shared_libs = libnetd_resolv.so
+
+###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace exposes externally accessible libraries from the Runtime APEX.
+###############################################################################
+namespace.runtime.isolated = true
+namespace.runtime.visible = true
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = default
+# TODO(b/119867084): Restrict to Bionic dlopen dependencies and PALette library
+# when it exists.
+namespace.runtime.link.default.allow_all_shared_libs = true
+
+###############################################################################
+# "media" APEX namespace
+#
+# This namespace is for libraries within the media APEX.
+###############################################################################
+namespace.media.isolated = true
+namespace.media.visible = true
+
+namespace.media.search.paths = /apex/com.android.media/${LIB}
+namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
+
+namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
+
+namespace.media.links = default
+namespace.media.link.default.shared_libs  = %LLNDK_LIBRARIES%
+namespace.media.link.default.shared_libs += libandroid.so
+namespace.media.link.default.shared_libs += libbinder_ndk.so
+namespace.media.link.default.shared_libs += libmediametrics.so
+namespace.media.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+###############################################################################
+# "conscrypt" APEX namespace
+#
+# This namespace is for libraries within the conscrypt APEX.
+###############################################################################
+namespace.conscrypt.isolated = true
+namespace.conscrypt.visible = true
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.links = runtime,default
+namespace.conscrypt.link.runtime.shared_libs  = libjavacore.so
+namespace.conscrypt.link.default.shared_libs  = libc.so
+namespace.conscrypt.link.default.shared_libs += libm.so
+namespace.conscrypt.link.default.shared_libs += libdl.so
+
+###############################################################################
+# "resolv" APEX namespace
+#
+# This namespace is for libraries within the resolv APEX.
+###############################################################################
+namespace.resolv.isolated = true
+namespace.resolv.visible = true
+
+namespace.resolv.search.paths = /apex/com.android.resolv/${LIB}
+namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
+namespace.resolv.links = default
+namespace.resolv.link.default.shared_libs  = libc.so
+namespace.resolv.link.default.shared_libs += libm.so
+namespace.resolv.link.default.shared_libs += libdl.so
+namespace.resolv.link.default.shared_libs += libbinder_ndk.so
+
+###############################################################################
 # Namespace config for binaries under /postinstall.
 # Only default namespace is defined and default has no directories
 # other than /system/lib in the search paths. This is because linker calls
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index d10f7c1..455c9a8 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -1,14 +1,16 @@
 # set up the global environment
-on init
+on early-init
     export ANDROID_BOOTLOGO 1
     export ANDROID_ROOT /system
     export ANDROID_ASSETS /system/app
     export ANDROID_DATA /data
     export ANDROID_STORAGE /storage
     export ANDROID_RUNTIME_ROOT /apex/com.android.runtime
+    export ANDROID_TZDATA_ROOT /apex/com.android.tzdata
     export EXTERNAL_STORAGE /sdcard
     export ASEC_MOUNTPOINT /mnt/asec
     export BOOTCLASSPATH %BOOTCLASSPATH%
+    export DEX2OATBOOTCLASSPATH %DEX2OATBOOTCLASSPATH%
     export SYSTEMSERVERCLASSPATH %SYSTEMSERVERCLASSPATH%
     %EXPORT_GLOBAL_ASAN_OPTIONS%
     %EXPORT_GLOBAL_GCOV_OPTIONS%
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 57032bc..8e63a81 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -13,12 +13,6 @@
 
 # Cgroups are mounted right before early-init using list from /etc/cgroups.json
 on early-init
-    # Mount shared so changes propagate into child namespaces
-    # Do this before other processes are started from init. Otherwise,
-    # processes launched while the propagation type of / is 'private'
-    # won't get mount events from others.
-    mount rootfs rootfs / shared rec
-
     # Set init and its forked children's oom_adj.
     write /proc/1/oom_score_adj -1000
 
@@ -43,6 +37,11 @@
 
     start ueventd
 
+    # Run apexd-bootstrap so that APEXes that provide critical libraries
+    # become available. Note that this is executed as exec_start to ensure that
+    # the libraries are available to the processes started after this statement.
+    exec_start apexd-bootstrap
+
 on init
     sysclktz 0
 
@@ -84,6 +83,15 @@
     chmod 0664 /dev/stune/top-app/tasks
     chmod 0664 /dev/stune/rt/tasks
 
+    # Create blkio tuning nodes
+    mkdir /dev/blkio/background
+    chown system system /dev/blkio
+    chown system system /dev/blkio/background
+    chown system system /dev/blkio/tasks
+    chown system system /dev/blkio/background/tasks
+    chmod 0664 /dev/blkio/tasks
+    chmod 0664 /dev/blkio/background/tasks
+
     restorecon_recursive /mnt
 
     mount configfs none /config nodev noexec nosuid
@@ -269,18 +277,9 @@
     write /dev/cpu_variant:${ro.bionic.2nd_arch} ${ro.bionic.2nd_cpu_variant}
     chmod 0444 /dev/cpu_variant:${ro.bionic.2nd_arch}
 
-    # Setup APEX mount point and its security context
-    mount tmpfs tmpfs /apex nodev noexec nosuid
-    chmod 0755 /apex
-    chown root root /apex
-    restorecon /apex
-
     # Start logd before any other services run to ensure we capture all of their logs.
     start logd
 
-    # Start apexd as soon as we can
-    start apexd
-
     # Start essential services.
     start servicemanager
     start hwservicemanager
@@ -388,7 +387,10 @@
     restorecon_recursive /metadata
     mkdir /metadata/vold
     chmod 0700 /metadata/vold
+    mkdir /metadata/password_slots 0771 root system
 
+    mkdir /metadata/apex 0700 root system
+    mkdir /metadata/apex/sessions 0700 root system
 on late-fs
     # Ensure that tracefs has the correct permissions.
     # This does not work correctly if it is called in post-fs.
@@ -416,8 +418,25 @@
     mkdir /data/bootchart 0755 shell shell
     bootchart start
 
-    # /data/apex is now available. Let apexd to scan and activate APEXes.
-    setprop apexd.data.status ready
+    # Load fsverity keys. This needs to happen before apexd, as post-install of
+    # APEXes may rely on keys.
+    exec -- /system/bin/mini-keyctl dadd asymmetric product_cert /product/etc/security/cacerts_fsverity .fs-verity
+    exec -- /system/bin/mini-keyctl dadd asymmetric vendor_cert /vendor/etc/security/cacerts_fsverity .fs-verity
+    # Prevent future key links to fsverity keyring
+    exec -- /system/bin/mini-keyctl restrict_keyring .fs-verity
+    # Enforce fsverity signature checking
+    write /proc/sys/fs/verity/require_signatures 1
+
+    # Make sure that apexd is started in the default namespace
+    enter_default_mount_ns
+
+    # /data/apex is now available. Start apexd to scan and activate APEXes.
+    mkdir /data/apex 0750 root system
+    mkdir /data/apex/active 0750 root system
+    mkdir /data/apex/backup 0700 root system
+    mkdir /data/apex/sessions 0700 root system
+    mkdir /data/app-staging 0750 system system
+    start apexd
 
     # Avoid predictable entropy pool. Carry over entropy from previous boot.
     copy /data/system/entropy.dat /dev/urandom
@@ -534,12 +553,6 @@
 
     mkdir /data/anr 0775 system system
 
-    mkdir /data/apex 0750 root system
-    mkdir /data/apex/active 0750 root system
-    mkdir /data/apex/backup 0700 root system
-    mkdir /data/apex/sessions 0700 root system
-    mkdir /data/staging 0750 system system
-
     # NFC: create data/nfc for nv storage
     mkdir /data/nfc 0770 nfc nfc
     mkdir /data/nfc/param 0770 nfc nfc
@@ -572,6 +585,10 @@
     mkdir /data/cache/backup_stage 0700 system system
     mkdir /data/cache/backup 0700 system system
 
+    # Wait for apexd to finish activating APEXes before starting more processes.
+    wait_for_prop apexd.status ready
+    parse_apex_configs
+
     init_user0
 
     # Set SELinux security contexts on upgrade or policy update.
@@ -580,17 +597,6 @@
     # Check any timezone data in /data is newer than the copy in the runtime module, delete if not.
     exec - system system -- /system/bin/tzdatacheck /apex/com.android.runtime/etc/tz /data/misc/zoneinfo
 
-    # load fsverity keys
-    exec -- /system/bin/mini-keyctl -c /product/etc/security/cacerts_fsverity,/vendor/etc/security/cacerts_fsverity -k .fs-verity
-
-    # Wait for apexd to finish activating APEXes before starting more processes.
-    # This certainly reduces the parallelism but is required to make as many processes
-    # as possible to use the bionic libs from the runtime APEX. This takes less than 50ms
-    # so the impact on the booting time is not significant.
-    wait_for_prop apexd.status ready
-    setup_runtime_bionic
-    parse_apex_configs
-
     # If there is no post-fs-data action in the init.<device>.rc file, you
     # must uncomment this line, otherwise encrypted filesystems
     # won't work.
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index e8c5d8e..f8e680d 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -5,7 +5,6 @@
     group root readproc reserved_disk
     socket zygote stream 660 root system
     socket blastula_pool stream 660 root system
-    updatable
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
     onrestart restart audioserver
diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc
index 9c7e807..0235370 100644
--- a/rootdir/init.zygote32_64.rc
+++ b/rootdir/init.zygote32_64.rc
@@ -5,7 +5,6 @@
     group root readproc reserved_disk
     socket zygote stream 660 root system
     socket blastula_pool stream 660 root system
-    updatable
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
     onrestart restart audioserver
@@ -22,6 +21,5 @@
     group root readproc reserved_disk
     socket zygote_secondary stream 660 root system
     socket blastula_pool_secondary stream 660 root system
-    updatable
     onrestart restart zygote
     writepid /dev/cpuset/foreground/tasks
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 9908c99..3f3cc15 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -5,7 +5,6 @@
     group root readproc reserved_disk
     socket zygote stream 660 root system
     socket blastula_pool stream 660 root system
-    updatable
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
     onrestart restart audioserver
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index 0b5edff..fae38c9 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -5,7 +5,6 @@
     group root readproc reserved_disk
     socket zygote stream 660 root system
     socket blastula_pool stream 660 root system
-    updatable
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
     onrestart restart audioserver
@@ -22,6 +21,5 @@
     group root readproc reserved_disk
     socket zygote_secondary stream 660 root system
     socket blastula_pool_secondary stream 660 root system
-    updatable
     onrestart restart zygote
     writepid /dev/cpuset/foreground/tasks
diff --git a/rootdir/update_and_install_ld_config.mk b/rootdir/update_and_install_ld_config.mk
index 852e234..912a73e 100644
--- a/rootdir/update_and_install_ld_config.mk
+++ b/rootdir/update_and_install_ld_config.mk
@@ -23,6 +23,11 @@
 lib_list_from_prebuilts := $(strip $(lib_list_from_prebuilts))
 libz_is_llndk := $(strip $(libz_is_llndk))
 
+my_vndk_use_core_variant := $(TARGET_VNDK_USE_CORE_VARIANT)
+ifeq ($(lib_list_from_prebuilts),true)
+my_vndk_use_core_variant := false
+endif
+
 compatibility_check_script := \
   $(LOCAL_PATH)/ld_config_backward_compatibility_check.py
 intermediates_dir := $(call intermediates-dir-for,ETC,$(LOCAL_MODULE))
@@ -35,6 +40,9 @@
 vndksp_libraries_file := $(library_lists_dir)/vndksp.libraries.$(vndk_version).txt
 vndkcore_libraries_file := $(library_lists_dir)/vndkcore.libraries.txt
 vndkprivate_libraries_file := $(library_lists_dir)/vndkprivate.libraries.txt
+ifeq ($(my_vndk_use_core_variant),true)
+vndk_using_core_variant_libraries_file := $(library_lists_dir)/vndk_using_core_variant.libraries.$(vndk_version).txt
+endif
 
 sanitizer_runtime_libraries := $(call normalize-path-list,$(addsuffix .so,\
   $(ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
@@ -68,6 +76,9 @@
 $(eval $(call write-libs-to-file,$(vndksp_libraries_list),$(vndksp_libraries_file)))
 $(eval $(call write-libs-to-file,$(VNDK_CORE_LIBRARIES),$(vndkcore_libraries_file)))
 $(eval $(call write-libs-to-file,$(VNDK_PRIVATE_LIBRARIES),$(vndkprivate_libraries_file)))
+ifeq ($(my_vndk_use_core_variant),true)
+$(eval $(call write-libs-to-file,$(VNDK_USING_CORE_VARIANT_LIBRARIES),$(vndk_using_core_variant_libraries_file)))
+endif
 endif # ifneq ($(lib_list_from_prebuilts),true)
 
 # Given a file with a list of libs, filter-out the VNDK private libraries
@@ -94,6 +105,10 @@
 ifeq ($(check_backward_compatibility),true)
 deps += $(compatibility_check_script)
 endif
+ifeq ($(my_vndk_use_core_variant),true)
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_USING_CORE_VARIANT_LIBRARIES_FILE := $(vndk_using_core_variant_libraries_file)
+deps += $(vndk_using_core_variant_libraries_file)
+endif
 
 $(LOCAL_BUILT_MODULE): $(ld_config_template) $(deps)
 	@echo "Generate: $< -> $@"
@@ -109,6 +124,20 @@
 	$(call private-filter-out-private-libs,$(PRIVATE_VNDK_CORE_LIBRARIES_FILE),$(PRIVATE_INTERMEDIATES_DIR)/vndkcore_filtered)
 	$(hide) sed -i.bak -e "s?%VNDK_CORE_LIBRARIES%?$$(cat $(PRIVATE_INTERMEDIATES_DIR)/vndkcore_filtered)?g" $@
 
+ifeq ($(my_vndk_use_core_variant),true)
+	$(call private-filter-out-private-libs,$(PRIVATE_VNDK_USING_CORE_VARIANT_LIBRARIES_FILE),$(PRIVATE_INTERMEDIATES_DIR)/vndk_using_core_variant_filtered)
+	$(hide) sed -i.bak -e "s?%VNDK_IN_SYSTEM_NS%?,vndk_in_system?g" $@
+	$(hide) sed -i.bak -e "s?%VNDK_USING_CORE_VARIANT_LIBRARIES%?$$(cat $(PRIVATE_INTERMEDIATES_DIR)/vndk_using_core_variant_filtered)?g" $@
+else
+	$(hide) sed -i.bak -e "s?%VNDK_IN_SYSTEM_NS%??g" $@
+	# Unlike LLNDK or VNDK-SP, VNDK_USING_CORE_VARIANT_LIBRARIES can be nothing
+	# if TARGET_VNDK_USE_CORE_VARIANT is not set.  In this case, we need to remove
+	# the entire line in the linker config so that we are not left with a line
+	# like:
+	#   namespace.sphal.link.default.shared_libs +=
+	$(hide) sed -i.bak -e "s?^.*\+= %VNDK_USING_CORE_VARIANT_LIBRARIES%\$$??" $@
+endif
+
 	$(hide) echo -n > $(PRIVATE_INTERMEDIATES_DIR)/private_llndk && \
 	cat $(PRIVATE_VNDK_PRIVATE_LIBRARIES_FILE) | \
 	xargs -n 1 -I privatelib bash -c "(grep privatelib $(PRIVATE_LLNDK_LIBRARIES_FILE) || true) >> $(PRIVATE_INTERMEDIATES_DIR)/private_llndk" && \
@@ -146,3 +175,10 @@
 llndk_libraries_list :=
 vndksp_libraries_list :=
 write-libs-to-file :=
+
+ifeq ($(my_vndk_use_core_variant),true)
+vndk_using_core_variant_libraries_file :=
+vndk_using_core_variant_libraries_list :=
+endif
+
+my_vndk_use_core_variant :=
diff --git a/run-as/run-as.cpp b/run-as/run-as.cpp
index 8752eef..432c434 100644
--- a/run-as/run-as.cpp
+++ b/run-as/run-as.cpp
@@ -70,32 +70,40 @@
   return true; // Keep searching.
 }
 
-static bool check_directory(const char* path, uid_t uid) {
+static void check_directory(const char* path, uid_t uid) {
   struct stat st;
-  if (TEMP_FAILURE_RETRY(lstat(path, &st)) == -1) return false;
+  if (TEMP_FAILURE_RETRY(lstat(path, &st)) == -1) {
+    error(1, errno, "couldn't stat %s", path);
+  }
 
   // /data/user/0 is a known safe symlink.
-  if (strcmp("/data/user/0", path) == 0) return true;
+  if (strcmp("/data/user/0", path) == 0) return;
 
   // Must be a real directory, not a symlink.
-  if (!S_ISDIR(st.st_mode)) return false;
+  if (!S_ISDIR(st.st_mode)) {
+    error(1, 0, "%s not a directory: %o", path, st.st_mode);
+  }
 
   // Must be owned by specific uid/gid.
-  if (st.st_uid != uid || st.st_gid != uid) return false;
+  if (st.st_uid != uid || st.st_gid != uid) {
+    error(1, 0, "%s has wrong owner: %d/%d, not %d", path, st.st_uid, st.st_gid, uid);
+  }
 
   // Must not be readable or writable by others.
-  if ((st.st_mode & (S_IROTH|S_IWOTH)) != 0) return false;
-
-  return true;
+  if ((st.st_mode & (S_IROTH | S_IWOTH)) != 0) {
+    error(1, 0, "%s readable or writable by others: %o", path, st.st_mode);
+  }
 }
 
 // This function is used to check the data directory path for safety.
 // We check that every sub-directory is owned by the 'system' user
 // and exists and is not a symlink. We also check that the full directory
 // path is properly owned by the user ID.
-static bool check_data_path(const char* data_path, uid_t uid) {
+static void check_data_path(const char* package_name, const char* data_path, uid_t uid) {
   // The path should be absolute.
-  if (data_path[0] != '/') return false;
+  if (data_path[0] != '/') {
+    error(1, 0, "%s data path not absolute: %s", package_name, data_path);
+  }
 
   // Look for all sub-paths, we do that by finding
   // directory separators in the input path and
@@ -110,26 +118,28 @@
     if (data_path[nn+1] == '\0') break;
 
     /* found a separator, check that data_path is not too long. */
-    if (nn >= (int)(sizeof subpath)) return false;
+    if (nn >= (int)(sizeof subpath)) {
+      error(1, 0, "%s data path too long: %s", package_name, data_path);
+    }
 
     /* reject any '..' subpath */
     if (nn >= 3               &&
         data_path[nn-3] == '/' &&
         data_path[nn-2] == '.' &&
         data_path[nn-1] == '.') {
-      return false;
+      error(1, 0, "%s contains '..': %s", package_name, data_path);
     }
 
     /* copy to 'subpath', then check ownership */
     memcpy(subpath, data_path, nn);
     subpath[nn] = '\0';
 
-    if (!check_directory(subpath, AID_SYSTEM)) return false;
+    check_directory(subpath, AID_SYSTEM);
   }
 
   // All sub-paths were checked, now verify that the full data
   // directory is owned by the application uid.
-  return check_directory(data_path, uid);
+  check_directory(data_path, uid);
 }
 
 std::vector<gid_t> get_supplementary_gids(uid_t userAppId) {
@@ -222,9 +232,7 @@
   }
 
   // Check that the data directory path is valid.
-  if (!check_data_path(info.data_dir, userAppId)) {
-    error(1, 0, "package has corrupt installation: %s", pkgname);
-  }
+  check_data_path(pkgname, info.data_dir, userAppId);
 
   // Ensure that we change all real/effective/saved IDs at the
   // same time to avoid nasty surprises.
diff --git a/sdcard/sdcard.cpp b/sdcard/sdcard.cpp
index e1de130..2b35819 100644
--- a/sdcard/sdcard.cpp
+++ b/sdcard/sdcard.cpp
@@ -27,6 +27,7 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <vector>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -99,14 +100,21 @@
 
 static bool sdcardfs_setup(const std::string& source_path, const std::string& dest_path,
                            uid_t fsuid, gid_t fsgid, bool multi_user, userid_t userid, gid_t gid,
-                           mode_t mask, bool derive_gid, bool default_normal, bool use_esdfs) {
+                           mode_t mask, bool derive_gid, bool default_normal, bool unshared_obb,
+                           bool use_esdfs) {
+    // Add new options at the end of the vector.
+    std::vector<std::string> new_opts_list;
+    if (multi_user) new_opts_list.push_back("multiuser,");
+    if (derive_gid) new_opts_list.push_back("derive_gid,");
+    if (default_normal) new_opts_list.push_back("default_normal,");
+    if (unshared_obb) new_opts_list.push_back("unshared_obb,");
     // Try several attempts, each time with one less option, to gracefully
     // handle older kernels that aren't updated yet.
-    for (int i = 0; i < 4; i++) {
+    for (int i = 0; i <= new_opts_list.size(); ++i) {
         std::string new_opts;
-        if (multi_user && i < 3) new_opts += "multiuser,";
-        if (derive_gid && i < 2) new_opts += "derive_gid,";
-        if (default_normal && i < 1) new_opts += "default_normal,";
+        for (int j = 0; j < new_opts_list.size() - i; ++j) {
+            new_opts += new_opts_list[j];
+        }
 
         auto opts = android::base::StringPrintf("fsuid=%d,fsgid=%d,%smask=%d,userid=%d,gid=%d",
                                                 fsuid, fsgid, new_opts.c_str(), mask, userid, gid);
@@ -142,13 +150,14 @@
     return true;
 }
 
-static bool sdcardfs_setup_secondary(const std::string& default_path, const std::string& source_path,
-                                     const std::string& dest_path, uid_t fsuid, gid_t fsgid,
-                                     bool multi_user, userid_t userid, gid_t gid, mode_t mask,
-                                     bool derive_gid, bool default_normal, bool use_esdfs) {
+static bool sdcardfs_setup_secondary(const std::string& default_path,
+                                     const std::string& source_path, const std::string& dest_path,
+                                     uid_t fsuid, gid_t fsgid, bool multi_user, userid_t userid,
+                                     gid_t gid, mode_t mask, bool derive_gid, bool default_normal,
+                                     bool unshared_obb, bool use_esdfs) {
     if (use_esdfs) {
         return sdcardfs_setup(source_path, dest_path, fsuid, fsgid, multi_user, userid, gid, mask,
-                              derive_gid, default_normal, use_esdfs);
+                              derive_gid, default_normal, unshared_obb, use_esdfs);
     } else {
         return sdcardfs_setup_bind_remount(default_path, dest_path, gid, mask);
     }
@@ -156,7 +165,7 @@
 
 static void run_sdcardfs(const std::string& source_path, const std::string& label, uid_t uid,
                          gid_t gid, userid_t userid, bool multi_user, bool full_write,
-                         bool derive_gid, bool default_normal, bool use_esdfs) {
+                         bool derive_gid, bool default_normal, bool unshared_obb, bool use_esdfs) {
     std::string dest_path_default = "/mnt/runtime/default/" + label;
     std::string dest_path_read = "/mnt/runtime/read/" + label;
     std::string dest_path_write = "/mnt/runtime/write/" + label;
@@ -167,16 +176,17 @@
         // Multi-user storage is fully isolated per user, so "other"
         // permissions are completely masked off.
         if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
-                            AID_SDCARD_RW, 0006, derive_gid, default_normal, use_esdfs) ||
+                            AID_SDCARD_RW, 0006, derive_gid, default_normal, unshared_obb,
+                            use_esdfs) ||
             !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_read, uid, gid,
                                       multi_user, userid, AID_EVERYBODY, 0027, derive_gid,
-                                      default_normal, use_esdfs) ||
+                                      default_normal, unshared_obb, use_esdfs) ||
             !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_write, uid, gid,
                                       multi_user, userid, AID_EVERYBODY, full_write ? 0007 : 0027,
-                                      derive_gid, default_normal, use_esdfs) ||
+                                      derive_gid, default_normal, unshared_obb, use_esdfs) ||
             !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_full, uid, gid,
                                       multi_user, userid, AID_EVERYBODY, 0007, derive_gid,
-                                      default_normal, use_esdfs)) {
+                                      default_normal, unshared_obb, use_esdfs)) {
             LOG(FATAL) << "failed to sdcardfs_setup";
         }
     } else {
@@ -184,16 +194,17 @@
         // the Android directories are masked off to a single user
         // deep inside attr_from_stat().
         if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
-                            AID_SDCARD_RW, 0006, derive_gid, default_normal, use_esdfs) ||
+                            AID_SDCARD_RW, 0006, derive_gid, default_normal, unshared_obb,
+                            use_esdfs) ||
             !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_read, uid, gid,
                                       multi_user, userid, AID_EVERYBODY, full_write ? 0027 : 0022,
-                                      derive_gid, default_normal, use_esdfs) ||
+                                      derive_gid, default_normal, unshared_obb, use_esdfs) ||
             !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_write, uid, gid,
                                       multi_user, userid, AID_EVERYBODY, full_write ? 0007 : 0022,
-                                      derive_gid, default_normal, use_esdfs) ||
+                                      derive_gid, default_normal, unshared_obb, use_esdfs) ||
             !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_full, uid, gid,
                                       multi_user, userid, AID_EVERYBODY, 0007, derive_gid,
-                                      default_normal, use_esdfs)) {
+                                      default_normal, unshared_obb, use_esdfs)) {
             LOG(FATAL) << "failed to sdcardfs_setup";
         }
     }
@@ -216,7 +227,8 @@
                << "    -U: specify user ID that owns device"
                << "    -m: source_path is multi-user"
                << "    -w: runtime write mount has full write access"
-               << "    -P  preserve owners on the lower file system";
+               << "    -P: preserve owners on the lower file system"
+               << "    -o: obb dir doesn't need to be shared between users";
     return 1;
 }
 
@@ -230,6 +242,7 @@
     bool full_write = false;
     bool derive_gid = false;
     bool default_normal = false;
+    bool unshared_obb = false;
     int i;
     struct rlimit rlim;
     int fs_version;
@@ -238,7 +251,7 @@
     android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
 
     int opt;
-    while ((opt = getopt(argc, argv, "u:g:U:mwGi")) != -1) {
+    while ((opt = getopt(argc, argv, "u:g:U:mwGio")) != -1) {
         switch (opt) {
             case 'u':
                 uid = strtoul(optarg, NULL, 10);
@@ -261,8 +274,12 @@
             case 'i':
                 default_normal = true;
                 break;
+            case 'o':
+                unshared_obb = true;
+                break;
             case '?':
             default:
+                LOG(ERROR) << "Unknown option: '" << opt << "'";
                 return usage();
         }
     }
@@ -304,6 +321,6 @@
     }
 
     run_sdcardfs(source_path, label, uid, gid, userid, multi_user, full_write, derive_gid,
-                 default_normal, !should_use_sdcardfs());
+                 default_normal, unshared_obb, !should_use_sdcardfs());
     return 1;
 }
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index f08cf93..7ad6f1c 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -59,6 +59,7 @@
     name: "r",
     defaults: ["toolbox_defaults"],
     srcs: ["r.c"],
+    vendor_available: true,
 }
 
 // We build BSD grep separately (but see http://b/111849261).
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index 41263e5..c61f7d0 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -17,8 +17,8 @@
 #include <getopt.h>
 #include <stdbool.h>
 #include <stdint.h>
-#include <string.h>
 #include <stdlib.h>
+#include <string.h>
 #include <sys/capability.h>
 #include <sys/prctl.h>
 #include <sys/stat.h>
@@ -34,28 +34,38 @@
 #define REQ_BUFFER_SIZE 4096
 static uint8_t req_buffer[REQ_BUFFER_SIZE + 1];
 
-static const char *ss_data_root;
-static const char *trusty_devname;
-static const char *rpmb_devname;
-static const char *ss_srv_name = STORAGE_DISK_PROXY_PORT;
+static const char* ss_data_root;
+static const char* trusty_devname;
+static const char* rpmb_devname;
+static const char* ss_srv_name = STORAGE_DISK_PROXY_PORT;
 
-static const char *_sopts = "hp:d:r:";
-static const struct option _lopts[] =  {
-    {"help",       no_argument,       NULL, 'h'},
-    {"trusty_dev", required_argument, NULL, 'd'},
-    {"data_path",  required_argument, NULL, 'p'},
-    {"rpmb_dev",   required_argument, NULL, 'r'},
-    {0, 0, 0, 0}
-};
+static enum dev_type dev_type = MMC_RPMB;
 
-static void show_usage_and_exit(int code)
-{
-    ALOGE("usage: storageproxyd -d <trusty_dev> -p <data_path> -r <rpmb_dev>\n");
+static enum dev_type parse_dev_type(const char* dev_type_name) {
+    if (!strcmp(dev_type_name, "mmc")) {
+        return MMC_RPMB;
+    } else if (!strcmp(dev_type_name, "virt")) {
+        return VIRT_RPMB;
+    } else {
+        return UNKNOWN_RPMB;
+    }
+}
+
+static const char* _sopts = "hp:d:r:t:";
+static const struct option _lopts[] = {{"help", no_argument, NULL, 'h'},
+                                       {"trusty_dev", required_argument, NULL, 'd'},
+                                       {"data_path", required_argument, NULL, 'p'},
+                                       {"rpmb_dev", required_argument, NULL, 'r'},
+                                       {"dev_type", required_argument, NULL, 't'},
+                                       {0, 0, 0, 0}};
+
+static void show_usage_and_exit(int code) {
+    ALOGE("usage: storageproxyd -d <trusty_dev> -p <data_path> -r <rpmb_dev> -t <dev_type>\n");
+    ALOGE("Available dev types: mmc, virt\n");
     exit(code);
 }
 
-static int drop_privs(void)
-{
+static int drop_privs(void) {
     struct __user_cap_header_struct capheader;
     struct __user_cap_data_struct capdata[2];
 
@@ -95,12 +105,10 @@
     return 0;
 }
 
-static int handle_req(struct storage_msg *msg, const void *req, size_t req_len)
-{
+static int handle_req(struct storage_msg* msg, const void* req, size_t req_len) {
     int rc;
 
-    if ((msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) &&
-        (msg->cmd != STORAGE_RPMB_SEND)) {
+    if ((msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) && (msg->cmd != STORAGE_RPMB_SEND)) {
         /*
          * handling post commit messages on non rpmb commands are not
          * implemented as there is no use case for this yet.
@@ -119,42 +127,42 @@
     }
 
     switch (msg->cmd) {
-    case STORAGE_FILE_DELETE:
-        rc = storage_file_delete(msg, req, req_len);
-        break;
+        case STORAGE_FILE_DELETE:
+            rc = storage_file_delete(msg, req, req_len);
+            break;
 
-    case STORAGE_FILE_OPEN:
-        rc = storage_file_open(msg, req, req_len);
-        break;
+        case STORAGE_FILE_OPEN:
+            rc = storage_file_open(msg, req, req_len);
+            break;
 
-    case STORAGE_FILE_CLOSE:
-        rc = storage_file_close(msg, req, req_len);
-        break;
+        case STORAGE_FILE_CLOSE:
+            rc = storage_file_close(msg, req, req_len);
+            break;
 
-    case STORAGE_FILE_WRITE:
-        rc = storage_file_write(msg, req, req_len);
-        break;
+        case STORAGE_FILE_WRITE:
+            rc = storage_file_write(msg, req, req_len);
+            break;
 
-    case STORAGE_FILE_READ:
-        rc = storage_file_read(msg, req, req_len);
-        break;
+        case STORAGE_FILE_READ:
+            rc = storage_file_read(msg, req, req_len);
+            break;
 
-    case STORAGE_FILE_GET_SIZE:
-        rc = storage_file_get_size(msg, req, req_len);
-        break;
+        case STORAGE_FILE_GET_SIZE:
+            rc = storage_file_get_size(msg, req, req_len);
+            break;
 
-    case STORAGE_FILE_SET_SIZE:
-        rc = storage_file_set_size(msg, req, req_len);
-        break;
+        case STORAGE_FILE_SET_SIZE:
+            rc = storage_file_set_size(msg, req, req_len);
+            break;
 
-    case STORAGE_RPMB_SEND:
-        rc = rpmb_send(msg, req, req_len);
-        break;
+        case STORAGE_RPMB_SEND:
+            rc = rpmb_send(msg, req, req_len);
+            break;
 
-    default:
-        ALOGE("unhandled command 0x%x\n", msg->cmd);
-        msg->result = STORAGE_ERR_UNIMPLEMENTED;
-        rc = 1;
+        default:
+            ALOGE("unhandled command 0x%x\n", msg->cmd);
+            msg->result = STORAGE_ERR_UNIMPLEMENTED;
+            rc = 1;
     }
 
     if (rc > 0) {
@@ -164,58 +172,58 @@
     return rc;
 }
 
-static int proxy_loop(void)
-{
+static int proxy_loop(void) {
     ssize_t rc;
     struct storage_msg msg;
 
     /* enter main message handling loop */
     while (true) {
-
         /* get incoming message */
         rc = ipc_get_msg(&msg, req_buffer, REQ_BUFFER_SIZE);
-        if (rc < 0)
-            return rc;
+        if (rc < 0) return rc;
 
         /* handle request */
         req_buffer[rc] = 0; /* force zero termination */
         rc = handle_req(&msg, req_buffer, rc);
-        if (rc)
-            return rc;
+        if (rc) return rc;
     }
 
     return 0;
 }
 
-static void parse_args(int argc, char *argv[])
-{
+static void parse_args(int argc, char* argv[]) {
     int opt;
     int oidx = 0;
 
     while ((opt = getopt_long(argc, argv, _sopts, _lopts, &oidx)) != -1) {
         switch (opt) {
+            case 'd':
+                trusty_devname = strdup(optarg);
+                break;
 
-        case 'd':
-            trusty_devname = strdup(optarg);
-            break;
+            case 'p':
+                ss_data_root = strdup(optarg);
+                break;
 
-        case 'p':
-            ss_data_root = strdup(optarg);
-            break;
+            case 'r':
+                rpmb_devname = strdup(optarg);
+                break;
 
-        case 'r':
-            rpmb_devname = strdup(optarg);
-            break;
+            case 't':
+                dev_type = parse_dev_type(optarg);
+                if (dev_type == UNKNOWN_RPMB) {
+                    ALOGE("Unrecognized dev type: %s\n", optarg);
+                    show_usage_and_exit(EXIT_FAILURE);
+                }
+                break;
 
-        default:
-            ALOGE("unrecognized option (%c):\n", opt);
-            show_usage_and_exit(EXIT_FAILURE);
+            default:
+                ALOGE("unrecognized option (%c):\n", opt);
+                show_usage_and_exit(EXIT_FAILURE);
         }
     }
 
-    if (ss_data_root == NULL ||
-        trusty_devname == NULL ||
-        rpmb_devname == NULL) {
+    if (ss_data_root == NULL || trusty_devname == NULL || rpmb_devname == NULL) {
         ALOGE("missing required argument(s)\n");
         show_usage_and_exit(EXIT_FAILURE);
     }
@@ -226,31 +234,26 @@
     ALOGI("rpmb dev: %s\n", rpmb_devname);
 }
 
-int main(int argc, char *argv[])
-{
+int main(int argc, char* argv[]) {
     int rc;
 
     /* drop privileges */
-    if (drop_privs() < 0)
-        return EXIT_FAILURE;
+    if (drop_privs() < 0) return EXIT_FAILURE;
 
     /* parse arguments */
     parse_args(argc, argv);
 
     /* initialize secure storage directory */
     rc = storage_init(ss_data_root);
-    if (rc < 0)
-        return EXIT_FAILURE;
+    if (rc < 0) return EXIT_FAILURE;
 
     /* open rpmb device */
-    rc = rpmb_open(rpmb_devname);
-    if (rc < 0)
-        return EXIT_FAILURE;
+    rc = rpmb_open(rpmb_devname, dev_type);
+    if (rc < 0) return EXIT_FAILURE;
 
     /* connect to Trusty secure storage server */
     rc = ipc_connect(trusty_devname, ss_srv_name);
-    if (rc < 0)
-        return EXIT_FAILURE;
+    if (rc < 0) return EXIT_FAILURE;
 
     /* enter main loop */
     rc = proxy_loop();
diff --git a/trusty/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c
index 9c79105..29827e2 100644
--- a/trusty/storage/proxy/rpmb.c
+++ b/trusty/storage/proxy/rpmb.c
@@ -51,17 +51,16 @@
 
 static int rpmb_fd = -1;
 static uint8_t read_buf[4096];
+static enum dev_type dev_type = UNKNOWN_RPMB;
 
 #ifdef RPMB_DEBUG
 
-static void print_buf(const char *prefix, const uint8_t *buf, size_t size)
-{
+static void print_buf(const char* prefix, const uint8_t* buf, size_t size) {
     size_t i;
 
     printf("%s @%p [%zu]", prefix, buf, size);
     for (i = 0; i < size; i++) {
-        if (i && i % 32 == 0)
-            printf("\n%*s", (int) strlen(prefix), "");
+        if (i && i % 32 == 0) printf("\n%*s", (int)strlen(prefix), "");
         printf(" %02x", buf[i]);
     }
     printf("\n");
@@ -70,41 +69,16 @@
 
 #endif
 
-
-int rpmb_send(struct storage_msg *msg, const void *r, size_t req_len)
-{
-    int rc;
+static int send_mmc_rpmb_req(int mmc_fd, const struct storage_rpmb_send_req* req) {
     struct {
         struct mmc_ioc_multi_cmd multi;
         struct mmc_ioc_cmd cmd_buf[3];
     } mmc = {};
-    struct mmc_ioc_cmd *cmd = mmc.multi.cmds;
-    const struct storage_rpmb_send_req *req = r;
+    struct mmc_ioc_cmd* cmd = mmc.multi.cmds;
+    int rc;
 
-    if (req_len < sizeof(*req)) {
-        ALOGW("malformed rpmb request: invalid length (%zu < %zu)\n",
-              req_len, sizeof(*req));
-        msg->result = STORAGE_ERR_NOT_VALID;
-        goto err_response;
-    }
-
-    size_t expected_len =
-            sizeof(*req) + req->reliable_write_size + req->write_size;
-    if (req_len != expected_len) {
-        ALOGW("malformed rpmb request: invalid length (%zu != %zu)\n",
-              req_len, expected_len);
-        msg->result = STORAGE_ERR_NOT_VALID;
-        goto err_response;
-    }
-
-    const uint8_t *write_buf = req->payload;
+    const uint8_t* write_buf = req->payload;
     if (req->reliable_write_size) {
-        if ((req->reliable_write_size % MMC_BLOCK_SIZE) != 0) {
-            ALOGW("invalid reliable write size %u\n", req->reliable_write_size);
-            msg->result = STORAGE_ERR_NOT_VALID;
-            goto err_response;
-        }
-
         cmd->write_flag = MMC_WRITE_FLAG_RELW;
         cmd->opcode = MMC_WRITE_MULTIPLE_BLOCK;
         cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
@@ -121,12 +95,6 @@
     }
 
     if (req->write_size) {
-        if ((req->write_size % MMC_BLOCK_SIZE) != 0) {
-            ALOGW("invalid write size %u\n", req->write_size);
-            msg->result = STORAGE_ERR_NOT_VALID;
-            goto err_response;
-        }
-
         cmd->write_flag = MMC_WRITE_FLAG_W;
         cmd->opcode = MMC_WRITE_MULTIPLE_BLOCK;
         cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
@@ -143,17 +111,9 @@
     }
 
     if (req->read_size) {
-        if (req->read_size % MMC_BLOCK_SIZE != 0 ||
-            req->read_size > sizeof(read_buf)) {
-            ALOGE("%s: invalid read size %u\n", __func__, req->read_size);
-            msg->result = STORAGE_ERR_NOT_VALID;
-            goto err_response;
-        }
-
         cmd->write_flag = MMC_WRITE_FLAG_R;
         cmd->opcode = MMC_READ_MULTIPLE_BLOCK;
-        cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC,
-        cmd->blksz = MMC_BLOCK_SIZE;
+        cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC, cmd->blksz = MMC_BLOCK_SIZE;
         cmd->blocks = req->read_size / MMC_BLOCK_SIZE;
         mmc_ioc_cmd_set_data((*cmd), read_buf);
 #ifdef RPMB_DEBUG
@@ -163,15 +123,97 @@
         cmd++;
     }
 
-    rc = ioctl(rpmb_fd, MMC_IOC_MULTI_CMD, &mmc.multi);
+    rc = ioctl(mmc_fd, MMC_IOC_MULTI_CMD, &mmc.multi);
     if (rc < 0) {
         ALOGE("%s: mmc ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
+    }
+    return rc;
+}
+
+static int send_virt_rpmb_req(int rpmb_fd, void* read_buf, size_t read_size, const void* payload,
+                              size_t payload_size) {
+    int rc;
+    uint16_t res_count = read_size / MMC_BLOCK_SIZE;
+    uint16_t cmd_count = payload_size / MMC_BLOCK_SIZE;
+    rc = write(rpmb_fd, &res_count, sizeof(res_count));
+    if (rc < 0) {
+        return rc;
+    }
+    rc = write(rpmb_fd, &cmd_count, sizeof(cmd_count));
+    if (rc < 0) {
+        return rc;
+    }
+    rc = write(rpmb_fd, payload, payload_size);
+    if (rc < 0) {
+        return rc;
+    }
+    rc = read(rpmb_fd, read_buf, read_size);
+    return rc;
+}
+
+int rpmb_send(struct storage_msg* msg, const void* r, size_t req_len) {
+    int rc;
+    const struct storage_rpmb_send_req* req = r;
+
+    if (req_len < sizeof(*req)) {
+        ALOGW("malformed rpmb request: invalid length (%zu < %zu)\n", req_len, sizeof(*req));
+        msg->result = STORAGE_ERR_NOT_VALID;
+        goto err_response;
+    }
+
+    size_t expected_len = sizeof(*req) + req->reliable_write_size + req->write_size;
+    if (req_len != expected_len) {
+        ALOGW("malformed rpmb request: invalid length (%zu != %zu)\n", req_len, expected_len);
+        msg->result = STORAGE_ERR_NOT_VALID;
+        goto err_response;
+    }
+
+    if ((req->reliable_write_size % MMC_BLOCK_SIZE) != 0) {
+        ALOGW("invalid reliable write size %u\n", req->reliable_write_size);
+        msg->result = STORAGE_ERR_NOT_VALID;
+        goto err_response;
+    }
+
+    if ((req->write_size % MMC_BLOCK_SIZE) != 0) {
+        ALOGW("invalid write size %u\n", req->write_size);
+        msg->result = STORAGE_ERR_NOT_VALID;
+        goto err_response;
+    }
+
+    if (req->read_size % MMC_BLOCK_SIZE != 0 || req->read_size > sizeof(read_buf)) {
+        ALOGE("%s: invalid read size %u\n", __func__, req->read_size);
+        msg->result = STORAGE_ERR_NOT_VALID;
+        goto err_response;
+    }
+
+    if (dev_type == MMC_RPMB) {
+        rc = send_mmc_rpmb_req(rpmb_fd, req);
+        if (rc < 0) {
+            msg->result = STORAGE_ERR_GENERIC;
+            goto err_response;
+        }
+    } else if (dev_type == VIRT_RPMB) {
+        size_t payload_size = req->reliable_write_size + req->write_size;
+        rc = send_virt_rpmb_req(rpmb_fd, read_buf, req->read_size, req->payload, payload_size);
+        if (rc < 0) {
+            ALOGE("send_virt_rpmb_req failed: %d, %s\n", rc, strerror(errno));
+            msg->result = STORAGE_ERR_GENERIC;
+            goto err_response;
+        }
+        if (rc != req->read_size) {
+            ALOGE("send_virt_rpmb_req got incomplete response: "
+                  "(size %d, expected %d)\n",
+                  rc, req->read_size);
+            msg->result = STORAGE_ERR_GENERIC;
+            goto err_response;
+        }
+    } else {
+        ALOGE("Unsupported dev_type\n");
         msg->result = STORAGE_ERR_GENERIC;
         goto err_response;
     }
 #ifdef RPMB_DEBUG
-    if (req->read_size)
-        print_buf("response: ", read_buf, req->read_size);
+    if (req->read_size) print_buf("response: ", read_buf, req->read_size);
 #endif
 
     if (msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) {
@@ -188,24 +230,20 @@
     return ipc_respond(msg, NULL, 0);
 }
 
-
-int rpmb_open(const char *rpmb_devname)
-{
+int rpmb_open(const char* rpmb_devname, enum dev_type open_dev_type) {
     int rc;
+    dev_type = open_dev_type;
 
     rc = open(rpmb_devname, O_RDWR, 0);
     if (rc < 0) {
-        ALOGE("unable (%d) to open rpmb device '%s': %s\n",
-              errno, rpmb_devname, strerror(errno));
+        ALOGE("unable (%d) to open rpmb device '%s': %s\n", errno, rpmb_devname, strerror(errno));
         return rc;
     }
     rpmb_fd = rc;
     return 0;
 }
 
-void rpmb_close(void)
-{
+void rpmb_close(void) {
     close(rpmb_fd);
     rpmb_fd = -1;
 }
-
diff --git a/trusty/storage/proxy/rpmb.h b/trusty/storage/proxy/rpmb.h
index 85cff44..4c330c9 100644
--- a/trusty/storage/proxy/rpmb.h
+++ b/trusty/storage/proxy/rpmb.h
@@ -18,6 +18,8 @@
 #include <stdint.h>
 #include <trusty/interface/storage.h>
 
-int rpmb_open(const char *rpmb_devname);
-int rpmb_send(struct storage_msg *msg, const void *r, size_t req_len);
+enum dev_type { UNKNOWN_RPMB, MMC_RPMB, VIRT_RPMB };
+
+int rpmb_open(const char* rpmb_devname, enum dev_type dev_type);
+int rpmb_send(struct storage_msg* msg, const void* r, size_t req_len);
 void rpmb_close(void);