Merge "libfs_avb: support key rotation for standalone partitions"
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 91b0d1f..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);
 }
 
diff --git a/adb/adb_io.h b/adb/adb_io.h
index e2df1b1..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);
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/client/adb_client.cpp b/adb/client/adb_client.cpp
index 0a09d1e..4cf3a74 100644
--- a/adb/client/adb_client.cpp
+++ b/adb/client/adb_client.cpp
@@ -70,46 +70,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 +147,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 +162,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,11 +210,15 @@
     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());
+int adb_connect(TransportId* transport, std::string_view service, std::string* error) {
+    // first query the adb server's version
+    unique_fd fd(_adb_connect("host:version", nullptr, error));
+
+    LOG(DEBUG) << "adb_connect: service: " << service;
     if (fd == -2 && !is_local_socket_spec(__adb_server_socket_spec)) {
         fprintf(stderr, "* cannot start server on remote host\n");
         // error is the original network connection error
@@ -216,7 +240,7 @@
         // 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) {
@@ -254,7 +278,7 @@
         return 0;
     }
 
-    fd.reset(_adb_connect(service, error));
+    fd.reset(_adb_connect(service, transport, error));
     if (fd == -1) {
         D("_adb_connect error: %s", error->c_str());
     } else if(fd == -2) {
@@ -265,7 +289,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..0a73787 100644
--- a/adb/client/adb_client.h
+++ b/adb/client/adb_client.h
@@ -24,7 +24,10 @@
 
 // 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();
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..3286959 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"
@@ -1031,10 +1031,11 @@
     }
 
     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;
     }
@@ -1046,7 +1047,8 @@
 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 +1081,9 @@
         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.
+    adb_set_transport(kTransportAny, nullptr, transport_id);
+    wait_for_device("wait-for-disconnect");
     return true;
 }
 
diff --git a/adb/daemon/abb.cpp b/adb/daemon/abb.cpp
index d949dd1..eeac41a 100644
--- a/adb/daemon/abb.cpp
+++ b/adb/daemon/abb.cpp
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 
-#include "adb.h"
-#include "adb_io.h"
-#include "shell_service.h"
-
-#include "cmd.h"
-
 #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"
 
 namespace {
 
@@ -85,7 +85,17 @@
             break;
         }
 
-        unique_fd result = StartCommandInProcess(std::move(data), &execCmd);
+        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 d32bf52..a435279 100644
--- a/adb/daemon/abb_service.cpp
+++ b/adb/daemon/abb_service.cpp
@@ -86,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/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/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/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/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/transport.cpp b/adb/transport.cpp
index 0c7b0b6..0b4e084 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -72,6 +72,7 @@
 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 {
 
@@ -1013,6 +1014,7 @@
             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/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/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_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 9364b2d..40da36d 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;
 }
