Merge "Don't include log/log_read.h in log/log.h"
diff --git a/adb/Android.bp b/adb/Android.bp
index 6386a78..8779c0a 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -165,7 +165,6 @@
     "adb_unique_fd.cpp",
     "adb_utils.cpp",
     "fdevent/fdevent.cpp",
-    "fdevent/fdevent_poll.cpp",
     "services.cpp",
     "sockets.cpp",
     "socket_spec.cpp",
@@ -176,6 +175,17 @@
     "types.cpp",
 ]
 
+libadb_darwin_srcs = [
+    "fdevent/fdevent_poll.cpp",
+]
+
+libadb_windows_srcs = [
+    "fdevent/fdevent_poll.cpp",
+    "sysdeps_win32.cpp",
+    "sysdeps/win32/errno.cpp",
+    "sysdeps/win32/stat.cpp",
+]
+
 libadb_posix_srcs = [
     "sysdeps_unix.cpp",
     "sysdeps/posix/network.cpp",
@@ -219,7 +229,7 @@
             srcs: ["client/usb_linux.cpp"] + libadb_linux_srcs,
         },
         darwin: {
-            srcs: ["client/usb_osx.cpp"],
+            srcs: ["client/usb_osx.cpp"] + libadb_darwin_srcs,
         },
         not_windows: {
             srcs: libadb_posix_srcs,
@@ -228,10 +238,7 @@
             enabled: true,
             srcs: [
                 "client/usb_windows.cpp",
-                "sysdeps_win32.cpp",
-                "sysdeps/win32/errno.cpp",
-                "sysdeps/win32/stat.cpp",
-            ],
+            ] + libadb_windows_srcs,
             shared_libs: ["AdbWinApi"],
         },
     },
@@ -695,6 +702,7 @@
         "daemon/shell_service_test.cpp",
         "shell_service_protocol.cpp",
         "shell_service_protocol_test.cpp",
+        "mdns_test.cpp",
     ],
 
     shared_libs: [
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 08d3904..dcec0ba 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -149,7 +149,7 @@
     case A_WRTE: tag = "WRTE"; break;
     case A_AUTH: tag = "AUTH"; break;
     case A_STLS:
-        tag = "ATLS";
+        tag = "STLS";
         break;
     default: tag = "????"; break;
     }
@@ -1076,6 +1076,25 @@
     g_reject_kill_server = value;
 }
 
+static bool handle_mdns_request(std::string_view service, int reply_fd) {
+    if (!android::base::ConsumePrefix(&service, "mdns:")) {
+        return false;
+    }
+
+    if (service == "check") {
+        std::string check = mdns_check();
+        SendOkay(reply_fd, check);
+        return true;
+    }
+    if (service == "services") {
+        std::string services_list = mdns_list_discovered_services();
+        SendOkay(reply_fd, services_list);
+        return true;
+    }
+
+    return false;
+}
+
 HostRequestResult handle_host_request(std::string_view service, TransportType type,
                                       const char* serial, TransportId transport_id, int reply_fd,
                                       asocket* s) {
@@ -1320,6 +1339,10 @@
         return HostRequestResult::Handled;
     }
 
+    if (handle_mdns_request(service, reply_fd)) {
+        return HostRequestResult::Handled;
+    }
+
     return HostRequestResult::Unhandled;
 }
 
diff --git a/adb/adb_mdns.h b/adb/adb_mdns.h
index 6b37355..3111248 100644
--- a/adb/adb_mdns.h
+++ b/adb/adb_mdns.h
@@ -19,9 +19,14 @@
 
 #include <android-base/macros.h>
 
-const char* kADBServiceType = "_adb._tcp";
-const char* kADBSecurePairingServiceType = "_adb_secure_pairing._tcp";
-const char* kADBSecureConnectServiceType = "_adb_secure_connect._tcp";
+// The rules for Service Names [RFC6335] state that they may be no more
+// than fifteen characters long (not counting the mandatory underscore),
+// consisting of only letters, digits, and hyphens, must begin and end
+// with a letter or digit, must not contain consecutive hyphens, and
+// must contain at least one letter.
+#define ADB_MDNS_SERVICE_TYPE "adb"
+#define ADB_MDNS_TLS_PAIRING_TYPE "adb-tls-pairing"
+#define ADB_MDNS_TLS_CONNECT_TYPE "adb-tls-connect"
 
 const int kADBTransportServiceRefIndex = 0;
 const int kADBSecurePairingServiceRefIndex = 1;
@@ -71,11 +76,10 @@
 const char* kADBSecureConnectServiceTxtRecord =
         ADB_SECURE_SERVICE_VERSION_TXT_RECORD(ADB_SECURE_SERVICE_VERSION);
 
-const char* kADBDNSServices[] = {
-        kADBServiceType,
-        kADBSecurePairingServiceType,
-        kADBSecureConnectServiceType,
-};
+#define ADB_FULL_MDNS_SERVICE_TYPE(atype) ("_" atype "._tcp")
+const char* kADBDNSServices[] = {ADB_FULL_MDNS_SERVICE_TYPE(ADB_MDNS_SERVICE_TYPE),
+                                 ADB_FULL_MDNS_SERVICE_TYPE(ADB_MDNS_TLS_PAIRING_TYPE),
+                                 ADB_FULL_MDNS_SERVICE_TYPE(ADB_MDNS_TLS_CONNECT_TYPE)};
 
 const char* kADBDNSServiceTxtRecords[] = {
         nullptr,
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
index cea24fe..c579dde 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -89,18 +89,13 @@
 
 int adb_trace_mask;
 
-std::string get_trace_setting_from_env() {
+std::string get_trace_setting() {
+#if ADB_HOST
     const char* setting = getenv("ADB_TRACE");
     if (setting == nullptr) {
         setting = "";
     }
-
-    return std::string(setting);
-}
-
-std::string get_trace_setting() {
-#if ADB_HOST
-    return get_trace_setting_from_env();
+    return setting;
 #else
     return android::base::GetProperty("persist.adb.trace_mask", "");
 #endif
diff --git a/adb/adb_wifi.h b/adb/adb_wifi.h
index 585748c..3a6b0b1 100644
--- a/adb/adb_wifi.h
+++ b/adb/adb_wifi.h
@@ -27,6 +27,9 @@
                           std::string& response);
 bool adb_wifi_is_known_host(const std::string& host);
 
+std::string mdns_check();
+std::string mdns_list_discovered_services();
+
 #else  // !ADB_HOST
 
 struct AdbdAuthContext;
diff --git a/adb/client/adb_client.h b/adb/client/adb_client.h
index 27be28f..caf4e86 100644
--- a/adb/client/adb_client.h
+++ b/adb/client/adb_client.h
@@ -90,8 +90,9 @@
 
 // ADB Secure DNS service interface. Used to query what ADB Secure DNS services have been
 // resolved, and to run some kind of callback for each one.
-using adb_secure_foreach_service_callback = std::function<void(
-        const char* _Nonnull service_name, const char* _Nonnull ip_address, uint16_t port)>;
+using adb_secure_foreach_service_callback =
+        std::function<void(const char* _Nonnull service_name, const char* _Nonnull reg_type,
+                           const char* _Nonnull ip_address, uint16_t port)>;
 
 // Queries pairing/connect services that have been discovered and resolved.
 // If |host_name| is not null, run |cb| only for services
diff --git a/adb/client/auth.cpp b/adb/client/auth.cpp
index 4b2fa04..b674a81 100644
--- a/adb/client/auth.cpp
+++ b/adb/client/auth.cpp
@@ -504,6 +504,12 @@
     }).detach();
 }
 
+// Callback given to SSL_set_cert_cb to select a certificate when server requests
+// for a certificate. This is where the server will give us a CA-issuer list, and
+// figure out if the server knows any of our public keys. We currently always return
+// 1 here to indicate success, since we always try a key here (in the case of no auth).
+// See https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_set_cert_cb
+// for more details.
 int adb_tls_set_certificate(SSL* ssl) {
     LOG(INFO) << __func__;
 
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 29f9dc1..d565e01 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -127,6 +127,8 @@
         "       localfilesystem:<unix domain socket name>\n"
         " reverse --remove REMOTE  remove specific reverse socket connection\n"
         " reverse --remove-all     remove all reverse socket connections from device\n"
+        " mdns check               check if mdns discovery is available\n"
+        " mdns services            list all discovered services\n"
         "\n"
         "file transfer:\n"
         " push [--sync] [-z ALGORITHM] [-Z] LOCAL... REMOTE\n"
@@ -1910,6 +1912,29 @@
 
         ReadOrderlyShutdown(fd);
         return 0;
+    } else if (!strcmp(argv[0], "mdns")) {
+        --argc;
+        if (argc < 1) error_exit("mdns requires an argument");
+        ++argv;
+
+        std::string error;
+        if (!adb_check_server_version(&error)) {
+            error_exit("failed to check server version: %s", error.c_str());
+        }
+
+        std::string query = "host:mdns:";
+        if (!strcmp(argv[0], "check")) {
+            if (argc != 1) error_exit("mdns %s doesn't take any arguments", argv[0]);
+            query += "check";
+        } else if (!strcmp(argv[0], "services")) {
+            if (argc != 1) error_exit("mdns %s doesn't take any arguments", argv[0]);
+            query += "services";
+            printf("List of discovered mdns services\n");
+        } else {
+            error_exit("unknown mdns command [%s]", argv[0]);
+        }
+
+        return adb_query_command(query);
     }
     /* do_sync_*() commands */
     else if (!strcmp(argv[0], "ls")) {
diff --git a/adb/client/incremental_server.cpp b/adb/client/incremental_server.cpp
index 1a1361c..bfe18c0 100644
--- a/adb/client/incremental_server.cpp
+++ b/adb/client/incremental_server.cpp
@@ -264,7 +264,7 @@
     char* pendingBlocks_ = nullptr;
 
     // True when client notifies that all the data has been received
-    bool servingComplete_;
+    bool servingComplete_ = false;
 };
 
 bool IncrementalServer::SkipToRequest(void* buffer, size_t* size, bool blocking) {
diff --git a/adb/client/transport_mdns.cpp b/adb/client/transport_mdns.cpp
index 22b9b18..2bf062f 100644
--- a/adb/client/transport_mdns.cpp
+++ b/adb/client/transport_mdns.cpp
@@ -216,6 +216,9 @@
 
         int adbSecureServiceType = serviceIndex();
         switch (adbSecureServiceType) {
+            case kADBTransportServiceRefIndex:
+                sAdbTransportServices->push_back(this);
+                break;
             case kADBSecurePairingServiceRefIndex:
                 sAdbSecurePairingServices->push_back(this);
                 break;
@@ -233,16 +236,21 @@
 
     std::string serviceName() const { return serviceName_; }
 
+    std::string regType() const { return regType_; }
+
     std::string ipAddress() const { return ip_addr_; }
 
     uint16_t port() const { return port_; }
 
     using ServiceRegistry = std::vector<ResolvedService*>;
 
+    // unencrypted tcp connections
+    static ServiceRegistry* sAdbTransportServices;
+
     static ServiceRegistry* sAdbSecurePairingServices;
     static ServiceRegistry* sAdbSecureConnectServices;
 
-    static void initAdbSecure();
+    static void initAdbServiceRegistries();
 
     static void forEachService(const ServiceRegistry& services, const std::string& hostname,
                                adb_secure_foreach_service_callback cb);
@@ -264,13 +272,19 @@
 };
 
 // static
+std::vector<ResolvedService*>* ResolvedService::sAdbTransportServices = NULL;
+
+// static
 std::vector<ResolvedService*>* ResolvedService::sAdbSecurePairingServices = NULL;
 
 // static
 std::vector<ResolvedService*>* ResolvedService::sAdbSecureConnectServices = NULL;
 
 // static
-void ResolvedService::initAdbSecure() {
+void ResolvedService::initAdbServiceRegistries() {
+    if (!sAdbTransportServices) {
+        sAdbTransportServices = new ServiceRegistry;
+    }
     if (!sAdbSecurePairingServices) {
         sAdbSecurePairingServices = new ServiceRegistry;
     }
@@ -283,17 +297,18 @@
 void ResolvedService::forEachService(const ServiceRegistry& services,
                                      const std::string& wanted_service_name,
                                      adb_secure_foreach_service_callback cb) {
-    initAdbSecure();
+    initAdbServiceRegistries();
 
     for (auto service : services) {
         auto service_name = service->serviceName();
+        auto reg_type = service->regType();
         auto ip = service->ipAddress();
         auto port = service->port();
 
         if (wanted_service_name == "") {
-            cb(service_name.c_str(), ip.c_str(), port);
+            cb(service_name.c_str(), reg_type.c_str(), ip.c_str(), port);
         } else if (service_name == wanted_service_name) {
-            cb(service_name.c_str(), ip.c_str(), port);
+            cb(service_name.c_str(), reg_type.c_str(), ip.c_str(), port);
         }
     }
 }
@@ -301,7 +316,7 @@
 // static
 bool ResolvedService::connectByServiceName(const ServiceRegistry& services,
                                            const std::string& service_name) {
-    initAdbSecure();
+    initAdbServiceRegistries();
     for (auto service : services) {
         if (service_name == service->serviceName()) {
             D("Got service_name match [%s]", service->serviceName().c_str());
@@ -398,6 +413,9 @@
     int index = adb_DNSServiceIndexByName(regType);
     ResolvedService::ServiceRegistry* services;
     switch (index) {
+        case kADBTransportServiceRefIndex:
+            services = ResolvedService::sAdbTransportServices;
+            break;
         case kADBSecurePairingServiceRefIndex:
             services = ResolvedService::sAdbSecurePairingServices;
             break;
@@ -542,6 +560,34 @@
 }
 
 void init_mdns_transport_discovery(void) {
-    ResolvedService::initAdbSecure();
+    ResolvedService::initAdbServiceRegistries();
     std::thread(init_mdns_transport_discovery_thread).detach();
 }
+
+std::string mdns_check() {
+    uint32_t daemon_version;
+    uint32_t sz = sizeof(daemon_version);
+
+    auto dnserr = DNSServiceGetProperty(kDNSServiceProperty_DaemonVersion, &daemon_version, &sz);
+    std::string result = "ERROR: mdns daemon unavailable";
+    if (dnserr != kDNSServiceErr_NoError) {
+        return result;
+    }
+
+    result = android::base::StringPrintf("mdns daemon version [%u]", daemon_version);
+    return result;
+}
+
+std::string mdns_list_discovered_services() {
+    std::string result;
+    auto cb = [&](const char* service_name, const char* reg_type, const char* ip_addr,
+                  uint16_t port) {
+        result += android::base::StringPrintf("%s\t%s\t%s:%u\n", service_name, reg_type, ip_addr,
+                                              port);
+    };
+
+    ResolvedService::forEachService(*ResolvedService::sAdbTransportServices, "", cb);
+    ResolvedService::forEachService(*ResolvedService::sAdbSecureConnectServices, "", cb);
+    ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices, "", cb);
+    return result;
+}
diff --git a/adb/coverage/.gitignore b/adb/coverage/.gitignore
new file mode 100644
index 0000000..b6a2582
--- /dev/null
+++ b/adb/coverage/.gitignore
@@ -0,0 +1,2 @@
+/adbd.profdata
+/report
diff --git a/adb/coverage/gen_coverage.sh b/adb/coverage/gen_coverage.sh
new file mode 100755
index 0000000..cced62a
--- /dev/null
+++ b/adb/coverage/gen_coverage.sh
@@ -0,0 +1,114 @@
+#!/bin/bash
+
+set -euxo pipefail
+
+OUTPUT_DIR=$(dirname "$0")
+. "$OUTPUT_DIR"/include.sh
+
+TRACEDIR=`mktemp -d`
+
+### Make sure we can connect to the device.
+
+# Get the device's wlan0 address.
+IP_ADDR=$(adb shell ip route get 0.0.0.0 oif wlan0 | sed -En -e 's/.*src (\S+)\s.*/\1/p')
+REMOTE_PORT=5555
+REMOTE=$IP_ADDR:$REMOTE_PORT
+LOCAL_SERIAL=$(adb shell getprop ro.serialno)
+
+# Check that we can connect to it.
+adb disconnect
+adb tcpip $REMOTE_PORT
+
+# TODO: Add `adb transport-id` and wait-for-offline on it.
+sleep 5
+
+adb connect $REMOTE
+
+REMOTE_FETCHED_SERIAL=$(adb -s $REMOTE shell getprop ro.serialno)
+
+if [[ "$LOCAL_SERIAL" != "$REMOTE_FETCHED_SERIAL" ]]; then
+  echo "Mismatch: local serial = $LOCAL_SERIAL, remote serial = $REMOTE_FETCHED_SERIAL"
+  exit 1
+fi
+
+# Back to USB, and make sure adbd is root.
+adb disconnect $REMOTE
+
+adb root
+adb wait-for-device usb
+
+# TODO: Add `adb transport-id` and wait-for-offline on it.
+sleep 5
+
+adb wait-for-device
+
+### Run the adb unit tests and fetch traces from them.
+mkdir "$TRACEDIR"/test_traces
+adb shell rm -rf /data/local/tmp/adb_coverage
+adb shell mkdir /data/local/tmp/adb_coverage
+
+for TEST in $ADB_TESTS; do
+  adb shell LLVM_PROFILE_FILE=/data/local/tmp/adb_coverage/$TEST.profraw /data/nativetest64/$TEST/$TEST
+  adb pull /data/local/tmp/adb_coverage/$TEST.profraw "$TRACEDIR"/test_traces/
+done
+
+adb pull /data/local/tmp/adb_coverage "$TRACEDIR"/test_traces
+
+# Clear logcat and increase the buffer to something ridiculous so we can fetch the pids of adbd later.
+adb shell logcat -c -G128M
+
+# Turn on extremely verbose logging so as to not count debug logging against us.
+adb shell setprop persist.adb.trace_mask 1
+
+### Run test_device.py over USB.
+adb shell killall adbd
+
+# TODO: Add `adb transport-id` and wait-for-offline on it.
+sleep 5
+
+adb wait-for-device shell rm -rf "/data/misc/trace/*" /data/local/tmp/adb_coverage/
+"$OUTPUT_DIR"/../test_device.py
+
+# Do a usb reset to exercise the disconnect code.
+adb_usbreset
+adb wait-for-device
+
+# Dump traces from the currently running adbd.
+adb shell killall -37 adbd
+
+echo Waiting for adbd to finish dumping traces
+sleep 5
+
+# Restart adbd in tcp mode.
+adb tcpip $REMOTE_PORT
+sleep 5
+adb connect $REMOTE
+adb -s $REMOTE wait-for-device
+
+# Run test_device.py again.
+ANDROID_SERIAL=$REMOTE "$OUTPUT_DIR"/../test_device.py
+
+# Dump traces again.
+adb disconnect $REMOTE
+adb shell killall -37 adbd
+
+echo Waiting for adbd to finish dumping traces
+sleep 5
+
+adb pull /data/misc/trace "$TRACEDIR"/
+echo Pulled traces to $TRACEDIR
+
+# Identify which of the trace files are actually adbd, in case something else exited simultaneously.
+ADBD_PIDS=$(adb shell "logcat -d -s adbd --format=process | grep 'adbd started' | cut -c 3-7 | tr -d ' ' | sort | uniq")
+mkdir "$TRACEDIR"/adbd_traces
+
+adb shell 'setprop persist.adb.trace_mask 0; killall adbd'
+
+IFS=$'\n'
+for PID in $ADBD_PIDS; do
+  cp "$TRACEDIR"/trace/clang-$PID-*.profraw "$TRACEDIR"/adbd_traces 2>/dev/null || true
+done
+unset IFS
+
+### Merge the traces.
+llvm-profdata merge --output="$OUTPUT_DIR"/adbd.profdata "$TRACEDIR"/adbd_traces/* "$TRACEDIR"/test_traces/*
diff --git a/adb/coverage/include.sh b/adb/coverage/include.sh
new file mode 100644
index 0000000..45ebc34
--- /dev/null
+++ b/adb/coverage/include.sh
@@ -0,0 +1,5 @@
+ADB_TESTS="adbd_test adb_crypto_test adb_pairing_auth_test adb_pairing_connection_test adb_tls_connection_test"
+ADB_TEST_BINARIES=""
+for TEST in $ADB_TESTS; do
+  ADB_TEST_BINARIES="--object=$ANDROID_PRODUCT_OUT/data/nativetest64/$TEST/$TEST $ADB_TEST_BINARIES"
+done
diff --git a/adb/coverage/report.sh b/adb/coverage/report.sh
new file mode 100755
index 0000000..257310c
--- /dev/null
+++ b/adb/coverage/report.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+set -euxo pipefail
+
+OUTPUT_DIR=$(realpath $(dirname "$0"))
+. "$OUTPUT_DIR"/include.sh
+
+rm -rf "$OUTPUT_DIR"/report
+
+cd $ANDROID_BUILD_TOP
+llvm-cov show --instr-profile="$OUTPUT_DIR"/adbd.profdata \
+  $ANDROID_PRODUCT_OUT/apex/com.android.adbd/bin/adbd \
+  /proc/self/cwd/system/core/adb \
+  $ADB_TEST_BINARIES \
+  --show-region-summary=false \
+  --format=html -o "$OUTPUT_DIR"/report
+
+llvm-cov report --instr-profile="$OUTPUT_DIR"/adbd.profdata \
+  $ANDROID_PRODUCT_OUT/apex/com.android.adbd/bin/adbd \
+  /proc/self/cwd/system/core/adb \
+  $ADB_TEST_BINARIES \
+  --show-region-summary=false
diff --git a/adb/coverage/show.sh b/adb/coverage/show.sh
new file mode 100755
index 0000000..7ea7949
--- /dev/null
+++ b/adb/coverage/show.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+set -euxo pipefail
+
+OUTPUT_DIR=$(realpath $(dirname "$0"))
+. "$OUTPUT_DIR"/include.sh
+
+cd $ANDROID_BUILD_TOP
+llvm-cov show --instr-profile="$OUTPUT_DIR"/adbd.profdata \
+  $ANDROID_PRODUCT_OUT/apex/com.android.adbd/bin/adbd \
+  /proc/self/cwd/system/core/adb \
+  $ADB_TEST_BINARIES
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 9e02e89..55b7783 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -301,6 +301,8 @@
         setup_adb(addrs);
     }
 
+    LOG(INFO) << "adbd started";
+
     D("adbd_main(): pre init_jdwp()");
     init_jdwp();
     D("adbd_main(): post init_jdwp()");
diff --git a/adb/fdevent/fdevent.cpp b/adb/fdevent/fdevent.cpp
index fd55020..70cb9b3 100644
--- a/adb/fdevent/fdevent.cpp
+++ b/adb/fdevent/fdevent.cpp
@@ -27,7 +27,10 @@
 #include "adb_utils.h"
 #include "fdevent.h"
 #include "fdevent_epoll.h"
+
+#if !defined(__linux__)
 #include "fdevent_poll.h"
+#endif
 
 using namespace std::chrono_literals;
 using std::chrono::duration_cast;
diff --git a/adb/libs/adbconnection/adbconnection_client.cpp b/adb/libs/adbconnection/adbconnection_client.cpp
index b569421..7e16148 100644
--- a/adb/libs/adbconnection/adbconnection_client.cpp
+++ b/adb/libs/adbconnection/adbconnection_client.cpp
@@ -122,16 +122,7 @@
     return nullptr;
   }
 
-#if defined(__APPLE__)
-  ctx->control_socket_.reset(socket(AF_UNIX, SOCK_SEQPACKET, 0));
-  // TODO: find a better home for all these copies of the mac CLOEXEC workaround.
-  if (int fd = ctx->control_socket_.get(), flags = fcntl(fd, F_GETFD);
-      flags != -1 && (flags & FD_CLOEXEC) == 0) {
-    fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
-  }
-#else
   ctx->control_socket_.reset(socket(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0));
-#endif
   if (ctx->control_socket_ < 0) {
     PLOG(ERROR) << "failed to create Unix domain socket";
     return nullptr;
@@ -149,7 +140,13 @@
 
   int rc = connect(ctx->control_socket_.get(), reinterpret_cast<sockaddr*>(&addr), addr_len);
   if (rc != 0) {
-    PLOG(ERROR) << "failed to connect to jdwp control socket";
+    if (errno == ECONNREFUSED) {
+      // On userdebug devices, every Java process is debuggable, so if adbd is explicitly turned
+      // off, this would spew enormous amounts of red-herring errors.
+      LOG(DEBUG) << "failed to connect to jdwp control socket, adbd not running?";
+    } else {
+      PLOG(ERROR) << "failed to connect to jdwp control socket";
+    }
     return nullptr;
   }
 
diff --git a/adb/mdns_test.cpp b/adb/mdns_test.cpp
new file mode 100644
index 0000000..1f662c1
--- /dev/null
+++ b/adb/mdns_test.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "adb_mdns.h"
+
+static bool isValidMdnsServiceName(std::string_view name) {
+    // The rules for Service Names [RFC6335] state that they may be no more
+    // than fifteen characters long (not counting the mandatory underscore),
+    // consisting of only letters, digits, and hyphens, must begin and end
+    // with a letter or digit, must not contain consecutive hyphens, and
+    // must contain at least one letter.
+
+    // No more than 15 characters long
+    if (name.empty() || name.size() > 15) {
+        return false;
+    }
+
+    bool hasAtLeastOneLetter = false;
+    bool sawHyphen = false;
+    for (size_t i = 0; i < name.size(); ++i) {
+        // Must contain at least one letter
+        // Only contains letters, digits and hyphens
+        if (name[i] == '-') {
+            // Cannot be at beginning or end
+            if (i == 0 || i == name.size() - 1) {
+                return false;
+            }
+            if (sawHyphen) {
+                // Consecutive hyphen found
+                return false;
+            }
+            sawHyphen = true;
+            continue;
+        }
+
+        sawHyphen = false;
+        if ((name[i] >= 'a' && name[i] <= 'z') || (name[i] >= 'A' && name[i] <= 'Z')) {
+            hasAtLeastOneLetter = true;
+            continue;
+        }
+
+        if (name[i] >= '0' && name[i] <= '9') {
+            continue;
+        }
+
+        // Invalid character
+        return false;
+    }
+
+    return hasAtLeastOneLetter;
+}
+
+TEST(mdns, test_isValidMdnsServiceName) {
+    // Longer than 15 characters
+    EXPECT_FALSE(isValidMdnsServiceName("abcd1234abcd1234"));
+
+    // Contains invalid characters
+    EXPECT_FALSE(isValidMdnsServiceName("a*a"));
+    EXPECT_FALSE(isValidMdnsServiceName("a_a"));
+    EXPECT_FALSE(isValidMdnsServiceName("_a"));
+
+    // Does not begin or end with letter or digit
+    EXPECT_FALSE(isValidMdnsServiceName(""));
+    EXPECT_FALSE(isValidMdnsServiceName("-"));
+    EXPECT_FALSE(isValidMdnsServiceName("-a"));
+    EXPECT_FALSE(isValidMdnsServiceName("-1"));
+    EXPECT_FALSE(isValidMdnsServiceName("a-"));
+    EXPECT_FALSE(isValidMdnsServiceName("1-"));
+
+    // Contains consecutive hyphens
+    EXPECT_FALSE(isValidMdnsServiceName("a--a"));
+
+    // Does not contain at least one letter
+    EXPECT_FALSE(isValidMdnsServiceName("1"));
+    EXPECT_FALSE(isValidMdnsServiceName("12"));
+    EXPECT_FALSE(isValidMdnsServiceName("1-2"));
+
+    // Some valid names
+    EXPECT_TRUE(isValidMdnsServiceName("a"));
+    EXPECT_TRUE(isValidMdnsServiceName("a1"));
+    EXPECT_TRUE(isValidMdnsServiceName("1A"));
+    EXPECT_TRUE(isValidMdnsServiceName("aZ"));
+    EXPECT_TRUE(isValidMdnsServiceName("a-Z"));
+    EXPECT_TRUE(isValidMdnsServiceName("a-b-Z"));
+    EXPECT_TRUE(isValidMdnsServiceName("abc-def-123-456"));
+}
+
+TEST(mdns, ServiceName_RFC6335) {
+    EXPECT_TRUE(isValidMdnsServiceName(ADB_MDNS_SERVICE_TYPE));
+    EXPECT_TRUE(isValidMdnsServiceName(ADB_MDNS_TLS_PAIRING_TYPE));
+    EXPECT_TRUE(isValidMdnsServiceName(ADB_MDNS_TLS_CONNECT_TYPE));
+}
diff --git a/adb/services.cpp b/adb/services.cpp
index d87948c..19a9030 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -202,11 +202,22 @@
                                       transport_id, &is_ambiguous, &error);
 
         for (const auto& state : states) {
-            // wait-for-disconnect uses kCsOffline, we don't actually want to wait for 'offline'.
-            if ((t == nullptr && state == kCsOffline) || (t != nullptr && state == kCsAny) ||
-                (t != nullptr && state == t->GetConnectionState())) {
-                SendOkay(fd);
-                return;
+            if (state == kCsOffline) {
+                // Special case for wait-for-disconnect:
+                // We want to wait for USB devices to completely disappear, but TCP devices can
+                // go into the offline state, since we automatically reconnect.
+                if (!t) {
+                    SendOkay(fd);
+                    return;
+                } else if (!t->GetUsbHandle()) {
+                    SendOkay(fd);
+                    return;
+                }
+            } else {
+                if (t && (state == kCsAny || state == t->GetConnectionState())) {
+                    SendOkay(fd);
+                    return;
+                }
             }
         }
 
diff --git a/adb/test_adb.py b/adb/test_adb.py
index c872fb0..03bdcbd 100755
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -32,6 +32,8 @@
 import time
 import unittest
 import warnings
+from importlib import util
+from parameterized import parameterized_class
 
 def find_open_port():
     # Find an open port.
@@ -576,6 +578,98 @@
         # If the power event was detected, the adb shell command should be broken very quickly.
         self.assertLess(end - start, 2)
 
+"""Use 'adb mdns check' to see if mdns discovery is available."""
+def is_adb_mdns_available():
+    with adb_server() as server_port:
+        output = subprocess.check_output(["adb", "-P", str(server_port),
+                                          "mdns", "check"]).strip()
+        return output.startswith(b"mdns daemon version")
+
+"""Check if we have zeroconf python library installed"""
+def is_zeroconf_installed():
+    zeroconf_spec = util.find_spec("zeroconf")
+    return zeroconf_spec is not None
+
+@contextlib.contextmanager
+def zeroconf_context(ipversion):
+    from zeroconf import Zeroconf
+    """Context manager for a zeroconf instance
+
+    This creates a zeroconf instance and returns it.
+    """
+
+    try:
+        zeroconf = Zeroconf(ip_version=ipversion)
+        yield zeroconf
+    finally:
+        zeroconf.close()
+
+@contextlib.contextmanager
+def zeroconf_register_service(zeroconf_ctx, info):
+    """Context manager for a zeroconf service
+
+    Registers a service and unregisters it on cleanup. Returns the ServiceInfo
+    supplied.
+    """
+
+    try:
+        zeroconf_ctx.register_service(info)
+        yield info
+    finally:
+        zeroconf_ctx.unregister_service(info)
+
+"""Should match the service names listed in adb_mdns.h"""
+@parameterized_class(('service_name',), [
+    ("adb",),
+    ("adb-tls-connect",),
+    ("adb-tls-pairing",),
+])
+@unittest.skipIf(not is_adb_mdns_available(), "mdns feature not available")
+class MdnsTest(unittest.TestCase):
+    """Tests for adb mdns."""
+
+    @unittest.skipIf(not is_zeroconf_installed(), "zeroconf library not installed")
+    def test_mdns_services_register_unregister(self):
+        """Ensure that `adb mdns services` correctly adds and removes a service
+        """
+        from zeroconf import IPVersion, ServiceInfo
+ 
+        def _mdns_services(port):
+            output = subprocess.check_output(["adb", "-P", str(port), "mdns", "services"])
+            return [x.split("\t") for x in output.decode("utf8").strip().splitlines()[1:]]
+
+        with adb_server() as server_port:
+            output = subprocess.check_output(["adb", "-P", str(server_port),
+                                              "mdns", "services"]).strip()
+            self.assertTrue(output.startswith(b"List of discovered mdns services"))
+            print(f"services={_mdns_services(server_port)}")
+
+            """TODO(joshuaduong): Add ipv6 tests once we have it working in adb"""
+            """Register/Unregister a service"""
+            with zeroconf_context(IPVersion.V4Only) as zc:
+                serv_instance = "my_fake_test_service"
+                serv_type = "_" + self.service_name + "._tcp."
+                serv_ipaddr = socket.inet_aton("1.2.3.4")
+                serv_port = 12345
+                service_info = ServiceInfo(
+                        serv_type + "local.",
+                        name=serv_instance + "." + serv_type + "local.",
+                        addresses=[serv_ipaddr],
+                        port=serv_port)
+                print(f"Registering {serv_instance}.{serv_type} ...")
+                with zeroconf_register_service(zc, service_info) as info:
+                    """Give adb some time to register the service"""
+                    time.sleep(0.25)
+                    print(f"services={_mdns_services(server_port)}")
+                    self.assertTrue(any((serv_instance in line and serv_type in line)
+                        for line in _mdns_services(server_port)))
+
+                """Give adb some time to unregister the service"""
+                print("Unregistering mdns service...")
+                time.sleep(0.25)
+                print(f"services={_mdns_services(server_port)}")
+                self.assertFalse(any((serv_instance in line and serv_type in line)
+                    for line in _mdns_services(server_port)))
 
 def main():
     """Main entrypoint."""
diff --git a/adb/tools/Android.bp b/adb/tools/Android.bp
index 71e32b7..a7af53c 100644
--- a/adb/tools/Android.bp
+++ b/adb/tools/Android.bp
@@ -34,3 +34,26 @@
         ],
     },
 }
+
+cc_binary_host {
+    name: "adb_usbreset",
+
+    defaults: ["adb_defaults"],
+
+    srcs: [
+        "adb_usbreset.cpp",
+    ],
+
+    static_libs: [
+        "libbase",
+        "libusb",
+    ],
+
+    stl: "libc++_static",
+
+    dist: {
+        targets: [
+            "sdk",
+        ],
+    },
+}
diff --git a/adb/tools/adb_usbreset.cpp b/adb/tools/adb_usbreset.cpp
new file mode 100644
index 0000000..6f141bd
--- /dev/null
+++ b/adb/tools/adb_usbreset.cpp
@@ -0,0 +1,188 @@
+// Copyright (C) 2020 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 <err.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <string>
+#include <string_view>
+#include <variant>
+#include <vector>
+
+#include <libusb/libusb.h>
+
+struct AllDevices {};
+struct SingleDevice {};
+struct Serial {
+    std::string_view serial;
+};
+
+using DeviceSelection = std::variant<std::monostate, AllDevices, SingleDevice, Serial>;
+
+[[noreturn]] static void Usage(int rc) {
+    fprintf(stderr, "usage: [ANDROID_SERIAL=SERIAL] usbreset [-d] [-s SERIAL]\n");
+    fprintf(stderr, "\t-a --all\t\tReset all connected devices\n");
+    fprintf(stderr, "\t-d --device\t\tReset the single connected device\n");
+    fprintf(stderr, "\t-s --serial\t\tReset device with specified serial\n");
+    exit(rc);
+}
+
+static void SetOption(DeviceSelection* out, DeviceSelection in) {
+    if (!std::get_if<std::monostate>(out)) {
+        printf("error: multiple device selection options provided\n");
+        Usage(1);
+    }
+
+    *out = in;
+}
+
+static __attribute__((format(printf, 2, 3))) void PrintLibusbError(int err, const char* fmt, ...) {
+    va_list args;
+    va_start(args, fmt);
+    vprintf(fmt, args);
+    vprintf(fmt, args);
+    va_end(args);
+
+    printf(": %s", libusb_strerror(static_cast<libusb_error>(err)));
+}
+
+static bool IsAdbInterface(const libusb_interface_descriptor* desc) {
+    return desc->bInterfaceClass == 0xFF && desc->bInterfaceSubClass == 0x42 &&
+           desc->bInterfaceProtocol == 0x1;
+}
+
+int main(int argc, char** argv) {
+    std::variant<std::monostate, AllDevices, SingleDevice, Serial> selection;
+
+    static constexpr struct option long_opts[] = {
+            {"all", 0, 0, 'a'},    {"help", 0, 0, 'h'}, {"serial", required_argument, 0, 's'},
+            {"device", 0, 0, 'd'}, {0, 0, 0, 0},
+    };
+
+    int opt;
+    while ((opt = getopt_long(argc, argv, "adhs:", long_opts, nullptr)) != -1) {
+        if (opt == 'h') {
+            Usage(0);
+        } else if (opt == 'a') {
+            SetOption(&selection, AllDevices{});
+        } else if (opt == 's') {
+            SetOption(&selection, Serial{optarg});
+        } else if (opt == 'd') {
+            SetOption(&selection, Serial{optarg});
+        } else {
+            errx(1, "unknown option: '%c'", opt);
+        }
+    }
+
+    if (std::get_if<std::monostate>(&selection)) {
+        const char* env = getenv("ANDROID_SERIAL");
+        if (env) {
+            SetOption(&selection, Serial{env});
+        } else {
+            fprintf(stderr, "adb_usbreset: no device specified\n");
+            Usage(1);
+        }
+    }
+
+    libusb_context* ctx;
+    int rc = libusb_init(&ctx);
+    if (rc != LIBUSB_SUCCESS) {
+        PrintLibusbError(rc, "error: failed to initialize libusb");
+        exit(1);
+    }
+
+    libusb_device** device_list;
+    ssize_t device_count = libusb_get_device_list(ctx, &device_list);
+    if (device_count < 0) {
+        PrintLibusbError(device_count, "error: failed to list devices");
+        exit(1);
+    }
+
+    std::vector<std::pair<std::string, libusb_device_handle*>> selected_devices;
+    for (int i = 0; i < device_count; ++i) {
+        libusb_device* device = device_list[i];
+        libusb_device_descriptor device_desc;
+
+        // Always succeeds for LIBUSB_API_VERSION >= 0x01000102.
+        libusb_get_device_descriptor(device, &device_desc);
+        static_assert(LIBUSB_API_VERSION >= 0x01000102);
+
+        libusb_config_descriptor* config_desc;
+        rc = libusb_get_active_config_descriptor(device, &config_desc);
+        if (rc != 0) {
+            PrintLibusbError(rc, "warning: failed to get config descriptor");
+            continue;
+        }
+
+        bool found_adb_interface = false;
+        for (int i = 0; i < config_desc->bNumInterfaces; ++i) {
+            if (IsAdbInterface(&config_desc->interface[i].altsetting[0])) {
+                found_adb_interface = true;
+                break;
+            }
+        }
+
+        if (found_adb_interface) {
+            libusb_device_handle* device_handle;
+            rc = libusb_open(device, &device_handle);
+            if (rc != 0) {
+                PrintLibusbError(rc, "warning: failed to open device");
+                continue;
+            }
+
+            char buf[128];
+            rc = libusb_get_string_descriptor_ascii(device_handle, device_desc.iSerialNumber,
+                                                    reinterpret_cast<unsigned char*>(buf),
+                                                    sizeof(buf));
+
+            if (rc < 0) {
+                PrintLibusbError(rc, "warning: failed to get device serial");
+                continue;
+            }
+
+            std::string serial(buf, buf + rc);
+            if (auto s = std::get_if<Serial>(&selection)) {
+                if (s->serial == serial) {
+                    selected_devices.push_back(std::make_pair(std::move(serial), device_handle));
+                }
+            } else {
+                selected_devices.push_back(std::make_pair(std::move(serial), device_handle));
+            }
+        }
+    }
+
+    if (selected_devices.empty()) {
+        errx(1, "no devices match criteria");
+    } else if (std::get_if<SingleDevice>(&selection) && selected_devices.size() != 1) {
+        errx(1, "more than 1 device connected");
+    }
+
+    bool success = true;
+    for (auto& [serial, device_handle] : selected_devices) {
+        rc = libusb_reset_device(device_handle);
+        // libusb_reset_device will try to restore the previous state, and will return
+        // LIBUSB_ERROR_NOT_FOUND if it can't.
+        if (rc == 0 || rc == LIBUSB_ERROR_NOT_FOUND) {
+            printf("%s: successfully reset\n", serial.c_str());
+        } else {
+            PrintLibusbError(rc, "%s: failed to reset", serial.c_str());
+            success = false;
+        }
+    }
+
+    return !success;
+}
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 963c3c1..25ed366 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -505,11 +505,10 @@
 
     int osh = cast_handle_to_int(adb_get_os_handle(fd_));
 #if ADB_HOST
-    tls_ = TlsConnection::Create(TlsConnection::Role::Client,
+    tls_ = TlsConnection::Create(TlsConnection::Role::Client, x509_str, evp_str, osh);
 #else
-    tls_ = TlsConnection::Create(TlsConnection::Role::Server,
+    tls_ = TlsConnection::Create(TlsConnection::Role::Server, x509_str, evp_str, osh);
 #endif
-                                 x509_str, evp_str, osh);
     CHECK(tls_);
 #if ADB_HOST
     // TLS 1.3 gives the client no message if the server rejected the
diff --git a/base/logging.cpp b/base/logging.cpp
index cd460eb..3c73fea 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -195,7 +195,6 @@
   return logging_lock;
 }
 
-// Only used for Q fallback.
 static LogFunction& Logger() {
 #ifdef __ANDROID__
   static auto& logger = *new LogFunction(LogdLogger());
@@ -205,7 +204,6 @@
   return logger;
 }
 
-// Only used for Q fallback.
 static AbortFunction& Aborter() {
   static auto& aborter = *new AbortFunction(DefaultAborter);
   return aborter;
@@ -416,45 +414,27 @@
 }
 
 void SetLogger(LogFunction&& logger) {
+  Logger() = std::move(logger);
+
   static auto& liblog_functions = GetLibLogFunctions();
   if (liblog_functions) {
-    // We need to atomically swap the old and new pointers since other threads may be logging.
-    // We know all threads will be using the new logger after __android_log_set_logger() returns,
-    // so we can delete it then.
-    // This leaks one std::function<> per instance of libbase if multiple copies of libbase within a
-    // single process call SetLogger().  That is the same cost as having a static
-    // std::function<>, which is the not-thread-safe alternative.
-    static std::atomic<LogFunction*> logger_function(nullptr);
-    auto* old_logger_function = logger_function.exchange(new LogFunction(logger));
     liblog_functions->__android_log_set_logger([](const struct __android_log_message* log_message) {
       auto log_id = log_id_tToLogId(log_message->buffer_id);
       auto severity = PriorityToLogSeverity(log_message->priority);
 
-      auto& function = *logger_function.load(std::memory_order_acquire);
-      function(log_id, severity, log_message->tag, log_message->file, log_message->line,
+      Logger()(log_id, severity, log_message->tag, log_message->file, log_message->line,
                log_message->message);
     });
-    delete old_logger_function;
-  } else {
-    std::lock_guard<std::mutex> lock(LoggingLock());
-    Logger() = std::move(logger);
   }
 }
 
 void SetAborter(AbortFunction&& aborter) {
+  Aborter() = std::move(aborter);
+
   static auto& liblog_functions = GetLibLogFunctions();
   if (liblog_functions) {
-    // See the comment in SetLogger().
-    static std::atomic<AbortFunction*> abort_function(nullptr);
-    auto* old_abort_function = abort_function.exchange(new AbortFunction(aborter));
-    liblog_functions->__android_log_set_aborter([](const char* abort_message) {
-      auto& function = *abort_function.load(std::memory_order_acquire);
-      function(abort_message);
-    });
-    delete old_abort_function;
-  } else {
-    std::lock_guard<std::mutex> lock(LoggingLock());
-    Aborter() = std::move(aborter);
+    liblog_functions->__android_log_set_aborter(
+        [](const char* abort_message) { Aborter()(abort_message); });
   }
 }
 
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 0c2569d..d2daaa1 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -62,6 +62,7 @@
 #include <fs_mgr_overlayfs.h>
 #include <fscrypt/fscrypt.h>
 #include <libdm/dm.h>
+#include <libdm/loop_control.h>
 #include <liblp/metadata_format.h>
 #include <linux/fs.h>
 #include <linux/loop.h>
@@ -105,6 +106,7 @@
 using android::dm::DeviceMapper;
 using android::dm::DmDeviceState;
 using android::dm::DmTargetLinear;
+using android::dm::LoopControl;
 
 // Realistically, this file should be part of the android::fs_mgr namespace;
 using namespace android::fs_mgr;
@@ -1908,19 +1910,6 @@
         return InstallZramDevice(bdev);
     }
 
-    // Get free loopback
-    unique_fd loop_fd(TEMP_FAILURE_RETRY(open("/dev/loop-control", O_RDWR | O_CLOEXEC)));
-    if (loop_fd.get() == -1) {
-        PERROR << "Cannot open loop-control";
-        return false;
-    }
-
-    int num = ioctl(loop_fd.get(), LOOP_CTL_GET_FREE);
-    if (num == -1) {
-        PERROR << "Cannot get free loop slot";
-        return false;
-    }
-
     // Prepare target path
     unique_fd target_fd(TEMP_FAILURE_RETRY(open(loop.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600)));
     if (target_fd.get() == -1) {
@@ -1932,25 +1921,21 @@
         return false;
     }
 
-    // Connect loopback (device_fd) to target path (target_fd)
-    std::string device = android::base::StringPrintf("/dev/block/loop%d", num);
-    unique_fd device_fd(TEMP_FAILURE_RETRY(open(device.c_str(), O_RDWR | O_CLOEXEC)));
-    if (device_fd.get() == -1) {
-        PERROR << "Cannot open /dev/block/loop" << num;
-        return false;
-    }
-
-    if (ioctl(device_fd.get(), LOOP_SET_FD, target_fd.get())) {
-        PERROR << "Cannot set loopback to target path";
+    // Allocate loop device and attach it to file_path.
+    LoopControl loop_control;
+    std::string device;
+    if (!loop_control.Attach(target_fd.get(), 5s, &device)) {
         return false;
     }
 
     // set block size & direct IO
-    if (ioctl(device_fd.get(), LOOP_SET_BLOCK_SIZE, 4096)) {
-        PWARNING << "Cannot set 4KB blocksize to /dev/block/loop" << num;
+    unique_fd device_fd(TEMP_FAILURE_RETRY(open(device.c_str(), O_RDWR | O_CLOEXEC)));
+    if (device_fd.get() == -1) {
+        PERROR << "Cannot open " << device;
+        return false;
     }
-    if (ioctl(device_fd.get(), LOOP_SET_DIRECT_IO, 1)) {
-        PWARNING << "Cannot set direct_io to /dev/block/loop" << num;
+    if (!LoopControl::EnableDirectIo(device_fd.get())) {
+        return false;
     }
 
     return InstallZramDevice(device);
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index d496466..2f516fa 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -40,6 +40,20 @@
     return true;
 }
 
+bool LinearExtent::OverlapsWith(const LinearExtent& other) const {
+    if (device_index_ != other.device_index()) {
+        return false;
+    }
+    return physical_sector() < other.end_sector() && other.physical_sector() < end_sector();
+}
+
+bool LinearExtent::OverlapsWith(const Interval& interval) const {
+    if (device_index_ != interval.device_index) {
+        return false;
+    }
+    return physical_sector() < interval.end && interval.start < end_sector();
+}
+
 Interval LinearExtent::AsInterval() const {
     return Interval(device_index(), physical_sector(), end_sector());
 }
@@ -774,8 +788,7 @@
 bool MetadataBuilder::IsAnyRegionCovered(const std::vector<Interval>& regions,
                                          const LinearExtent& candidate) const {
     for (const auto& region : regions) {
-        if (region.device_index == candidate.device_index() &&
-            (candidate.OwnsSector(region.start) || candidate.OwnsSector(region.end))) {
+        if (candidate.OverlapsWith(region)) {
             return true;
         }
     }
@@ -786,11 +799,10 @@
     for (const auto& partition : partitions_) {
         for (const auto& extent : partition->extents()) {
             LinearExtent* linear = extent->AsLinearExtent();
-            if (!linear || linear->device_index() != candidate.device_index()) {
+            if (!linear) {
                 continue;
             }
-            if (linear->OwnsSector(candidate.physical_sector()) ||
-                linear->OwnsSector(candidate.end_sector() - 1)) {
+            if (linear->OverlapsWith(candidate)) {
                 return true;
             }
         }
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index ca8df61..977ebe3 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -937,3 +937,85 @@
     EXPECT_EQ(exported->header.header_size, sizeof(LpMetadataHeaderV1_2));
     EXPECT_EQ(exported->header.flags, 0x5e5e5e5e);
 }
+
+static Interval ToInterval(const std::unique_ptr<Extent>& extent) {
+    if (LinearExtent* le = extent->AsLinearExtent()) {
+        return le->AsInterval();
+    }
+    return {0, 0, 0};
+}
+
+static void AddPartition(const std::unique_ptr<MetadataBuilder>& builder,
+                         const std::string& partition_name, uint64_t num_sectors,
+                         uint64_t start_sector, std::vector<Interval>* intervals) {
+    Partition* p = builder->AddPartition(partition_name, "group", 0);
+    ASSERT_NE(p, nullptr);
+    ASSERT_TRUE(builder->AddLinearExtent(p, "super", num_sectors, start_sector));
+    ASSERT_EQ(p->extents().size(), 1);
+
+    if (!intervals) {
+        return;
+    }
+
+    auto new_interval = ToInterval(p->extents().back());
+    std::vector<Interval> new_intervals = {new_interval};
+
+    auto overlap = Interval::Intersect(*intervals, new_intervals);
+    ASSERT_TRUE(overlap.empty());
+
+    intervals->push_back(new_interval);
+}
+
+TEST_F(BuilderTest, CollidedExtents) {
+    BlockDeviceInfo super("super", 8_GiB, 786432, 229376, 4096);
+    std::vector<BlockDeviceInfo> block_devices = {super};
+
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(block_devices, "super", 65536, 2);
+    ASSERT_NE(builder, nullptr);
+
+    ASSERT_TRUE(builder->AddGroup("group", 0));
+
+    std::vector<Interval> old_intervals;
+    AddPartition(builder, "system", 10229008, 2048, &old_intervals);
+    AddPartition(builder, "test_a", 648, 12709888, &old_intervals);
+    AddPartition(builder, "test_b", 625184, 12711936, &old_intervals);
+    AddPartition(builder, "test_c", 130912, 13338624, &old_intervals);
+    AddPartition(builder, "test_d", 888, 13469696, &old_intervals);
+    AddPartition(builder, "test_e", 888, 13471744, &old_intervals);
+    AddPartition(builder, "test_f", 888, 13475840, &old_intervals);
+    AddPartition(builder, "test_g", 888, 13477888, &old_intervals);
+
+    // Don't track the first vendor interval, since it will get extended.
+    AddPartition(builder, "vendor", 2477920, 10231808, nullptr);
+
+    std::vector<Interval> new_intervals;
+
+    Partition* p = builder->FindPartition("vendor");
+    ASSERT_NE(p, nullptr);
+    ASSERT_TRUE(builder->ResizePartition(p, 1282031616));
+    ASSERT_GE(p->extents().size(), 1);
+    for (const auto& extent : p->extents()) {
+        new_intervals.push_back(ToInterval(extent));
+    }
+
+    std::vector<Interval> overlap = Interval::Intersect(old_intervals, new_intervals);
+    ASSERT_TRUE(overlap.empty());
+}
+
+TEST_F(BuilderTest, LinearExtentOverlap) {
+    LinearExtent extent(20, 0, 10);
+
+    EXPECT_TRUE(extent.OverlapsWith(LinearExtent{20, 0, 10}));
+    EXPECT_TRUE(extent.OverlapsWith(LinearExtent{50, 0, 10}));
+    EXPECT_FALSE(extent.OverlapsWith(LinearExtent{20, 0, 30}));
+    EXPECT_FALSE(extent.OverlapsWith(LinearExtent{10, 0, 0}));
+    EXPECT_TRUE(extent.OverlapsWith(LinearExtent{20, 0, 0}));
+    EXPECT_TRUE(extent.OverlapsWith(LinearExtent{40, 0, 0}));
+    EXPECT_TRUE(extent.OverlapsWith(LinearExtent{20, 0, 15}));
+
+    EXPECT_FALSE(extent.OverlapsWith(LinearExtent{20, 1, 0}));
+    EXPECT_FALSE(extent.OverlapsWith(LinearExtent{50, 1, 10}));
+    EXPECT_FALSE(extent.OverlapsWith(LinearExtent{40, 1, 0}));
+    EXPECT_FALSE(extent.OverlapsWith(LinearExtent{20, 1, 15}));
+    EXPECT_FALSE(extent.OverlapsWith(LinearExtent{20, 1, 10}));
+}
diff --git a/fs_mgr/liblp/device_test.cpp b/fs_mgr/liblp/device_test.cpp
index 382d53d..99bff6e 100644
--- a/fs_mgr/liblp/device_test.cpp
+++ b/fs_mgr/liblp/device_test.cpp
@@ -56,6 +56,10 @@
 
     // Having an alignment offset > alignment doesn't really make sense.
     EXPECT_LT(device_info.alignment_offset, device_info.alignment);
+
+    if (IPropertyFetcher::GetInstance()->GetBoolProperty("ro.virtual_ab.enabled", false)) {
+        EXPECT_EQ(device_info.alignment_offset, 0);
+    }
 }
 
 TEST_F(DeviceTest, ReadSuperPartitionCurrentSlot) {
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index f7738fb..bd39150 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -71,9 +71,8 @@
     uint64_t end_sector() const { return physical_sector_ + num_sectors_; }
     uint32_t device_index() const { return device_index_; }
 
-    bool OwnsSector(uint64_t sector) const {
-        return sector >= physical_sector_ && sector < end_sector();
-    }
+    bool OverlapsWith(const LinearExtent& other) const;
+    bool OverlapsWith(const Interval& interval) const;
 
     Interval AsInterval() const;
 
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index bd788f5..384595d 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -26,7 +26,6 @@
         "libbase",
         "libcutils",
         "liblog",
-        "liblp",
     ],
     static_libs: [
         "libdm",
@@ -73,6 +72,7 @@
         "device_info.cpp",
         "snapshot.cpp",
         "snapshot_stats.cpp",
+        "snapshot_stub.cpp",
         "snapshot_metadata_updater.cpp",
         "partition_cow_creator.cpp",
         "return.cpp",
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h
new file mode 100644
index 0000000..ef9d648
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h
@@ -0,0 +1,37 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <libsnapshot/snapshot.h>
+
+#include <gmock/gmock.h>
+
+namespace android::snapshot {
+
+class MockDeviceInfo : public SnapshotManager::IDeviceInfo {
+  public:
+    MOCK_METHOD(std::string, GetGsidDir, (), (const, override));
+    MOCK_METHOD(std::string, GetMetadataDir, (), (const, override));
+    MOCK_METHOD(std::string, GetSlotSuffix, (), (const, override));
+    MOCK_METHOD(std::string, GetOtherSlotSuffix, (), (const, override));
+    MOCK_METHOD(std::string, GetSuperDevice, (uint32_t slot), (const, override));
+    MOCK_METHOD(const android::fs_mgr::IPartitionOpener&, GetPartitionOpener, (), (const));
+    MOCK_METHOD(bool, IsOverlayfsSetup, (), (const, override));
+    MOCK_METHOD(bool, SetBootControlMergeStatus, (MergeStatus status), (override));
+    MOCK_METHOD(bool, SetSlotAsUnbootable, (unsigned int slot), (override));
+    MOCK_METHOD(bool, IsRecovery, (), (const, override));
+};
+
+}  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
new file mode 100644
index 0000000..758d66c
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
@@ -0,0 +1,54 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <libsnapshot/snapshot.h>
+
+#include <gmock/gmock.h>
+
+namespace android::snapshot {
+
+class MockSnapshotManager : public ISnapshotManager {
+  public:
+    MOCK_METHOD(bool, BeginUpdate, (), (override));
+    MOCK_METHOD(bool, CancelUpdate, (), (override));
+    MOCK_METHOD(bool, FinishedSnapshotWrites, (bool wipe), (override));
+    MOCK_METHOD(bool, InitiateMerge, (), (override));
+
+    MOCK_METHOD(UpdateState, ProcessUpdateState,
+                (const std::function<bool()>& callback, const std::function<bool()>& before_cancel),
+                (override));
+    MOCK_METHOD(UpdateState, GetUpdateState, (double* progress), (override));
+    MOCK_METHOD(Return, CreateUpdateSnapshots,
+                (const chromeos_update_engine::DeltaArchiveManifest& manifest), (override));
+    MOCK_METHOD(bool, MapUpdateSnapshot,
+                (const android::fs_mgr::CreateLogicalPartitionParams& params,
+                 std::string* snapshot_path),
+                (override));
+    MOCK_METHOD(bool, UnmapUpdateSnapshot, (const std::string& target_partition_name), (override));
+    MOCK_METHOD(bool, NeedSnapshotsInFirstStageMount, (), (override));
+    MOCK_METHOD(bool, CreateLogicalAndSnapshotPartitions,
+                (const std::string& super_device, const std::chrono::milliseconds& timeout_ms),
+                (override));
+    MOCK_METHOD(bool, HandleImminentDataWipe, (const std::function<void()>& callback), (override));
+    MOCK_METHOD(CreateResult, RecoveryCreateSnapshotDevices, (), (override));
+    MOCK_METHOD(CreateResult, RecoveryCreateSnapshotDevices,
+                (const std::unique_ptr<AutoDevice>& metadata_device), (override));
+    MOCK_METHOD(bool, Dump, (std::ostream & os), (override));
+    MOCK_METHOD(std::unique_ptr<AutoDevice>, EnsureMetadataMounted, (), (override));
+    MOCK_METHOD(ISnapshotMergeStats*, GetSnapshotMergeStatsInstance, (), (override));
+};
+
+}  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 1daa83b..fff667e 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -70,6 +70,8 @@
 struct AutoDeleteSnapshot;
 struct AutoDeviceList;
 struct PartitionCowCreator;
+class ISnapshotMergeStats;
+class SnapshotMergeStats;
 class SnapshotStatus;
 
 static constexpr const std::string_view kCowGroupName = "cow";
@@ -83,17 +85,7 @@
     NOT_CREATED,
 };
 
-class SnapshotManager final {
-    using CreateLogicalPartitionParams = android::fs_mgr::CreateLogicalPartitionParams;
-    using IPartitionOpener = android::fs_mgr::IPartitionOpener;
-    using LpMetadata = android::fs_mgr::LpMetadata;
-    using MetadataBuilder = android::fs_mgr::MetadataBuilder;
-    using DeltaArchiveManifest = chromeos_update_engine::DeltaArchiveManifest;
-    using MergeStatus = android::hardware::boot::V1_1::MergeStatus;
-    using FiemapStatus = android::fiemap::FiemapStatus;
-
-    friend class SnapshotMergeStats;
-
+class ISnapshotManager {
   public:
     // Dependency injection for testing.
     class IDeviceInfo {
@@ -104,39 +96,23 @@
         virtual std::string GetSlotSuffix() const = 0;
         virtual std::string GetOtherSlotSuffix() const = 0;
         virtual std::string GetSuperDevice(uint32_t slot) const = 0;
-        virtual const IPartitionOpener& GetPartitionOpener() const = 0;
+        virtual const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const = 0;
         virtual bool IsOverlayfsSetup() const = 0;
-        virtual bool SetBootControlMergeStatus(MergeStatus status) = 0;
+        virtual bool SetBootControlMergeStatus(
+                android::hardware::boot::V1_1::MergeStatus status) = 0;
         virtual bool SetSlotAsUnbootable(unsigned int slot) = 0;
         virtual bool IsRecovery() const = 0;
     };
-
-    ~SnapshotManager();
-
-    // Return a new SnapshotManager instance, or null on error. The device
-    // pointer is owned for the lifetime of SnapshotManager. If null, a default
-    // instance will be created.
-    static std::unique_ptr<SnapshotManager> New(IDeviceInfo* device = nullptr);
-
-    // This is similar to New(), except designed specifically for first-stage
-    // init or recovery.
-    static std::unique_ptr<SnapshotManager> NewForFirstStageMount(IDeviceInfo* device = nullptr);
-
-    // Helper function for first-stage init to check whether a SnapshotManager
-    // might be needed to perform first-stage mounts.
-    static bool IsSnapshotManagerNeeded();
-
-    // Helper function for second stage init to restorecon on the rollback indicator.
-    static std::string GetGlobalRollbackIndicatorPath();
+    virtual ~ISnapshotManager() = default;
 
     // Begin an update. This must be called before creating any snapshots. It
     // will fail if GetUpdateState() != None.
-    bool BeginUpdate();
+    virtual bool BeginUpdate() = 0;
 
     // Cancel an update; any snapshots will be deleted. This is allowed if the
     // state == Initiated, None, or Unverified (before rebooting to the new
     // slot).
-    bool CancelUpdate();
+    virtual bool CancelUpdate() = 0;
 
     // Mark snapshot writes as having completed. After this, new snapshots cannot
     // be created, and the device must either cancel the OTA (either before
@@ -144,11 +120,11 @@
     // Before calling this function, all snapshots must be mapped.
     // If |wipe| is set to true, wipe is scheduled after reboot, and snapshots
     // may need to be merged before wiping.
-    bool FinishedSnapshotWrites(bool wipe);
+    virtual bool FinishedSnapshotWrites(bool wipe) = 0;
 
     // Initiate a merge on all snapshot devices. This should only be used after an
     // update has been marked successful after booting.
-    bool InitiateMerge();
+    virtual bool InitiateMerge() = 0;
 
     // Perform any necessary post-boot actions. This should be run soon after
     // /data is mounted.
@@ -178,8 +154,8 @@
     //
     // The optional callback allows the caller to periodically check the
     // progress with GetUpdateState().
-    UpdateState ProcessUpdateState(const std::function<bool()>& callback = {},
-                                   const std::function<bool()>& before_cancel = {});
+    virtual UpdateState ProcessUpdateState(const std::function<bool()>& callback = {},
+                                           const std::function<bool()>& before_cancel = {}) = 0;
 
     // Find the status of the current update, if any.
     //
@@ -187,28 +163,30 @@
     //   Merging: Value in the range [0, 100]
     //   MergeCompleted: 100
     //   Other: 0
-    UpdateState GetUpdateState(double* progress = nullptr);
+    virtual UpdateState GetUpdateState(double* progress = nullptr) = 0;
 
     // Create necessary COW device / files for OTA clients. New logical partitions will be added to
     // group "cow" in target_metadata. Regions of partitions of current_metadata will be
     // "write-protected" and snapshotted.
-    Return CreateUpdateSnapshots(const DeltaArchiveManifest& manifest);
+    virtual Return CreateUpdateSnapshots(
+            const chromeos_update_engine::DeltaArchiveManifest& manifest) = 0;
 
     // Map a snapshotted partition for OTA clients to write to. Write-protected regions are
     // determined previously in CreateSnapshots.
-    bool MapUpdateSnapshot(const CreateLogicalPartitionParams& params, std::string* snapshot_path);
+    virtual bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params,
+                                   std::string* snapshot_path) = 0;
 
     // Unmap a snapshot device that's previously mapped with MapUpdateSnapshot.
-    bool UnmapUpdateSnapshot(const std::string& target_partition_name);
+    virtual bool UnmapUpdateSnapshot(const std::string& target_partition_name) = 0;
 
     // If this returns true, first-stage mount must call
     // CreateLogicalAndSnapshotPartitions rather than CreateLogicalPartitions.
-    bool NeedSnapshotsInFirstStageMount();
+    virtual bool NeedSnapshotsInFirstStageMount() = 0;
 
     // Perform first-stage mapping of snapshot targets. This replaces init's
     // call to CreateLogicalPartitions when snapshots are present.
-    bool CreateLogicalAndSnapshotPartitions(const std::string& super_device,
-                                            const std::chrono::milliseconds& timeout_ms = {});
+    virtual bool CreateLogicalAndSnapshotPartitions(
+            const std::string& super_device, const std::chrono::milliseconds& timeout_ms = {}) = 0;
 
     // This method should be called preceding any wipe or flash of metadata or
     // userdata. It is only valid in recovery or fastbootd, and it ensures that
@@ -221,7 +199,7 @@
     //
     // Returns true on success (or nothing to do), false on failure. The
     // optional callback fires periodically to query progress via GetUpdateState.
-    bool HandleImminentDataWipe(const std::function<void()>& callback = {});
+    virtual bool HandleImminentDataWipe(const std::function<void()>& callback = {}) = 0;
 
     // This method is only allowed in recovery and is used as a helper to
     // initialize the snapshot devices as a requirement to mount a snapshotted
@@ -234,14 +212,15 @@
     // be aborted.
     // This function mounts /metadata when called, and unmounts /metadata upon
     // return.
-    CreateResult RecoveryCreateSnapshotDevices();
+    virtual CreateResult RecoveryCreateSnapshotDevices() = 0;
 
     // Same as RecoveryCreateSnapshotDevices(), but does not auto mount/umount
     // /metadata.
-    CreateResult RecoveryCreateSnapshotDevices(const std::unique_ptr<AutoDevice>& metadata_device);
+    virtual CreateResult RecoveryCreateSnapshotDevices(
+            const std::unique_ptr<AutoDevice>& metadata_device) = 0;
 
     // Dump debug information.
-    bool Dump(std::ostream& os);
+    virtual bool Dump(std::ostream& os) = 0;
 
     // Ensure metadata directory is mounted in recovery. When the returned
     // AutoDevice is destroyed, the metadata directory is automatically
@@ -257,7 +236,65 @@
     //   auto b = mgr->EnsureMetadataMounted(); // does nothing
     //   b.reset() // unmounts
     //   a.reset() // does nothing
-    std::unique_ptr<AutoDevice> EnsureMetadataMounted();
+    virtual std::unique_ptr<AutoDevice> EnsureMetadataMounted() = 0;
+
+    // Return the associated ISnapshotMergeStats instance. Never null.
+    virtual ISnapshotMergeStats* GetSnapshotMergeStatsInstance() = 0;
+};
+
+class SnapshotManager final : public ISnapshotManager {
+    using CreateLogicalPartitionParams = android::fs_mgr::CreateLogicalPartitionParams;
+    using IPartitionOpener = android::fs_mgr::IPartitionOpener;
+    using LpMetadata = android::fs_mgr::LpMetadata;
+    using MetadataBuilder = android::fs_mgr::MetadataBuilder;
+    using DeltaArchiveManifest = chromeos_update_engine::DeltaArchiveManifest;
+    using MergeStatus = android::hardware::boot::V1_1::MergeStatus;
+    using FiemapStatus = android::fiemap::FiemapStatus;
+
+    friend class SnapshotMergeStats;
+
+  public:
+    ~SnapshotManager();
+
+    // Return a new SnapshotManager instance, or null on error. The device
+    // pointer is owned for the lifetime of SnapshotManager. If null, a default
+    // instance will be created.
+    static std::unique_ptr<SnapshotManager> New(IDeviceInfo* device = nullptr);
+
+    // This is similar to New(), except designed specifically for first-stage
+    // init or recovery.
+    static std::unique_ptr<SnapshotManager> NewForFirstStageMount(IDeviceInfo* device = nullptr);
+
+    // Helper function for first-stage init to check whether a SnapshotManager
+    // might be needed to perform first-stage mounts.
+    static bool IsSnapshotManagerNeeded();
+
+    // Helper function for second stage init to restorecon on the rollback indicator.
+    static std::string GetGlobalRollbackIndicatorPath();
+
+    // ISnapshotManager overrides.
+    bool BeginUpdate() override;
+    bool CancelUpdate() override;
+    bool FinishedSnapshotWrites(bool wipe) override;
+    bool InitiateMerge() override;
+    UpdateState ProcessUpdateState(const std::function<bool()>& callback = {},
+                                   const std::function<bool()>& before_cancel = {}) override;
+    UpdateState GetUpdateState(double* progress = nullptr) override;
+    Return CreateUpdateSnapshots(const DeltaArchiveManifest& manifest) override;
+    bool MapUpdateSnapshot(const CreateLogicalPartitionParams& params,
+                           std::string* snapshot_path) override;
+    bool UnmapUpdateSnapshot(const std::string& target_partition_name) override;
+    bool NeedSnapshotsInFirstStageMount() override;
+    bool CreateLogicalAndSnapshotPartitions(
+            const std::string& super_device,
+            const std::chrono::milliseconds& timeout_ms = {}) override;
+    bool HandleImminentDataWipe(const std::function<void()>& callback = {}) override;
+    CreateResult RecoveryCreateSnapshotDevices() override;
+    CreateResult RecoveryCreateSnapshotDevices(
+            const std::unique_ptr<AutoDevice>& metadata_device) override;
+    bool Dump(std::ostream& os) override;
+    std::unique_ptr<AutoDevice> EnsureMetadataMounted() override;
+    ISnapshotMergeStats* GetSnapshotMergeStatsInstance() override;
 
   private:
     FRIEND_TEST(SnapshotTest, CleanFirstStageMount);
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
index 91dd34f..4caf632 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
@@ -23,14 +23,12 @@
 namespace android {
 namespace snapshot {
 
-class SnapshotMergeStats {
+class ISnapshotMergeStats {
   public:
-    // Not thread safe.
-    static SnapshotMergeStats* GetInstance(SnapshotManager& manager);
-
+    virtual ~ISnapshotMergeStats() = default;
     // Called when merge starts or resumes.
-    bool Start();
-    void set_state(android::snapshot::UpdateState state);
+    virtual bool Start() = 0;
+    virtual void set_state(android::snapshot::UpdateState state) = 0;
 
     // Called when merge ends. Properly clean up permanent storage.
     class Result {
@@ -40,7 +38,19 @@
         // Time between successful Start() / Resume() to Finish().
         virtual std::chrono::steady_clock::duration merge_time() const = 0;
     };
-    std::unique_ptr<Result> Finish();
+    // Return nullptr if any failure.
+    virtual std::unique_ptr<Result> Finish() = 0;
+};
+
+class SnapshotMergeStats : public ISnapshotMergeStats {
+  public:
+    // Not thread safe.
+    static SnapshotMergeStats* GetInstance(SnapshotManager& manager);
+
+    // ISnapshotMergeStats overrides
+    bool Start() override;
+    void set_state(android::snapshot::UpdateState state) override;
+    std::unique_ptr<Result> Finish() override;
 
   private:
     bool ReadState();
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
new file mode 100644
index 0000000..9b2590c
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
@@ -0,0 +1,52 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <libsnapshot/snapshot.h>
+
+namespace android::snapshot {
+
+class SnapshotManagerStub : public ISnapshotManager {
+  public:
+    // Create a stubbed snapshot manager. All calls into the stub fails.
+    static std::unique_ptr<ISnapshotManager> New();
+
+    // ISnapshotManager overrides.
+    bool BeginUpdate() override;
+    bool CancelUpdate() override;
+    bool FinishedSnapshotWrites(bool wipe) override;
+    bool InitiateMerge() override;
+    UpdateState ProcessUpdateState(const std::function<bool()>& callback = {},
+                                   const std::function<bool()>& before_cancel = {}) override;
+    UpdateState GetUpdateState(double* progress = nullptr) override;
+    Return CreateUpdateSnapshots(
+            const chromeos_update_engine::DeltaArchiveManifest& manifest) override;
+    bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params,
+                           std::string* snapshot_path) override;
+    bool UnmapUpdateSnapshot(const std::string& target_partition_name) override;
+    bool NeedSnapshotsInFirstStageMount() override;
+    bool CreateLogicalAndSnapshotPartitions(
+            const std::string& super_device,
+            const std::chrono::milliseconds& timeout_ms = {}) override;
+    bool HandleImminentDataWipe(const std::function<void()>& callback = {}) override;
+    CreateResult RecoveryCreateSnapshotDevices() override;
+    CreateResult RecoveryCreateSnapshotDevices(
+            const std::unique_ptr<AutoDevice>& metadata_device) override;
+    bool Dump(std::ostream& os) override;
+    std::unique_ptr<AutoDevice> EnsureMetadataMounted() override;
+    ISnapshotMergeStats* GetSnapshotMergeStatsInstance() override;
+};
+
+}  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index c9fa28e..eafeea2 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -2685,5 +2685,9 @@
     return true;
 }
 
+ISnapshotMergeStats* SnapshotManager::GetSnapshotMergeStatsInstance() {
+    return SnapshotMergeStats::GetInstance(*this);
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_stub.cpp b/fs_mgr/libsnapshot/snapshot_stub.cpp
new file mode 100644
index 0000000..2747b03
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_stub.cpp
@@ -0,0 +1,125 @@
+// Copyright (C) 2020 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 <libsnapshot/snapshot_stub.h>
+
+#include <android-base/logging.h>
+
+#include <libsnapshot/snapshot_stats.h>
+
+using android::fs_mgr::CreateLogicalPartitionParams;
+using chromeos_update_engine::DeltaArchiveManifest;
+
+namespace android::snapshot {
+
+std::unique_ptr<ISnapshotManager> SnapshotManagerStub::New() {
+    return std::make_unique<SnapshotManagerStub>();
+}
+
+bool SnapshotManagerStub::BeginUpdate() {
+    LOG(ERROR) << __FUNCTION__ << " should never be called.";
+    return false;
+}
+
+bool SnapshotManagerStub::CancelUpdate() {
+    LOG(ERROR) << __FUNCTION__ << " should never be called.";
+    return false;
+}
+
+bool SnapshotManagerStub::FinishedSnapshotWrites(bool) {
+    LOG(ERROR) << __FUNCTION__ << " should never be called.";
+    return false;
+}
+
+bool SnapshotManagerStub::InitiateMerge() {
+    LOG(ERROR) << __FUNCTION__ << " should never be called.";
+    return false;
+}
+
+UpdateState SnapshotManagerStub::ProcessUpdateState(const std::function<bool()>&,
+                                                    const std::function<bool()>&) {
+    LOG(ERROR) << __FUNCTION__ << " should never be called.";
+    return UpdateState::None;
+}
+
+UpdateState SnapshotManagerStub::GetUpdateState(double*) {
+    LOG(ERROR) << __FUNCTION__ << " should never be called.";
+    return UpdateState::None;
+}
+
+Return SnapshotManagerStub::CreateUpdateSnapshots(const DeltaArchiveManifest&) {
+    LOG(ERROR) << __FUNCTION__ << " should never be called.";
+    return Return::Error();
+}
+
+bool SnapshotManagerStub::MapUpdateSnapshot(const CreateLogicalPartitionParams&, std::string*) {
+    LOG(ERROR) << __FUNCTION__ << " should never be called.";
+    return false;
+}
+
+bool SnapshotManagerStub::UnmapUpdateSnapshot(const std::string&) {
+    LOG(ERROR) << __FUNCTION__ << " should never be called.";
+    return false;
+}
+
+bool SnapshotManagerStub::NeedSnapshotsInFirstStageMount() {
+    LOG(ERROR) << __FUNCTION__ << " should never be called.";
+    return false;
+}
+
+bool SnapshotManagerStub::CreateLogicalAndSnapshotPartitions(const std::string&,
+                                                             const std::chrono::milliseconds&) {
+    LOG(ERROR) << __FUNCTION__ << " should never be called.";
+    return false;
+}
+
+bool SnapshotManagerStub::HandleImminentDataWipe(const std::function<void()>&) {
+    LOG(ERROR) << __FUNCTION__ << " should never be called.";
+    return false;
+}
+
+CreateResult SnapshotManagerStub::RecoveryCreateSnapshotDevices() {
+    LOG(ERROR) << __FUNCTION__ << " should never be called.";
+    return CreateResult::ERROR;
+}
+
+CreateResult SnapshotManagerStub::RecoveryCreateSnapshotDevices(
+        const std::unique_ptr<AutoDevice>&) {
+    LOG(ERROR) << __FUNCTION__ << " should never be called.";
+    return CreateResult::ERROR;
+}
+
+bool SnapshotManagerStub::Dump(std::ostream&) {
+    LOG(ERROR) << __FUNCTION__ << " should never be called.";
+    return false;
+}
+
+std::unique_ptr<AutoDevice> SnapshotManagerStub::EnsureMetadataMounted() {
+    LOG(ERROR) << __FUNCTION__ << " should never be called.";
+    return nullptr;
+}
+
+class SnapshotMergeStatsStub : public ISnapshotMergeStats {
+    bool Start() override { return false; }
+    void set_state(android::snapshot::UpdateState) override {}
+    std::unique_ptr<Result> Finish() override { return nullptr; }
+};
+
+ISnapshotMergeStats* SnapshotManagerStub::GetSnapshotMergeStatsInstance() {
+    static SnapshotMergeStatsStub snapshot_merge_stats;
+    LOG(ERROR) << __FUNCTION__ << " should never be called.";
+    return &snapshot_merge_stats;
+}
+
+}  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index c662838..53a37be 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -41,6 +41,11 @@
 #include <libsnapshot/test_helpers.h>
 #include "utility.h"
 
+// Mock classes are not used. Header included to ensure mocked class definition aligns with the
+// class itself.
+#include <libsnapshot/mock_device_info.h>
+#include <libsnapshot/mock_snapshot.h>
+
 namespace android {
 namespace snapshot {
 
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index 0749fe3..f3b584c 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -240,14 +240,20 @@
     // slave to the /mnt/user mount, and at the same time /mnt/installer in the
     // bootstrap namespace shares a peer group with /mnt/installer in the
     // default namespace.
+    // /mnt/androidwritable is similar to /mnt/installer but serves for
+    // MOUNT_EXTERNAL_ANDROID_WRITABLE apps.
     if (!mkdir_recursive("/mnt/user", 0755)) return false;
     if (!mkdir_recursive("/mnt/installer", 0755)) return false;
+    if (!mkdir_recursive("/mnt/androidwritable", 0755)) return false;
     if (!(BindMount("/mnt/user", "/mnt/installer", true))) return false;
-    // First, make /mnt/installer a slave bind mount
+    if (!(BindMount("/mnt/user", "/mnt/androidwritable", true))) return false;
+    // First, make /mnt/installer and /mnt/androidwritable a slave bind mount
     if (!(MakeSlave("/mnt/installer"))) return false;
+    if (!(MakeSlave("/mnt/androidwritable"))) return false;
     // Then, make it shared again - effectively creating a new peer group, that
     // will be inherited by new mount namespaces.
     if (!(MakeShared("/mnt/installer"))) return false;
+    if (!(MakeShared("/mnt/androidwritable"))) return false;
 
     bootstrap_ns_fd.reset(OpenMountNamespace());
     bootstrap_ns_id = GetMountNamespaceId();
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 2f91663..72f0450 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -203,6 +203,7 @@
 }
 
 static Result<void> CallVdc(const std::string& system, const std::string& cmd) {
+    LOG(INFO) << "Calling /system/bin/vdc " << system << " " << cmd;
     const char* vdc_argv[] = {"/system/bin/vdc", system.c_str(), cmd.c_str()};
     int status;
     if (logwrap_fork_execvp(arraysize(vdc_argv), vdc_argv, &status, false, LOG_KLOG, true,
@@ -456,10 +457,14 @@
 #define ZRAM_RESET    "/sys/block/zram0/reset"
 #define ZRAM_BACK_DEV "/sys/block/zram0/backing_dev"
 static Result<void> KillZramBackingDevice() {
+    if (access(ZRAM_BACK_DEV, F_OK) != 0 && errno == ENOENT) {
+        LOG(INFO) << "No zram backing device configured";
+        return {};
+    }
     std::string backing_dev;
-    if (!android::base::ReadFileToString(ZRAM_BACK_DEV, &backing_dev)) return {};
-
-    if (!android::base::StartsWith(backing_dev, "/dev/block/loop")) return {};
+    if (!android::base::ReadFileToString(ZRAM_BACK_DEV, &backing_dev)) {
+        return ErrnoError() << "Failed to read " << ZRAM_BACK_DEV;
+    }
 
     // cut the last "\n"
     backing_dev.erase(backing_dev.length() - 1);
@@ -478,6 +483,11 @@
                        << " failed";
     }
 
+    if (!android::base::StartsWith(backing_dev, "/dev/block/loop")) {
+        LOG(INFO) << backing_dev << " is not a loop device. Exiting early";
+        return {};
+    }
+
     // clear loopback device
     unique_fd loop(TEMP_FAILURE_RETRY(open(backing_dev.c_str(), O_RDWR | O_CLOEXEC)));
     if (loop.get() < 0) {
@@ -785,7 +795,7 @@
     }
     auto sigterm_timeout = GetMillisProperty("init.userspace_reboot.sigterm.timeoutmillis", 5s);
     auto sigkill_timeout = GetMillisProperty("init.userspace_reboot.sigkill.timeoutmillis", 10s);
-    LOG(INFO) << "Timeout to terminate services : " << sigterm_timeout.count() << "ms"
+    LOG(INFO) << "Timeout to terminate services: " << sigterm_timeout.count() << "ms "
               << "Timeout to kill services: " << sigkill_timeout.count() << "ms";
     StopServicesAndLogViolations(stop_first, sigterm_timeout, true /* SIGTERM */);
     if (int r = StopServicesAndLogViolations(stop_first, sigkill_timeout, false /* SIGKILL */);
diff --git a/libcutils/arch-x86/android_memset16.S b/libcutils/arch-x86/android_memset16.S
index cb2ff14..f4d497e 100755
--- a/libcutils/arch-x86/android_memset16.S
+++ b/libcutils/arch-x86/android_memset16.S
@@ -105,14 +105,16 @@
     /* We loaded the jump table and adjuested EDX. Go.  */	\
     jmp		*%ebx
 
-	.section	.gnu.linkonce.t.__x86.get_pc_thunk.bx,"ax",@progbits
+	.section	.text.__x86.get_pc_thunk.bx,"axG",@progbits,__x86.get_pc_thunk.bx,comdat
 	.globl	__x86.get_pc_thunk.bx
 	.hidden	__x86.get_pc_thunk.bx
 	ALIGN (4)
 	.type	__x86.get_pc_thunk.bx,@function
 __x86.get_pc_thunk.bx:
+	cfi_startproc
 	movl	(%esp), %ebx
 	ret
+	cfi_endproc
 #else
 # define ENTRANCE
 # define RETURN_END	ret
diff --git a/libcutils/arch-x86/android_memset32.S b/libcutils/arch-x86/android_memset32.S
index f4326dc..b928f6b 100755
--- a/libcutils/arch-x86/android_memset32.S
+++ b/libcutils/arch-x86/android_memset32.S
@@ -105,14 +105,16 @@
     /* We loaded the jump table and adjuested EDX. Go.  */	\
     jmp		*%ebx
 
-	.section	.gnu.linkonce.t.__x86.get_pc_thunk.bx,"ax",@progbits
+	.section	.text.__x86.get_pc_thunk.bx,"axG",@progbits,__x86.get_pc_thunk.bx,comdat
 	.globl	__x86.get_pc_thunk.bx
 	.hidden	__x86.get_pc_thunk.bx
 	ALIGN (4)
 	.type	__x86.get_pc_thunk.bx,@function
 __x86.get_pc_thunk.bx:
+	cfi_startproc
 	movl	(%esp), %ebx
 	ret
+	cfi_endproc
 #else
 # define ENTRANCE
 # define RETURN_END	ret
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
index 512c7cd..8a0ebf2 100644
--- a/liblog/include/android/log.h
+++ b/liblog/include/android/log.h
@@ -185,14 +185,26 @@
  * and sending log messages to user defined loggers specified in __android_log_set_logger().
  */
 struct __android_log_message {
-  size_t
-      struct_size;   /** Must be set to sizeof(__android_log_message) and is used for versioning. */
-  int32_t buffer_id; /** {@link log_id_t} values. */
-  int32_t priority;  /** {@link android_LogPriority} values. */
-  const char* tag;   /** The tag for the log message. */
-  const char* file;  /** Optional file name, may be set to nullptr. */
-  uint32_t line;     /** Optional line number, ignore if file is nullptr. */
-  const char* message; /** The log message itself. */
+  /** Must be set to sizeof(__android_log_message) and is used for versioning. */
+  size_t struct_size;
+
+  /** {@link log_id_t} values. */
+  int32_t buffer_id;
+
+  /** {@link android_LogPriority} values. */
+  int32_t priority;
+
+  /** The tag for the log message. */
+  const char* tag;
+
+  /** Optional file name, may be set to nullptr. */
+  const char* file;
+
+  /** Optional line number, ignore if file is nullptr. */
+  uint32_t line;
+
+  /** The log message itself. */
+  const char* message;
 };
 
 /**
@@ -215,7 +227,7 @@
  * buffers, then pass the message to liblog via this function, and therefore we do not want to
  * duplicate the loggability check here.
  *
- * @param log_message the log message itself, see {@link __android_log_message}.
+ * @param log_message the log message itself, see __android_log_message.
  *
  * Available since API level 30.
  */
@@ -237,7 +249,7 @@
  * Writes the log message to logd.  This is an __android_logger_function and can be provided to
  * __android_log_set_logger().  It is the default logger when running liblog on a device.
  *
- * @param log_message the log message to write, see {@link __android_log_message}.
+ * @param log_message the log message to write, see __android_log_message.
  *
  * Available since API level 30.
  */
@@ -247,7 +259,7 @@
  * Writes the log message to stderr.  This is an __android_logger_function and can be provided to
  * __android_log_set_logger().  It is the default logger when running liblog on host.
  *
- * @param log_message the log message to write, see {@link __android_log_message}.
+ * @param log_message the log message to write, see __android_log_message.
  *
  * Available since API level 30.
  */
@@ -259,7 +271,7 @@
  * user defined aborter function is highly recommended to abort and be noreturn, but is not strictly
  * required to.
  *
- * @param aborter the new aborter function, see {@link __android_aborter_function}.
+ * @param aborter the new aborter function, see __android_aborter_function.
  *
  * Available since API level 30.
  */
@@ -297,7 +309,26 @@
  * minimum priority needed to log.  If only one is set, then that value is used to determine the
  * minimum priority needed.  If none are set, then default_priority is used.
  *
- * @param prio         the priority to test, takes {@link android_LogPriority} values.
+ * @param prio         the priority to test, takes android_LogPriority values.
+ * @param tag          the tag to test.
+ * @param default_prio the default priority to use if no properties or minimum priority are set.
+ * @return an integer where 1 indicates that the message is loggable and 0 indicates that it is not.
+ *
+ * Available since API level 30.
+ */
+int __android_log_is_loggable(int prio, const char* tag, int default_prio) __INTRODUCED_IN(30);
+
+/**
+ * Use the per-tag properties "log.tag.<tagname>" along with the minimum priority from
+ * __android_log_set_minimum_priority() to determine if a log message with a given prio and tag will
+ * be printed.  A non-zero result indicates yes, zero indicates false.
+ *
+ * If both a priority for a tag and a minimum priority are set by
+ * __android_log_set_minimum_priority(), then the lowest of the two values are to determine the
+ * minimum priority needed to log.  If only one is set, then that value is used to determine the
+ * minimum priority needed.  If none are set, then default_priority is used.
+ *
+ * @param prio         the priority to test, takes android_LogPriority values.
  * @param tag          the tag to test.
  * @param len          the length of the tag.
  * @param default_prio the default priority to use if no properties or minimum priority are set.
@@ -305,15 +336,14 @@
  *
  * Available since API level 30.
  */
-int __android_log_is_loggable(int prio, const char* tag, int default_prio) __INTRODUCED_IN(30);
 int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio)
     __INTRODUCED_IN(30);
 
 /**
  * Sets the minimum priority that will be logged for this process.
  *
- * @param priority the new minimum priority to set, takes @{link android_LogPriority} values.
- * @return the previous set minimum priority as @{link android_LogPriority} values, or
+ * @param priority the new minimum priority to set, takes android_LogPriority values.
+ * @return the previous set minimum priority as android_LogPriority values, or
  *         ANDROID_LOG_DEFAULT if none was set.
  *
  * Available since API level 30.
@@ -324,7 +354,7 @@
  * Gets the minimum priority that will be logged for this process.  If none has been set by a
  * previous __android_log_set_minimum_priority() call, this returns ANDROID_LOG_DEFAULT.
  *
- * @return the current minimum priority as @{link android_LogPriority} values, or
+ * @return the current minimum priority as android_LogPriority values, or
  *         ANDROID_LOG_DEFAULT if none is set.
  *
  * Available since API level 30.
diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h
index 7a144df..f27b8e6 100644
--- a/liblog/include/log/log.h
+++ b/liblog/include/log/log.h
@@ -93,8 +93,6 @@
 /*
  * Event log entry types.
  */
-#ifndef __AndroidEventLogType_defined
-#define __AndroidEventLogType_defined
 typedef enum {
   /* Special markers for android_log_list_element type */
   EVENT_TYPE_LIST_STOP = '\n', /* declare end of list  */
@@ -107,9 +105,6 @@
   EVENT_TYPE_LIST = 3,
   EVENT_TYPE_FLOAT = 4,
 } AndroidEventLogType;
-#endif
-#define sizeof_AndroidEventLogType sizeof(typeof_AndroidEventLogType)
-#define typeof_AndroidEventLogType unsigned char
 
 #ifndef LOG_EVENT_INT
 #define LOG_EVENT_INT(_tag, _value)                                          \
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
index d15b367..3a75fa3 100644
--- a/liblog/logger_write.cpp
+++ b/liblog/logger_write.cpp
@@ -28,7 +28,6 @@
 #endif
 
 #include <atomic>
-#include <shared_mutex>
 
 #include <android-base/errno_restorer.h>
 #include <android-base/macros.h>
@@ -38,7 +37,6 @@
 #include "android/log.h"
 #include "log/log_read.h"
 #include "logger.h"
-#include "rwlock.h"
 #include "uio.h"
 
 #ifdef __ANDROID__
@@ -142,10 +140,8 @@
   static std::string default_tag = getprogname();
   return default_tag;
 }
-RwLock default_tag_lock;
 
 void __android_log_set_default_tag(const char* tag) {
-  auto lock = std::unique_lock{default_tag_lock};
   GetDefaultTag().assign(tag, 0, LOGGER_ENTRY_MAX_PAYLOAD);
 }
 
@@ -163,10 +159,8 @@
 #else
 static __android_logger_function logger_function = __android_log_stderr_logger;
 #endif
-static RwLock logger_function_lock;
 
 void __android_log_set_logger(__android_logger_function logger) {
-  auto lock = std::unique_lock{logger_function_lock};
   logger_function = logger;
 }
 
@@ -180,15 +174,12 @@
 }
 
 static __android_aborter_function aborter_function = __android_log_default_aborter;
-static RwLock aborter_function_lock;
 
 void __android_log_set_aborter(__android_aborter_function aborter) {
-  auto lock = std::unique_lock{aborter_function_lock};
   aborter_function = aborter;
 }
 
 void __android_log_call_aborter(const char* abort_message) {
-  auto lock = std::shared_lock{aborter_function_lock};
   aborter_function(abort_message);
 }
 
@@ -310,9 +301,7 @@
     return;
   }
 
-  auto tag_lock = std::shared_lock{default_tag_lock, std::defer_lock};
   if (log_message->tag == nullptr) {
-    tag_lock.lock();
     log_message->tag = GetDefaultTag().c_str();
   }
 
@@ -322,7 +311,6 @@
   }
 #endif
 