@@ -1026,7 +1031,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_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
new file mode 100644
index 0000000..c0e0ccd
--- /dev/null
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -0,0 +1,348 @@
+/*
+ * 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]\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"
+                 "\n"
+                 "Remount all partitions read-write.\n"
+                 "-R notwithstanding, verity must be disabled.";
+
+    ::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 try_unmount_bionic(android::fs_mgr::Fstab* mounts) {
+    static constexpr const char* kBionic = "/bionic";
+
+    auto entry = GetEntryForMountPoint(mounts, kBionic);
+    if (!entry) return;
+
+    struct statfs buf;
+    if (::statfs(kBionic, &buf) == -1) {
+        PLOG(ERROR) << "statfs of " << kBionic;
+        return;
+    }
+    if (buf.f_flags & MS_RDONLY) {
+        // /bionic is on a read-only partition; can happen for
+        // non-system-as-root-devices. Don' try to unmount.
+        return;
+    }
+    fs_mgr_set_blk_ro(entry->blk_device, false);
+    if (::mount(entry->blk_device.c_str(), entry->mount_point.c_str(), entry->fs_type.c_str(),
+                MS_REMOUNT, nullptr) == -1) {
+        PLOG(ERROR) << "remount of " << kBionic;
+    }
+}
+
+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,
+        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;
+        }
+    }
+
+    if (argc > optind) {
+        LOG(ERROR) << "Bad Argument " << argv[optind];
+        usage(BADARG);
+    }
+
+    // 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);
+    }
+    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 partitions;
+    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)) {
+            partitions.emplace_back(entry);
+        }
+    }
+
+    // Check verity and optionally setup overlayfs backing.
+    auto reboot_later = false;
+    for (auto it = partitions.begin(); it != partitions.end();) {
+        auto& entry = *it;
+        auto& mount_point = entry.mount_point;
+        if (fs_mgr_is_verity_enabled(entry)) {
+            LOG(WARNING) << "Verity enabled on " << mount_point;
+            if (can_reboot &&
+                (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) {
+                        if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
+                            retval = VERITY_PARTITION;
+                            // w/o overlayfs available, also check for dedupe
+                            reboot_later = true;
+                            ++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)) reboot_later = true;
+                    }
+                }
+            }
+            LOG(ERROR) << "Skipping " << mount_point;
+            retval = VERITY_PARTITION;
+            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.
+    if (!fs_mgr_overlayfs_mount_all(&partitions)) {
+        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")) {
+                if (blk_device != "/dev/root") blk_device = rentry.blk_device;
+                mount_point = "/system";
+                break;
+            }
+        }
+        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);
+
+    try_unmount_bionic(&mounts);
+
+    return retval;
+}
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer.cpp b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
index b9b75f8..6b742e6 100644
--- a/fs_mgr/libfiemap_writer/fiemap_writer.cpp
+++ b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
@@ -126,7 +126,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 +147,10 @@
         return false;
     }
 
+    if (uses_dm) {
+        *uses_dm = (bdev_raw != bdev);
+    }
+
     LOG(DEBUG) << "Popped device (" << bdev_raw << ") from device mapper stack starting with ("
                << bdev << ")";
 
@@ -225,7 +230,7 @@
     // 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)) {
+    if (fallocate64(file_fd, FALLOC_FL_ZERO_RANGE, 0, file_size)) {
         PLOG(ERROR) << "Failed to allocate space for file: " << file_path << " size: " << file_size;
         return false;
     }
@@ -458,7 +463,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;
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
index 41fa959..3e8381b 100644
--- a/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
+++ b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
@@ -36,19 +36,19 @@
 #include <libfiemap_writer/fiemap_writer.h>
 
 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
 
 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()); }
@@ -113,7 +113,8 @@
 TEST_F(FiemapWriterTest, CheckBlockDevicePath) {
     FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 4096);
     EXPECT_EQ(fptr->size(), 4096);
-    EXPECT_EQ(fptr->bdev_path(), testbdev);
+    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) {
@@ -141,10 +142,9 @@
 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";
+        unaligned_file_ = gTestDir + "/unaligned_file";
+        file_4k_ = gTestDir + "/file_4k";
+        file_32k_ = gTestDir + "/file_32k";
 
         CleanupFiles();
         fptr_unaligned = FiemapWriter::Open(unaligned_file_, 4097);
@@ -266,14 +266,20 @@
 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 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];
+        exit(EXIT_FAILURE);
+    }
+    gTestDir = tempdir;
+
     if (argc > 2) {
         testfile_size = strtoull(argv[2], NULL, 0);
         if (testfile_size == ULLONG_MAX) {
@@ -281,5 +287,10 @@
         }
     }
 
-    return RUN_ALL_TESTS();
+    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..b5472c9 100644
--- a/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
+++ b/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
@@ -57,6 +57,13 @@
     // FiemapWriter::Open).
     static bool HasPinnedExtents(const std::string& file_path);
 
+    // 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);
+
     // 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.
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index 59af924..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",
     ],
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index e4ff765..1ded954 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -11,13 +11,14 @@
 ##  USAGE
 ##
 
-USAGE="USAGE: `basename ${0}` [--help] [--serial <SerialNumber>] [--color]
+USAGE="USAGE: `basename ${0}` [--help] [--serial <SerialNumber>] [options]
 
 adb remount tests
 
---help      This help
---serial    Specify device (must if multiple are present)
---color     Dress output with highlighting colors
+--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.
@@ -45,6 +46,8 @@
 BLUE="${ESCAPE}[35m"
 NORMAL="${ESCAPE}[0m"
 TMPDIR=${TMPDIR:-/tmp}
+print_time=false
+start_time=`date +%s`
 
 ##
 ##  Helper Functions
@@ -67,19 +70,37 @@
 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
       wc -l | grep '^1$' >/dev/null
     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
@@ -140,10 +161,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
@@ -162,7 +183,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
@@ -172,9 +193,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
@@ -202,10 +223,10 @@
 adb_wait() {
   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
+    local ret=${?}
     echo -n "                                                                             ${CR}"
-    return ${retval}
+    return ${ret}
   else
     adb wait-for-device
   fi
@@ -213,12 +234,15 @@
 
 [ "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
@@ -235,26 +259,58 @@
   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=${?}
+    local ret=${?}
     echo -n "                                                                             ${CR}"
-    ( exit ${retval} )
+    ( exit ${ret} )
   else
     fastboot wait-for-device >/dev/null 2>/dev/null
   fi ||
     inFastboot
 }
 
+[ "USAGE: recovery_wait [timeout]
+
+Returns: waits until the device has returned for recovery or optional timeout" ]
+recovery_wait() {
+  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
+    local ret=${?}
+    echo -n "                                                                             ${CR}"
+    return ${ret}
+  else
+    adb wait-for-recovery
+  fi
+}
+
+[ "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
 
 NB: This can be flakey on devices due to USB state
 
 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
@@ -263,22 +319,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
 
 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
@@ -312,6 +368,21 @@
   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
 
 If -d, or -t <epoch> argument is supplied, dump logcat.
@@ -332,6 +403,7 @@
   echo "${RED}[  FAILED  ]${NORMAL} ${@}" >&2
   cleanup
   restore
+  test_duration
   exit 1
 }
 
@@ -339,8 +411,8 @@
 
 Returns true if (regex) lval matches rval" ]
 EXPECT_EQ() {
-  lval="${1}"
-  rval="${2}"
+  local lval="${1}"
+  local rval="${2}"
   shift 2
   if ! ( echo X"${rval}" | grep '^X'"${lval}"'$' >/dev/null 2>/dev/null ); then
     if [ `echo ${lval}${rval}${*} | wc -c` -gt 50 -o "${rval}" != "${rval%
@@ -385,10 +457,10 @@
 
 Exits if (regex) lval mismatches rval" ]
 check_eq() {
-  left="${1}"
-  right="${2}"
+  local lval="${1}"
+  local rval="${2}"
   shift 2
-  EXPECT_EQ "${left}" "${right}" ||
+  EXPECT_EQ "${lval}" "${rval}" ||
     die "${@}"
 }
 
@@ -424,7 +496,10 @@
 ##  MAINLINE
 ##
 
-OPTIONS=`getopt --alternative --unquoted --longoptions help,serial:,colour,color,no-colour,no-color -- "?hs:" ${*}` ||
+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}
@@ -446,6 +521,9 @@
     --no-color | --no-colour)
       color=false
       ;;
+    --print-time | --gtest_print_time)
+      print_time=true
+      ;;
     --)
       shift
       break
@@ -468,16 +546,21 @@
   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
 
@@ -525,15 +608,28 @@
      "2" = "`get_property partition.system.verified`" ]; then
   restore() {
     ${overlayfs_supported} || return 0
-    echo "${GREEN}[     INFO ]${NORMAL} restoring verity" >&2
     inFastboot &&
       fastboot reboot &&
       adb_wait 2m
-    adb_root &&
-      adb enable-verity &&
+    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 </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
@@ -548,20 +644,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
-        ) ||
+  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 callers creds" >&2
-      ;;
-  esac
+        ;;
+      *)
+        echo "${GREEN}[       OK ]${NORMAL} overlay module uses caller's creds" >&2
+        ;;
+    esac
 fi
 
 adb_root ||
@@ -609,7 +702,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
@@ -758,7 +851,7 @@
   D=`echo "${D}" | cut -s -d' ' -f1 | sort -u`
   bad_rw=false
   for d in ${D}; do
-    if adb_sh tune2fs -l $d 2>&1 |
+    if adb_sh tune2fs -l $d </dev/null 2>&1 |
        grep "Filesystem features:.*shared_blocks" >/dev/null; then
       bad_rw=true
     else
@@ -831,21 +924,20 @@
     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
 
@@ -875,8 +967,9 @@
 else
   adb reboot-fastboot ||
     die "fastbootd not supported (wrong adb in path?)"
-  fastboot_wait 2m ||
-    die "reboot into fastboot to flash vendor `usb_status`"
+  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"
@@ -930,12 +1023,11 @@
       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"
@@ -943,13 +1035,21 @@
     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" )
+      echo "${ORANGE}[  WARNING ]${NORMAL} user fastboot missing required to invalidate, ignoring a failure" >&2
+      echo "${ORANGE}[  WARNING ]${NORMAL} re-read /vendor/hello after flash vendor" >&2
     fi
   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
+      restore() {
+        true
+      }
+      check_eq "cat: /vendor/hello: No such file or directory" "${B}" \
+               vendor content after flash vendor
+    )
   fi
 fi
 
@@ -977,12 +1077,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
 
@@ -1047,20 +1145,71 @@
 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
 
+# 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 </dev/null ||
+  die "remount command"
+adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null ||
+  die "/vendor is not read-write"
+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 </dev/null ||
+  die "remount command"
+adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null ||
+  die "/vendor is not read-write"
+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 remount -R &&
+    adb_wait 2m ||
+    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..6afc8d2 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));
@@ -927,7 +950,7 @@
 source none0       swap   defaults      avb=vbmeta_partition
 )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));
@@ -950,7 +973,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 +995,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 +1025,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/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index f9c161c..043f038 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -115,6 +115,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
@@ -272,8 +274,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(),
@@ -322,8 +335,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(),
diff --git a/libprocessgroup/profiles/cgroups.json b/libprocessgroup/profiles/cgroups.json
index aa71956..5871a63 100644
--- a/libprocessgroup/profiles/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/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 5a090c5..985720f 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -334,6 +334,59 @@
     },
 
     {
+      "Name": "LowIoPriority",
+      "Actions" : [
+        {
+          "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" : [
         {
diff --git a/libprocessgroup/sched_policy.cpp b/libprocessgroup/sched_policy.cpp
index 337b032..026c011 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;
     }
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index b69103c..8ec29d3 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -198,7 +198,7 @@
     }
 
     // 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);
@@ -211,7 +211,7 @@
 
     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
@@ -246,7 +246,7 @@
     PLOG(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
@@ -315,7 +315,7 @@
         std::string file_name = 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(ctrlName);
             if (controller) {
                 attributes_[name] = std::make_unique<ProfileAttribute>(controller, file_name);
             } else {
@@ -344,7 +344,7 @@
                 std::string ctrlName = paramsVal["Controller"].asString();
                 std::string path = paramsVal["Path"].asString();
 
-                const CgroupController* controller = cg_map.FindController(ctrlName.c_str());
+                const CgroupController* controller = cg_map.FindController(ctrlName);
                 if (controller) {
                     profile->Add(std::make_unique<SetCgroupAction>(controller, path));
                 } else {
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index c90f5b2..a49fd9e 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",
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/lmkd/lmkd.c b/lmkd/lmkd.c
index 562e578..98b3aa1 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -1694,16 +1694,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/rootdir/Android.mk b/rootdir/Android.mk
index 944554a..d2125d8 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -44,6 +44,7 @@
 
 # TODO(b/124106384): Clean up compat symlinks for ART binaries.
 ART_BINARIES= \
+  dalvikvm \
   dalvikvm32 \
   dalvikvm64 \
   dex2oat \
@@ -190,6 +191,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' $@
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index d5665f2..5d6cd2d 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -9,6 +9,7 @@
     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 8bd7c4c..ce4b380 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -84,6 +84,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
@@ -577,9 +586,6 @@
     # Set SELinux security contexts on upgrade or policy update.
     restorecon --recursive --skip-ce /data
 
-    # 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
 
@@ -591,6 +597,9 @@
     setup_runtime_bionic
     parse_apex_configs
 
+    # 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
+
     # 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.