-  auto lock = std::shared_lock{logger_function_lock};
   logger_function(log_message);
 }
 
diff --git a/liblog/logger_write.h b/liblog/logger_write.h
index 065fd55..eee2778 100644
--- a/liblog/logger_write.h
+++ b/liblog/logger_write.h
@@ -18,7 +18,4 @@
 
 #include <string>
 
-#include "rwlock.h"
-
-std::string& GetDefaultTag();  // Must read lock default_tag_lock
-extern RwLock default_tag_lock;
\ No newline at end of file
+std::string& GetDefaultTag();
diff --git a/liblog/properties.cpp b/liblog/properties.cpp
index abd48fc..37670ec 100644
--- a/liblog/properties.cpp
+++ b/liblog/properties.cpp
@@ -23,7 +23,6 @@
 #include <unistd.h>
 
 #include <algorithm>
-#include <shared_mutex>
 
 #include <private/android_logger.h>
 
@@ -99,9 +98,7 @@
   static const char log_namespace[] = "persist.log.tag.";
   static const size_t base_offset = 8; /* skip "persist." */
 
-  auto tag_lock = std::shared_lock{default_tag_lock, std::defer_lock};
   if (tag == nullptr || len == 0) {
-    tag_lock.lock();
     auto& tag_string = GetDefaultTag();
     tag = tag_string.c_str();
     len = tag_string.size();
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index ab59a4b..bf7d69e 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -57,6 +57,7 @@
         "MapInfo.cpp",
         "Maps.cpp",
         "Memory.cpp",
+        "MemoryMte.cpp",
         "LocalUnwinder.cpp",
         "Regs.cpp",
         "RegsArm.cpp",
@@ -101,6 +102,16 @@
         "liblog",
         "liblzma",
     ],
+
+    header_libs: [
+        "bionic_libc_platform_headers",
+    ],
+
+    product_variables: {
+        experimental_mte: {
+            cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+        },
+    },
 }
 
 cc_library {
@@ -213,6 +224,7 @@
         "tests/MemoryRangesTest.cpp",
         "tests/MemoryRemoteTest.cpp",
         "tests/MemoryTest.cpp",
+        "tests/MemoryMteTest.cpp",
         "tests/RegsInfoTest.cpp",
         "tests/RegsIterateTest.cpp",
         "tests/RegsStepIfSignalHandlerTest.cpp",
@@ -268,6 +280,16 @@
         "tests/files/offline/straddle_arm/*",
         "tests/files/offline/straddle_arm64/*",
     ],
+
+    header_libs: [
+        "bionic_libc_platform_headers",
+    ],
+
+    product_variables: {
+        experimental_mte: {
+            cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+        },
+    },
 }
 
 cc_test {
@@ -373,7 +395,9 @@
 
     srcs: [
         "benchmarks/unwind_benchmarks.cpp",
+        "benchmarks/ElfBenchmark.cpp",
         "benchmarks/SymbolBenchmark.cpp",
+        "benchmarks/Utils.cpp",
     ],
 
     data: [
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 821e042..17470fd 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -371,7 +371,7 @@
       // Look for the .debug_frame and .gnu_debugdata.
       if (shdr.sh_name < sec_size) {
         std::string name;
-        if (memory_->ReadString(sec_offset + shdr.sh_name, &name)) {
+        if (memory_->ReadString(sec_offset + shdr.sh_name, &name, sec_size - shdr.sh_name)) {
           if (name == ".debug_frame") {
             debug_frame_offset_ = shdr.sh_offset;
             debug_frame_size_ = shdr.sh_size;
@@ -405,7 +405,7 @@
     } else if (shdr.sh_type == SHT_NOTE) {
       if (shdr.sh_name < sec_size) {
         std::string name;
-        if (memory_->ReadString(sec_offset + shdr.sh_name, &name) &&
+        if (memory_->ReadString(sec_offset + shdr.sh_name, &name, sec_size - shdr.sh_name) &&
             name == ".note.gnu.build-id") {
           gnu_build_id_offset_ = shdr.sh_offset;
           gnu_build_id_size_ = shdr.sh_size;
@@ -456,10 +456,11 @@
   for (const auto& entry : strtabs_) {
     if (entry.first == strtab_addr) {
       soname_offset = entry.second + soname_offset;
-      if (soname_offset >= entry.second + strtab_size) {
+      uint64_t soname_max = entry.second + strtab_size;
+      if (soname_offset >= soname_max) {
         return "";
       }
-      if (!memory_->ReadString(soname_offset, &soname_)) {
+      if (!memory_->ReadString(soname_offset, &soname_, soname_max - soname_offset)) {
         return "";
       }
       soname_type_ = SONAME_VALID;
@@ -608,7 +609,8 @@
     }
     std::string name;
     if (shdr.sh_type == SHT_NOTE && shdr.sh_name < sec_size &&
-        memory->ReadString(sec_offset + shdr.sh_name, &name) && name == ".note.gnu.build-id") {
+        memory->ReadString(sec_offset + shdr.sh_name, &name, sec_size - shdr.sh_name) &&
+        name == ".note.gnu.build-id") {
       *build_id_offset = shdr.sh_offset;
       *build_id_size = shdr.sh_size;
       return true;
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index 8de3d98..b4623fa 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -158,20 +158,30 @@
   return rc == size;
 }
 
-bool Memory::ReadString(uint64_t addr, std::string* string, uint64_t max_read) {
-  string->clear();
-  uint64_t bytes_read = 0;
-  while (bytes_read < max_read) {
-    uint8_t value;
-    if (!ReadFully(addr, &value, sizeof(value))) {
-      return false;
+bool Memory::ReadString(uint64_t addr, std::string* dst, size_t max_read) {
+  char buffer[256];  // Large enough for 99% of symbol names.
+  size_t size = 0;   // Number of bytes which were read into the buffer.
+  for (size_t offset = 0; offset < max_read; offset += size) {
+    // Look for null-terminator first, so we can allocate string of exact size.
+    // If we know the end of valid memory range, do the reads in larger blocks.
+    size_t read = std::min(sizeof(buffer), max_read - offset);
+    size = Read(addr + offset, buffer, read);
+    if (size == 0) {
+      return false;  // We have not found end of string yet and we can not read more data.
     }
-    if (value == '\0') {
-      return true;
+    size_t length = strnlen(buffer, size);  // Index of the null-terminator.
+    if (length < size) {
+      // We found the null-terminator. Allocate the string and set its content.
+      if (offset == 0) {
+        // We did just single read, so the buffer already contains the whole string.
+        dst->assign(buffer, length);
+        return true;
+      } else {
+        // The buffer contains only the last block. Read the whole string again.
+        dst->assign(offset + length, '\0');
+        return ReadFully(addr, dst->data(), dst->size());
+      }
     }
-    string->push_back(value);
-    addr++;
-    bytes_read++;
   }
   return false;
 }
@@ -324,6 +334,16 @@
   return ProcessVmRead(getpid(), addr, dst, size);
 }
 
+#if !defined(ANDROID_EXPERIMENTAL_MTE)
+long MemoryRemote::ReadTag(uint64_t) {
+  return -1;
+}
+
+long MemoryLocal::ReadTag(uint64_t) {
+  return -1;
+}
+#endif
+
 MemoryRange::MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t length,
                          uint64_t offset)
     : memory_(memory), begin_(begin), length_(length), offset_(offset) {}
diff --git a/libunwindstack/MemoryCache.h b/libunwindstack/MemoryCache.h
index 769d907..d97640d 100644
--- a/libunwindstack/MemoryCache.h
+++ b/libunwindstack/MemoryCache.h
@@ -33,6 +33,7 @@
   virtual ~MemoryCache() = default;
 
   size_t Read(uint64_t addr, void* dst, size_t size) override;
+  long ReadTag(uint64_t addr) override { return impl_->ReadTag(addr); }
 
   void Clear() override { cache_.clear(); }
 
diff --git a/libunwindstack/MemoryLocal.h b/libunwindstack/MemoryLocal.h
index 7e027cf..741f107 100644
--- a/libunwindstack/MemoryLocal.h
+++ b/libunwindstack/MemoryLocal.h
@@ -31,6 +31,7 @@
   bool IsLocal() const override { return true; }
 
   size_t Read(uint64_t addr, void* dst, size_t size) override;
+  long ReadTag(uint64_t addr) override;
 };
 
 }  // namespace unwindstack
diff --git a/libunwindstack/MemoryMte.cpp b/libunwindstack/MemoryMte.cpp
new file mode 100644
index 0000000..d1d0ebc
--- /dev/null
+++ b/libunwindstack/MemoryMte.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#if defined(ANDROID_EXPERIMENTAL_MTE)
+
+#include <sys/ptrace.h>
+
+#include <bionic/mte.h>
+#include <bionic/mte_kernel.h>
+
+#include "MemoryLocal.h"
+#include "MemoryRemote.h"
+
+namespace unwindstack {
+
+long MemoryRemote::ReadTag(uint64_t addr) {
+#if defined(__aarch64__)
+  return ptrace(PTRACE_PEEKTAG, pid_, (void*)addr, nullptr);
+#else
+  (void)addr;
+  return -1;
+#endif
+}
+
+long MemoryLocal::ReadTag(uint64_t addr) {
+#if defined(__aarch64__)
+  // Check that the memory is readable first. This is racy with the ldg but there's not much
+  // we can do about it.
+  char data;
+  if (!mte_supported() || !Read(addr, &data, 1)) {
+    return -1;
+  }
+
+  __asm__ __volatile__(".arch_extension mte; ldg %0, [%0]" : "+r"(addr) : : "memory");
+  return (addr >> 56) & 0xf;
+#else
+  (void)addr;
+  return -1;
+#endif
+}
+
+}  // namespace unwindstack
+
+#endif
diff --git a/libunwindstack/MemoryRemote.h b/libunwindstack/MemoryRemote.h
index db367d6..dd09c88 100644
--- a/libunwindstack/MemoryRemote.h
+++ b/libunwindstack/MemoryRemote.h
@@ -32,6 +32,7 @@
   virtual ~MemoryRemote() = default;
 
   size_t Read(uint64_t addr, void* dst, size_t size) override;
+  long ReadTag(uint64_t addr) override;
 
   pid_t pid() { return pid_; }
 
diff --git a/libunwindstack/Symbols.cpp b/libunwindstack/Symbols.cpp
index e3c15a2..2117ebd 100644
--- a/libunwindstack/Symbols.cpp
+++ b/libunwindstack/Symbols.cpp
@@ -19,6 +19,7 @@
 
 #include <algorithm>
 #include <string>
+#include <vector>
 
 #include <unwindstack/Memory.h>
 
@@ -29,23 +30,55 @@
 
 Symbols::Symbols(uint64_t offset, uint64_t size, uint64_t entry_size, uint64_t str_offset,
                  uint64_t str_size)
-    : cur_offset_(offset),
-      offset_(offset),
-      end_(offset + size),
+    : offset_(offset),
+      count_(entry_size != 0 ? size / entry_size : 0),
       entry_size_(entry_size),
       str_offset_(str_offset),
       str_end_(str_offset_ + str_size) {}
 
-const Symbols::Info* Symbols::GetInfoFromCache(uint64_t addr) {
-  // Binary search the table.
+template <typename SymType>
+static bool IsFunc(const SymType* entry) {
+  return entry->st_shndx != SHN_UNDEF && ELF32_ST_TYPE(entry->st_info) == STT_FUNC;
+}
+
+// Read symbol entry from memory and cache it so we don't have to read it again.
+template <typename SymType>
+inline __attribute__((__always_inline__)) const Symbols::Info* Symbols::ReadFuncInfo(
+    uint32_t symbol_index, Memory* elf_memory) {
+  auto it = symbols_.find(symbol_index);
+  if (it != symbols_.end()) {
+    return &it->second;
+  }
+  SymType sym;
+  if (!elf_memory->ReadFully(offset_ + symbol_index * entry_size_, &sym, sizeof(sym))) {
+    return nullptr;
+  }
+  if (!IsFunc(&sym)) {
+    // We need the address for binary search, but we don't want it to be matched.
+    sym.st_size = 0;
+  }
+  Info info{.addr = sym.st_value, .size = static_cast<uint32_t>(sym.st_size), .name = sym.st_name};
+  return &symbols_.emplace(symbol_index, info).first->second;
+}
+
+// Binary search the symbol table to find function containing the given address.
+// Without remap, the symbol table is assumed to be sorted and accessed directly.
+// If the symbol table is not sorted this method might fail but should not crash.
+// When the indices are remapped, they are guaranteed to be sorted by address.
+template <typename SymType, bool RemapIndices>
+const Symbols::Info* Symbols::BinarySearch(uint64_t addr, Memory* elf_memory) {
   size_t first = 0;
-  size_t last = symbols_.size();
+  size_t last = RemapIndices ? remap_->size() : count_;
   while (first < last) {
     size_t current = first + (last - first) / 2;
-    const Info* info = &symbols_[current];
-    if (addr < info->start_offset) {
+    size_t symbol_index = RemapIndices ? remap_.value()[current] : current;
+    const Info* info = ReadFuncInfo<SymType>(symbol_index, elf_memory);
+    if (info == nullptr) {
+      return nullptr;
+    }
+    if (addr < info->addr) {
       last = current;
-    } else if (addr < info->end_offset) {
+    } else if (addr < info->addr + info->size) {
       return info;
     } else {
       first = current + 1;
@@ -54,64 +87,72 @@
   return nullptr;
 }
 
+// Create remapping table which allows us to access symbols as if they were sorted by address.
 template <typename SymType>
-bool Symbols::GetName(uint64_t addr, Memory* elf_memory, std::string* name, uint64_t* func_offset) {
-  if (symbols_.size() != 0) {
-    const Info* info = GetInfoFromCache(addr);
-    if (info) {
-      CHECK(addr >= info->start_offset && addr <= info->end_offset);
-      *func_offset = addr - info->start_offset;
-      return elf_memory->ReadString(info->str_offset, name, str_end_ - info->str_offset);
+void Symbols::BuildRemapTable(Memory* elf_memory) {
+  std::vector<uint64_t> addrs;  // Addresses of all symbols (addrs[i] == symbols[i].st_value).
+  addrs.reserve(count_);
+  remap_.emplace();  // Construct the optional remap table.
+  remap_->reserve(count_);
+  for (size_t symbol_idx = 0; symbol_idx < count_;) {
+    // Read symbols from memory.  We intentionally bypass the cache to save memory.
+    // Do the reads in batches so that we minimize the number of memory read calls.
+    uint8_t buffer[1024];
+    size_t read = std::min<size_t>(sizeof(buffer), (count_ - symbol_idx) * entry_size_);
+    size_t size = elf_memory->Read(offset_ + symbol_idx * entry_size_, buffer, read);
+    if (size < sizeof(SymType)) {
+      break;  // Stop processing, something looks like it is corrupted.
     }
-  }
-
-  bool symbol_added = false;
-  bool return_value = false;
-  while (cur_offset_ + entry_size_ <= end_) {
-    SymType entry;
-    if (!elf_memory->ReadFully(cur_offset_, &entry, sizeof(entry))) {
-      // Stop all processing, something looks like it is corrupted.
-      cur_offset_ = UINT64_MAX;
-      return false;
-    }
-    cur_offset_ += entry_size_;
-
-    if (entry.st_shndx != SHN_UNDEF && ELF32_ST_TYPE(entry.st_info) == STT_FUNC) {
-      // Treat st_value as virtual address.
-      uint64_t start_offset = entry.st_value;
-      uint64_t end_offset = start_offset + entry.st_size;
-
-      // Cache the value.
-      symbols_.emplace_back(start_offset, end_offset, str_offset_ + entry.st_name);
-      symbol_added = true;
-
-      if (addr >= start_offset && addr < end_offset) {
-        *func_offset = addr - start_offset;
-        uint64_t offset = str_offset_ + entry.st_name;
-        if (offset < str_end_) {
-          return_value = elf_memory->ReadString(offset, name, str_end_ - offset);
-        }
-        break;
+    for (size_t offset = 0; offset + sizeof(SymType) <= size; offset += entry_size_, symbol_idx++) {
+      SymType sym;
+      memcpy(&sym, &buffer[offset], sizeof(SymType));  // Copy to ensure alignment.
+      addrs.push_back(sym.st_value);  // Always insert so it is indexable by symbol index.
+      if (IsFunc(&sym)) {
+        remap_->push_back(symbol_idx);  // Indices of function symbols only.
       }
     }
   }
+  // Sort by address to make the remap list binary searchable (stable due to the a<b tie break).
+  auto comp = [&addrs](auto a, auto b) { return std::tie(addrs[a], a) < std::tie(addrs[b], b); };
+  std::sort(remap_->begin(), remap_->end(), comp);
+  // Remove duplicate entries (methods de-duplicated by the linker).
+  auto pred = [&addrs](auto a, auto b) { return addrs[a] == addrs[b]; };
+  remap_->erase(std::unique(remap_->begin(), remap_->end(), pred), remap_->end());
+  remap_->shrink_to_fit();
+}
 
-  if (symbol_added) {
-    std::sort(symbols_.begin(), symbols_.end(),
-              [](const Info& a, const Info& b) { return a.start_offset < b.start_offset; });
+template <typename SymType>
+bool Symbols::GetName(uint64_t addr, Memory* elf_memory, std::string* name, uint64_t* func_offset) {
+  const Info* info;
+  if (!remap_.has_value()) {
+    // Assume the symbol table is sorted. If it is not, this will gracefully fail.
+    info = BinarySearch<SymType, false>(addr, elf_memory);
+    if (info == nullptr) {
+      // Create the remapping table and retry the search.
+      BuildRemapTable<SymType>(elf_memory);
+      symbols_.clear();  // Remove cached symbols since the access pattern will be different.
+      info = BinarySearch<SymType, true>(addr, elf_memory);
+    }
+  } else {
+    // Fast search using the previously created remap table.
+    info = BinarySearch<SymType, true>(addr, elf_memory);
   }
-  return return_value;
+  if (info == nullptr) {
+    return false;
+  }
+  // Read the function name from the string table.
+  *func_offset = addr - info->addr;
+  uint64_t str = str_offset_ + info->name;
+  return str < str_end_ && elf_memory->ReadString(str, name, str_end_ - str);
 }
 
 template <typename SymType>
 bool Symbols::GetGlobal(Memory* elf_memory, const std::string& name, uint64_t* memory_address) {
-  uint64_t cur_offset = offset_;
-  while (cur_offset + entry_size_ <= end_) {
+  for (uint32_t i = 0; i < count_; i++) {
     SymType entry;
-    if (!elf_memory->ReadFully(cur_offset, &entry, sizeof(entry))) {
+    if (!elf_memory->ReadFully(offset_ + i * entry_size_, &entry, sizeof(entry))) {
       return false;
     }
-    cur_offset += entry_size_;
 
     if (entry.st_shndx != SHN_UNDEF && ELF32_ST_TYPE(entry.st_info) == STT_OBJECT &&
         ELF32_ST_BIND(entry.st_info) == STB_GLOBAL) {
diff --git a/libunwindstack/Symbols.h b/libunwindstack/Symbols.h
index 7fcd067..3b3f20b 100644
--- a/libunwindstack/Symbols.h
+++ b/libunwindstack/Symbols.h
@@ -19,8 +19,9 @@
 
 #include <stdint.h>
 
+#include <optional>
 #include <string>
-#include <vector>
+#include <unordered_map>
 
 namespace unwindstack {
 
@@ -29,11 +30,9 @@
 
 class Symbols {
   struct Info {
-    Info(uint64_t start_offset, uint64_t end_offset, uint64_t str_offset)
-        : start_offset(start_offset), end_offset(end_offset), str_offset(str_offset) {}
-    uint64_t start_offset;
-    uint64_t end_offset;
-    uint64_t str_offset;
+    uint64_t addr;  // Symbol address.
+    uint32_t size;  // Symbol size in bytes. Zero if not a function.
+    uint32_t name;  // Offset in .strtab.
   };
 
  public:
@@ -41,8 +40,6 @@
           uint64_t str_size);
   virtual ~Symbols() = default;
 
-  const Info* GetInfoFromCache(uint64_t addr);
-
   template <typename SymType>
   bool GetName(uint64_t addr, Memory* elf_memory, std::string* name, uint64_t* func_offset);
 
@@ -51,18 +48,27 @@
 
   void ClearCache() {
     symbols_.clear();
-    cur_offset_ = offset_;
+    remap_.reset();
   }
 
  private:
-  uint64_t cur_offset_;
-  uint64_t offset_;
-  uint64_t end_;
-  uint64_t entry_size_;
-  uint64_t str_offset_;
-  uint64_t str_end_;
+  template <typename SymType>
+  const Info* ReadFuncInfo(uint32_t symbol_index, Memory* elf_memory);
 
-  std::vector<Info> symbols_;
+  template <typename SymType, bool RemapIndices>
+  const Info* BinarySearch(uint64_t addr, Memory* elf_memory);
+
+  template <typename SymType>
+  void BuildRemapTable(Memory* elf_memory);
+
+  const uint64_t offset_;
+  const uint64_t count_;
+  const uint64_t entry_size_;
+  const uint64_t str_offset_;
+  const uint64_t str_end_;
+
+  std::unordered_map<uint32_t, Info> symbols_;  // Cache of read symbols (keyed by symbol index).
+  std::optional<std::vector<uint32_t>> remap_;  // Indices of function symbols sorted by address.
 };
 
 }  // namespace unwindstack
diff --git a/libunwindstack/benchmarks/ElfBenchmark.cpp b/libunwindstack/benchmarks/ElfBenchmark.cpp
new file mode 100644
index 0000000..c108a2a
--- /dev/null
+++ b/libunwindstack/benchmarks/ElfBenchmark.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 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 <err.h>
+#include <malloc.h>
+#include <stdint.h>
+
+#include <string>
+
+#include <benchmark/benchmark.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Memory.h>
+
+#include "Utils.h"
+
+static void BenchmarkElfCreate(benchmark::State& state, const std::string& elf_file) {
+#if defined(__BIONIC__)
+  uint64_t rss_bytes = 0;
+#endif
+  uint64_t alloc_bytes = 0;
+  for (auto _ : state) {
+    state.PauseTiming();
+#if defined(__BIONIC__)
+    mallopt(M_PURGE, 0);
+    uint64_t rss_bytes_before = 0;
+    GatherRss(&rss_bytes_before);
+#endif
+    uint64_t alloc_bytes_before = mallinfo().uordblks;
+    auto file_memory = unwindstack::Memory::CreateFileMemory(elf_file, 0);
+    state.ResumeTiming();
+
+    unwindstack::Elf elf(file_memory.release());
+    if (!elf.Init() || !elf.valid()) {
+      errx(1, "Internal Error: Cannot open elf.");
+    }
+
+    state.PauseTiming();
+#if defined(__BIONIC__)
+    mallopt(M_PURGE, 0);
+#endif
+    alloc_bytes += mallinfo().uordblks - alloc_bytes_before;
+#if defined(__BIONIC__)
+    GatherRss(&rss_bytes);
+    rss_bytes -= rss_bytes_before;
+#endif
+    state.ResumeTiming();
+  }
+
+#if defined(__BIONIC__)
+  state.counters["RSS_BYTES"] = rss_bytes / static_cast<double>(state.iterations());
+#endif
+  state.counters["ALLOCATED_BYTES"] = alloc_bytes / static_cast<double>(state.iterations());
+}
+
+void BM_elf_create(benchmark::State& state) {
+  BenchmarkElfCreate(state, GetElfFile());
+}
+BENCHMARK(BM_elf_create);
+
+void BM_elf_create_compressed(benchmark::State& state) {
+  BenchmarkElfCreate(state, GetCompressedElfFile());
+}
+BENCHMARK(BM_elf_create_compressed);
diff --git a/libunwindstack/benchmarks/SymbolBenchmark.cpp b/libunwindstack/benchmarks/SymbolBenchmark.cpp
index a850ff0..73088da 100644
--- a/libunwindstack/benchmarks/SymbolBenchmark.cpp
+++ b/libunwindstack/benchmarks/SymbolBenchmark.cpp
@@ -22,33 +22,12 @@
 #include <string>
 #include <vector>
 
-#include <android-base/file.h>
-#include <android-base/strings.h>
 #include <benchmark/benchmark.h>
 
 #include <unwindstack/Elf.h>
 #include <unwindstack/Memory.h>
 
-#if defined(__BIONIC__)
-
-#include <meminfo/procmeminfo.h>
-#include <procinfo/process_map.h>
-
-static void Gather(uint64_t* rss_bytes) {
-  android::meminfo::ProcMemInfo proc_mem(getpid());
-  const std::vector<android::meminfo::Vma>& maps = proc_mem.MapsWithoutUsageStats();
-  for (auto& vma : maps) {
-    if (vma.name == "[anon:libc_malloc]" || android::base::StartsWith(vma.name, "[anon:scudo:") ||
-        android::base::StartsWith(vma.name, "[anon:GWP-ASan")) {
-      android::meminfo::Vma update_vma(vma);
-      if (!proc_mem.FillInVmaStats(update_vma)) {
-        err(1, "FillInVmaStats failed\n");
-      }
-      *rss_bytes += update_vma.usage.rss;
-    }
-  }
-}
-#endif
+#include "Utils.h"
 
 static void BenchmarkSymbolLookup(benchmark::State& state, std::vector<uint64_t> offsets,
                                   std::string elf_file, bool expect_found) {
@@ -66,7 +45,7 @@
 #if defined(__BIONIC__)
     mallopt(M_PURGE, 0);
     uint64_t rss_bytes_before = 0;
-    Gather(&rss_bytes_before);
+    GatherRss(&rss_bytes_before);
 #endif
     uint64_t alloc_bytes_before = mallinfo().uordblks;
     state.ResumeTiming();
@@ -88,7 +67,7 @@
 #endif
     alloc_bytes += mallinfo().uordblks - alloc_bytes_before;
 #if defined(__BIONIC__)
-    Gather(&rss_bytes);
+    GatherRss(&rss_bytes);
     rss_bytes -= rss_bytes_before;
 #endif
     state.ResumeTiming();
@@ -105,14 +84,6 @@
   BenchmarkSymbolLookup(state, std::vector<uint64_t>{pc}, elf_file, expect_found);
 }
 
-std::string GetElfFile() {
-  return android::base::GetExecutableDirectory() + "/benchmarks/files/libart_arm.so";
-}
-
-std::string GetSortedElfFile() {
-  return android::base::GetExecutableDirectory() + "/benchmarks/files/boot_arm.oat";
-}
-
 void BM_symbol_not_present(benchmark::State& state) {
   BenchmarkSymbolLookup(state, 0, GetElfFile(), false);
 }
@@ -136,23 +107,23 @@
 BENCHMARK(BM_symbol_find_multiple);
 
 void BM_symbol_not_present_from_sorted(benchmark::State& state) {
-  BenchmarkSymbolLookup(state, 0, GetSortedElfFile(), false);
+  BenchmarkSymbolLookup(state, 0, GetSymbolSortedElfFile(), false);
 }
 BENCHMARK(BM_symbol_not_present_from_sorted);
 
 void BM_symbol_find_single_from_sorted(benchmark::State& state) {
-  BenchmarkSymbolLookup(state, 0x138638, GetSortedElfFile(), true);
+  BenchmarkSymbolLookup(state, 0x138638, GetSymbolSortedElfFile(), true);
 }
 BENCHMARK(BM_symbol_find_single_from_sorted);
 
 void BM_symbol_find_single_many_times_from_sorted(benchmark::State& state) {
-  BenchmarkSymbolLookup(state, std::vector<uint64_t>(15, 0x138638), GetSortedElfFile(), true);
+  BenchmarkSymbolLookup(state, std::vector<uint64_t>(15, 0x138638), GetSymbolSortedElfFile(), true);
 }
 BENCHMARK(BM_symbol_find_single_many_times_from_sorted);
 
 void BM_symbol_find_multiple_from_sorted(benchmark::State& state) {
   BenchmarkSymbolLookup(state,
                         std::vector<uint64_t>{0x138638, 0x84350, 0x14df18, 0x1f3a38, 0x1f3ca8},
-                        GetSortedElfFile(), true);
+                        GetSymbolSortedElfFile(), true);
 }
 BENCHMARK(BM_symbol_find_multiple_from_sorted);
diff --git a/libunwindstack/benchmarks/Utils.cpp b/libunwindstack/benchmarks/Utils.cpp
new file mode 100644
index 0000000..c92f109
--- /dev/null
+++ b/libunwindstack/benchmarks/Utils.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 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 <err.h>
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <benchmark/benchmark.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Memory.h>
+
+std::string GetElfFile() {
+  return android::base::GetExecutableDirectory() + "/benchmarks/files/libart_arm.so";
+}
+
+std::string GetSymbolSortedElfFile() {
+  return android::base::GetExecutableDirectory() + "/benchmarks/files/boot_arm.oat";
+}
+
+std::string GetCompressedElfFile() {
+  // Both are the same right now.
+  return GetSymbolSortedElfFile();
+}
+
+#if defined(__BIONIC__)
+
+#include <meminfo/procmeminfo.h>
+#include <procinfo/process_map.h>
+
+void GatherRss(uint64_t* rss_bytes) {
+  android::meminfo::ProcMemInfo proc_mem(getpid());
+  const std::vector<android::meminfo::Vma>& maps = proc_mem.MapsWithoutUsageStats();
+  for (auto& vma : maps) {
+    if (vma.name == "[anon:libc_malloc]" || android::base::StartsWith(vma.name, "[anon:scudo:") ||
+        android::base::StartsWith(vma.name, "[anon:GWP-ASan")) {
+      android::meminfo::Vma update_vma(vma);
+      if (!proc_mem.FillInVmaStats(update_vma)) {
+        err(1, "FillInVmaStats failed\n");
+      }
+      *rss_bytes += update_vma.usage.rss;
+    }
+  }
+}
+#endif
diff --git a/libunwindstack/benchmarks/Utils.h b/libunwindstack/benchmarks/Utils.h
new file mode 100644
index 0000000..bee6efc
--- /dev/null
+++ b/libunwindstack/benchmarks/Utils.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_UTILS_H
+#define _LIBUNWINDSTACK_UTILS_H
+
+#include <stdint.h>
+
+#include <string>
+
+std::string GetElfFile();
+
+std::string GetSymbolSortedElfFile();
+
+std::string GetCompressedElfFile();
+
+#if defined(__BIONIC__)
+
+#include <meminfo/procmeminfo.h>
+#include <procinfo/process_map.h>
+
+void GatherRss(uint64_t* rss_bytes);
+
+#endif
+
+#endif  // _LIBUNWINDSTACK_UTILS_h
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index ecd908a..3d81878 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -37,13 +37,14 @@
                                                      uint64_t end);
   static std::unique_ptr<Memory> CreateFileMemory(const std::string& path, uint64_t offset);
 
-  virtual bool ReadString(uint64_t addr, std::string* string, uint64_t max_read = UINT64_MAX);
+  virtual bool ReadString(uint64_t addr, std::string* dst, size_t max_read);
 
   virtual void Clear() {}
 
   virtual bool IsLocal() const { return false; }
 
   virtual size_t Read(uint64_t addr, void* dst, size_t size) = 0;
+  virtual long ReadTag(uint64_t) { return -1; }
 
   bool ReadFully(uint64_t addr, void* dst, size_t size);
 
diff --git a/libunwindstack/tests/MemoryMteTest.cpp b/libunwindstack/tests/MemoryMteTest.cpp
new file mode 100644
index 0000000..3ae322e
--- /dev/null
+++ b/libunwindstack/tests/MemoryMteTest.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#if defined(ANDROID_EXPERIMENTAL_MTE)
+
+#include <sys/mman.h>
+#include <sys/types.h>
+
+#include <gtest/gtest.h>
+
+#include <bionic/mte.h>
+
+#include "MemoryLocal.h"
+#include "MemoryRemote.h"
+#include "TestUtils.h"
+
+namespace unwindstack {
+
+static uintptr_t CreateTagMapping() {
+  uintptr_t mapping =
+      reinterpret_cast<uintptr_t>(mmap(nullptr, getpagesize(), PROT_READ | PROT_WRITE | PROT_MTE,
+                                       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
+  if (reinterpret_cast<void*>(mapping) == MAP_FAILED) {
+    return 0;
+  }
+#if defined(__aarch64__)
+  __asm__ __volatile__(".arch_extension mte; stg %0, [%0]"
+                       :
+                       : "r"(mapping + (1ULL << 56))
+                       : "memory");
+#endif
+  return mapping;
+}
+
+TEST(MemoryMteTest, remote_read_tag) {
+#if !defined(__aarch64__)
+  GTEST_SKIP() << "Requires aarch64";
+#else
+  if (!mte_supported()) {
+    GTEST_SKIP() << "Requires MTE";
+  }
+#endif
+
+  uintptr_t mapping = CreateTagMapping();
+  ASSERT_NE(0U, mapping);
+
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    while (true)
+      ;
+    exit(1);
+  }
+  ASSERT_LT(0, pid);
+  TestScopedPidReaper reap(pid);
+
+  ASSERT_TRUE(TestAttach(pid));
+
+  MemoryRemote remote(pid);
+
+  EXPECT_EQ(1, remote.ReadTag(mapping));
+  EXPECT_EQ(0, remote.ReadTag(mapping + 16));
+
+  ASSERT_TRUE(TestDetach(pid));
+}
+
+TEST(MemoryMteTest, local_read_tag) {
+#if !defined(__aarch64__)
+  GTEST_SKIP() << "Requires aarch64";
+#else
+  if (!mte_supported()) {
+    GTEST_SKIP() << "Requires MTE";
+  }
+#endif
+
+  uintptr_t mapping = CreateTagMapping();
+  ASSERT_NE(0U, mapping);
+
+  MemoryLocal local;
+
+  EXPECT_EQ(1, local.ReadTag(mapping));
+  EXPECT_EQ(0, local.ReadTag(mapping + 16));
+}
+
+}  // namespace unwindstack
+
+#endif
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
index c90dedc..385078d 100644
--- a/libunwindstack/tests/MemoryRemoteTest.cpp
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -26,8 +26,10 @@
 
 #include <vector>
 
-#include <android-base/test_utils.h>
 #include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <bionic/mte.h>
+#include <bionic/mte_kernel.h>
 #include <gtest/gtest.h>
 
 #include "MemoryRemote.h"
@@ -37,24 +39,7 @@
 
 namespace unwindstack {
 
-class MemoryRemoteTest : public ::testing::Test {
- protected:
-  static bool Attach(pid_t pid) {
-    if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
-      return false;
-    }
-
-    return TestQuiescePid(pid);
-  }
-
-  static bool Detach(pid_t pid) {
-    return ptrace(PTRACE_DETACH, pid, 0, 0) == 0;
-  }
-
-  static constexpr size_t NS_PER_SEC = 1000000000ULL;
-};
-
-TEST_F(MemoryRemoteTest, read) {
+TEST(MemoryRemoteTest, read) {
   std::vector<uint8_t> src(1024);
   memset(src.data(), 0x4c, 1024);
 
@@ -66,7 +51,7 @@
   ASSERT_LT(0, pid);
   TestScopedPidReaper reap(pid);
 
-  ASSERT_TRUE(Attach(pid));
+  ASSERT_TRUE(TestAttach(pid));
 
   MemoryRemote remote(pid);
 
@@ -76,10 +61,10 @@
     ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
   }
 
-  ASSERT_TRUE(Detach(pid));
+  ASSERT_TRUE(TestDetach(pid));
 }
 
-TEST_F(MemoryRemoteTest, read_large) {
+TEST(MemoryRemoteTest, read_large) {
   static constexpr size_t kTotalPages = 245;
   std::vector<uint8_t> src(kTotalPages * getpagesize());
   for (size_t i = 0; i < kTotalPages; i++) {
@@ -95,7 +80,7 @@
   ASSERT_LT(0, pid);
   TestScopedPidReaper reap(pid);
 
-  ASSERT_TRUE(Attach(pid));
+  ASSERT_TRUE(TestAttach(pid));
 
   MemoryRemote remote(pid);
 
@@ -105,10 +90,10 @@
     ASSERT_EQ(i / getpagesize(), dst[i]) << "Failed at byte " << i;
   }
 
-  ASSERT_TRUE(Detach(pid));
+  ASSERT_TRUE(TestDetach(pid));
 }
 
-TEST_F(MemoryRemoteTest, read_partial) {
+TEST(MemoryRemoteTest, read_partial) {
   char* mapping = static_cast<char*>(
       mmap(nullptr, 4 * getpagesize(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
   ASSERT_NE(MAP_FAILED, mapping);
@@ -128,7 +113,7 @@
   // Unmap from our process.
   ASSERT_EQ(0, munmap(mapping, 3 * getpagesize()));
 
-  ASSERT_TRUE(Attach(pid));
+  ASSERT_TRUE(TestAttach(pid));
 
   MemoryRemote remote(pid);
 
@@ -149,10 +134,10 @@
     ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
   }
 
-  ASSERT_TRUE(Detach(pid));
+  ASSERT_TRUE(TestDetach(pid));
 }
 
-TEST_F(MemoryRemoteTest, read_fail) {
+TEST(MemoryRemoteTest, read_fail) {
   int pagesize = getpagesize();
   void* src = mmap(nullptr, pagesize * 2, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,-1, 0);
   memset(src, 0x4c, pagesize * 2);
@@ -169,7 +154,7 @@
   ASSERT_LT(0, pid);
   TestScopedPidReaper reap(pid);
 
-  ASSERT_TRUE(Attach(pid));
+  ASSERT_TRUE(TestAttach(pid));
 
   MemoryRemote remote(pid);
 
@@ -188,10 +173,10 @@
 
   ASSERT_EQ(0, munmap(src, pagesize));
 
-  ASSERT_TRUE(Detach(pid));
+  ASSERT_TRUE(TestDetach(pid));
 }
 
-TEST_F(MemoryRemoteTest, read_overflow) {
+TEST(MemoryRemoteTest, read_overflow) {
   pid_t pid;
   if ((pid = fork()) == 0) {
     while (true)
@@ -201,7 +186,7 @@
   ASSERT_LT(0, pid);
   TestScopedPidReaper reap(pid);
 
-  ASSERT_TRUE(Attach(pid));
+  ASSERT_TRUE(TestAttach(pid));
 
   MemoryRemote remote(pid);
 
@@ -209,10 +194,10 @@
   std::vector<uint8_t> dst(200);
   ASSERT_FALSE(remote.ReadFully(UINT64_MAX - 100, dst.data(), 200));
 
-  ASSERT_TRUE(Detach(pid));
+  ASSERT_TRUE(TestDetach(pid));
 }
 
-TEST_F(MemoryRemoteTest, read_illegal) {
+TEST(MemoryRemoteTest, read_illegal) {
   pid_t pid;
   if ((pid = fork()) == 0) {
     while (true);
@@ -221,7 +206,7 @@
   ASSERT_LT(0, pid);
   TestScopedPidReaper reap(pid);
 
-  ASSERT_TRUE(Attach(pid));
+  ASSERT_TRUE(TestAttach(pid));
 
   MemoryRemote remote(pid);
 
@@ -229,10 +214,10 @@
   ASSERT_FALSE(remote.ReadFully(0, dst.data(), 1));
   ASSERT_FALSE(remote.ReadFully(0, dst.data(), 100));
 
-  ASSERT_TRUE(Detach(pid));
+  ASSERT_TRUE(TestDetach(pid));
 }
 
-TEST_F(MemoryRemoteTest, read_mprotect_hole) {
+TEST(MemoryRemoteTest, read_mprotect_hole) {
   size_t page_size = getpagesize();
   void* mapping =
       mmap(nullptr, 3 * getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
@@ -250,7 +235,7 @@
 
   ASSERT_EQ(0, munmap(mapping, 3 * page_size));
 
-  ASSERT_TRUE(Attach(pid));
+  ASSERT_TRUE(TestAttach(pid));
 
   MemoryRemote remote(pid);
   std::vector<uint8_t> dst(getpagesize() * 4, 0xCC);
@@ -263,9 +248,11 @@
   for (size_t i = read_size; i < dst.size(); ++i) {
     ASSERT_EQ(0xCC, dst[i]);
   }
+
+  ASSERT_TRUE(TestDetach(pid));
 }
 
-TEST_F(MemoryRemoteTest, read_munmap_hole) {
+TEST(MemoryRemoteTest, read_munmap_hole) {
   size_t page_size = getpagesize();
   void* mapping =
       mmap(nullptr, 3 * getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
@@ -285,7 +272,7 @@
   ASSERT_EQ(0, munmap(mapping, page_size));
   ASSERT_EQ(0, munmap(static_cast<char*>(mapping) + 2 * page_size, page_size));
 
-  ASSERT_TRUE(Attach(pid));
+  ASSERT_TRUE(TestAttach(pid));
 
   MemoryRemote remote(pid);
   std::vector<uint8_t> dst(getpagesize() * 4, 0xCC);
@@ -297,11 +284,13 @@
   for (size_t i = read_size; i < dst.size(); ++i) {
     ASSERT_EQ(0xCC, dst[i]);
   }
+
+  ASSERT_TRUE(TestDetach(pid));
 }
 
 // Verify that the memory remote object chooses a memory read function
 // properly. Either process_vm_readv or ptrace.
-TEST_F(MemoryRemoteTest, read_choose_correctly) {
+TEST(MemoryRemoteTest, read_choose_correctly) {
   size_t page_size = getpagesize();
   void* mapping =
       mmap(nullptr, 2 * getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
@@ -320,7 +309,7 @@
 
   ASSERT_EQ(0, munmap(mapping, 2 * page_size));
 
-  ASSERT_TRUE(Attach(pid));
+  ASSERT_TRUE(TestAttach(pid));
 
   // We know that process_vm_readv of a mprotect'd PROT_NONE region will fail.
   // Read from the PROT_NONE area first to force the choice of ptrace.
@@ -348,6 +337,8 @@
   bytes = remote_readv.Read(reinterpret_cast<uint64_t>(mapping) + page_size, &value, sizeof(value));
   ASSERT_EQ(sizeof(value), bytes);
   ASSERT_EQ(0xfcfcfcfcU, value);
+
+  ASSERT_TRUE(TestDetach(pid));
 }
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryTest.cpp b/libunwindstack/tests/MemoryTest.cpp
index 3655984..8a8eb24 100644
--- a/libunwindstack/tests/MemoryTest.cpp
+++ b/libunwindstack/tests/MemoryTest.cpp
@@ -59,10 +59,10 @@
   memory.SetMemory(100, name.c_str(), name.size() + 1);
 
   std::string dst_name;
-  ASSERT_TRUE(memory.ReadString(100, &dst_name));
+  ASSERT_TRUE(memory.ReadString(100, &dst_name, 100));
   ASSERT_EQ("string_in_memory", dst_name);
 
-  ASSERT_TRUE(memory.ReadString(107, &dst_name));
+  ASSERT_TRUE(memory.ReadString(107, &dst_name, 100));
   ASSERT_EQ("in_memory", dst_name);
 
   // Set size greater than string.
@@ -82,15 +82,56 @@
 
   std::string dst_name;
   // Read from a non-existant address.
-  ASSERT_FALSE(memory.ReadString(100, &dst_name));
+  ASSERT_FALSE(memory.ReadString(100, &dst_name, 100));
 
   // This should fail because there is no terminating '\0'.
-  ASSERT_FALSE(memory.ReadString(0, &dst_name));
+  ASSERT_FALSE(memory.ReadString(0, &dst_name, 100));
 
   // This should pass because there is a terminating '\0'.
   memory.SetData8(name.size(), '\0');
-  ASSERT_TRUE(memory.ReadString(0, &dst_name));
+  ASSERT_TRUE(memory.ReadString(0, &dst_name, 100));
   ASSERT_EQ("short", dst_name);
 }
 
+TEST(MemoryTest, read_string_long) {
+  // This string should be greater than 768 characters long (greater than 3 times
+  // the buffer in the ReadString function) to read multiple blocks.
+  static constexpr char kLongString[] =
+      "one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen "
+      "sixteen seventeen eightteen nineteen twenty twenty-one twenty-two twenty-three twenty-four "
+      "twenty-five twenty-six twenty-seven twenty-eight twenty-nine thirty thirty-one thirty-two "
+      "thirty-three thirty-four thirty-five thirty-six thirty-seven thirty-eight thirty-nine forty "
+      "forty-one forty-two forty-three forty-four forty-five forty-size forty-seven forty-eight "
+      "forty-nine fifty fifty-one fifty-two fifty-three fifty-four fifty-five fifty-six "
+      "fifty-seven fifty-eight fifty-nine sixty sixty-one sixty-two sixty-three sixty-four "
+      "sixty-five sixty-six sixty-seven sixty-eight sixty-nine seventy seventy-one seventy-two "
+      "seventy-three seventy-four seventy-five seventy-six seventy-seven seventy-eight "
+      "seventy-nine eighty";
+
+  MemoryFake memory;
+
+  memory.SetMemory(100, kLongString, sizeof(kLongString));
+
+  std::string dst_name;
+  ASSERT_TRUE(memory.ReadString(100, &dst_name, sizeof(kLongString)));
+  ASSERT_EQ(kLongString, dst_name);
+
+  std::string expected_str(kLongString, 255);
+  memory.SetMemory(100, expected_str.data(), expected_str.length() + 1);
+  ASSERT_TRUE(memory.ReadString(100, &dst_name, 256));
+  ASSERT_EQ(expected_str, dst_name);
+  ASSERT_FALSE(memory.ReadString(100, &dst_name, 255));
+
+  expected_str = std::string(kLongString, 256);
+  memory.SetMemory(100, expected_str.data(), expected_str.length() + 1);
+  ASSERT_TRUE(memory.ReadString(100, &dst_name, 257));
+  ASSERT_EQ(expected_str, dst_name);
+  ASSERT_FALSE(memory.ReadString(100, &dst_name, 256));
+
+  expected_str = std::string(kLongString, 299);
+  memory.SetMemory(100, expected_str.data(), expected_str.length() + 1);
+  ASSERT_TRUE(memory.ReadString(100, &dst_name, 300));
+  ASSERT_EQ(expected_str, dst_name);
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/SymbolsTest.cpp b/libunwindstack/tests/SymbolsTest.cpp
index c58aeff..9afbeec 100644
--- a/libunwindstack/tests/SymbolsTest.cpp
+++ b/libunwindstack/tests/SymbolsTest.cpp
@@ -185,18 +185,21 @@
   std::string fake_name;
 
   this->InitSym(&sym, 0x5000, 0x10, 0x40);
+  this->memory_.SetMemoryBlock(offset, entry_size, 0);
   this->memory_.SetMemory(offset, &sym, sizeof(sym));
   fake_name = "function_one";
   this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
   offset += entry_size;
 
   this->InitSym(&sym, 0x3004, 0x200, 0x100);
+  this->memory_.SetMemoryBlock(offset, entry_size, 0);
   this->memory_.SetMemory(offset, &sym, sizeof(sym));
   fake_name = "function_two";
   this->memory_.SetMemory(0x2100, fake_name.c_str(), fake_name.size() + 1);
   offset += entry_size;
 
   this->InitSym(&sym, 0xa010, 0x20, 0x230);
+  this->memory_.SetMemoryBlock(offset, entry_size, 0);
   this->memory_.SetMemory(offset, &sym, sizeof(sym));
   fake_name = "function_three";
   this->memory_.SetMemory(0x2230, fake_name.c_str(), fake_name.size() + 1);
@@ -274,7 +277,9 @@
   // Do call that should cache all of the entries (except the string data).
   std::string name;
   uint64_t func_offset;
-  ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, &this->memory_, &name, &func_offset));
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x2000, &this->memory_, &name, &func_offset));
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x1000, &this->memory_, &name, &func_offset));
   this->memory_.Clear();
   ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, &this->memory_, &name, &func_offset));
 
diff --git a/libunwindstack/tests/TestUtils.h b/libunwindstack/tests/TestUtils.h
index a4d7b9b..0685006 100644
--- a/libunwindstack/tests/TestUtils.h
+++ b/libunwindstack/tests/TestUtils.h
@@ -21,6 +21,7 @@
 #include <sys/ptrace.h>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <unistd.h>
 
 namespace unwindstack {
 
@@ -50,6 +51,18 @@
   return ready;
 }
 
+inline bool TestAttach(pid_t pid) {
+  if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
+    return false;
+  }
+
+  return TestQuiescePid(pid);
+}
+
+inline bool TestDetach(pid_t pid) {
+  return ptrace(PTRACE_DETACH, pid, 0, 0) == 0;
+}
+
 void TestCheckForLeaks(void (*unwind_func)(void*), void* data);
 
 }  // namespace unwindstack
diff --git a/libutils/include/utils/Looper.h b/libutils/include/utils/Looper.h
index c439c5c..466fbb7 100644
--- a/libutils/include/utils/Looper.h
+++ b/libutils/include/utils/Looper.h
@@ -26,6 +26,8 @@
 
 #include <android-base/unique_fd.h>
 
+#include <utility>
+
 namespace android {
 
 /*
@@ -438,9 +440,8 @@
     struct MessageEnvelope {
         MessageEnvelope() : uptime(0) { }
 
-        MessageEnvelope(nsecs_t u, const sp<MessageHandler> h,
-                const Message& m) : uptime(u), handler(h), message(m) {
-        }
+        MessageEnvelope(nsecs_t u, sp<MessageHandler> h, const Message& m)
+            : uptime(u), handler(std::move(h)), message(m) {}
 
         nsecs_t uptime;
         sp<MessageHandler> handler;
diff --git a/libziparchive/ziptool.cpp b/libziparchive/ziptool.cpp
index 17d4833..a261535 100644
--- a/libziparchive/ziptool.cpp
+++ b/libziparchive/ziptool.cpp
@@ -263,12 +263,12 @@
   snprintf(time, sizeof(time), "%04d-%02d-%02d %02d:%02d", t.tm_year + 1900, t.tm_mon + 1,
            t.tm_mday, t.tm_hour, t.tm_min);
   if (flag_v) {
-    printf("%8" PRIu64 " %s  %8" PRIu64 " %3.0f%% %s %08x  %s\n", entry.uncompressed_length,
+    printf("%8" PRIu64 "  %s %8" PRIu64 " %3.0f%% %s %08x  %s\n", entry.uncompressed_length,
            (entry.method == kCompressStored) ? "Stored" : "Defl:N", entry.compressed_length,
            CompressionRatio(entry.uncompressed_length, entry.compressed_length), time, entry.crc32,
            name.c_str());
   } else {
-    printf("%9" PRIu64 " %s   %s\n", entry.uncompressed_length, time, name.c_str());
+    printf("%9" PRIu64 "  %s   %s\n", entry.uncompressed_length, time, name.c_str());
   }
 }