Merge "Prevent infinite loop on zero length USB descriptors"
diff --git a/Android.bp b/Android.bp
index c6f6251..0b4a925 100644
--- a/Android.bp
+++ b/Android.bp
@@ -2,5 +2,3 @@
     name: "android_filesystem_config_header",
     srcs: ["include/private/android_filesystem_config.h"],
 }
-
-subdirs = ["*"]
diff --git a/TEST_MAPPING b/TEST_MAPPING
index d6945e3..e3a8675 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -4,6 +4,18 @@
       "name": "adbd_test"
     },
     {
+      "name": "adb_crypto_test"
+    },
+    {
+      "name": "adb_pairing_auth_test"
+    },
+    {
+      "name": "adb_pairing_connection_test"
+    },
+    {
+      "name": "adb_tls_connection_test"
+    },
+    {
       "name": "CtsInitTestCases"
     },
     {
diff --git a/adb/Android.bp b/adb/Android.bp
index bd1f124..fea8c78 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -165,9 +165,11 @@
 
     srcs: libadb_srcs + [
         "client/auth.cpp",
+        "client/adb_wifi.cpp",
         "client/usb_libusb.cpp",
         "client/usb_dispatch.cpp",
         "client/transport_mdns.cpp",
+        "client/pairing/pairing_client.cpp",
     ],
 
     generated_headers: ["platform_tools_version"],
@@ -195,6 +197,10 @@
     },
 
     static_libs: [
+        "libadb_crypto",
+        "libadb_protos",
+        "libadb_pairing_connection",
+        "libadb_tls_connection",
         "libbase",
         "libcrypto_utils",
         "libcrypto",
@@ -204,6 +210,7 @@
         "libutils",
         "liblog",
         "libcutils",
+        "libprotobuf-cpp-lite",
     ],
 }
 
@@ -212,13 +219,21 @@
     defaults: ["adb_defaults"],
     srcs: libadb_test_srcs,
     static_libs: [
+        "libadb_crypto_static",
         "libadb_host",
+        "libadb_pairing_auth_static",
+        "libadb_pairing_connection_static",
+        "libadb_protos_static",
+        "libadb_tls_connection_static",
         "libbase",
         "libcutils",
         "libcrypto_utils",
         "libcrypto",
+        "liblog",
         "libmdnssd",
         "libdiagnose_usb",
+        "libprotobuf-cpp-lite",
+        "libssl",
         "libusb",
     ],
 
@@ -250,12 +265,16 @@
     },
 
     static_libs: [
+        "libadb_crypto_static",
+        "libadb_tls_connection_static",
+        "libadbd_auth",
         "libbase",
         "libcutils",
         "libcrypto_utils",
         "libcrypto_static",
         "libdiagnose_usb",
         "liblog",
+        "libssl",
         "libusb",
     ],
 }
@@ -263,6 +282,7 @@
 cc_binary_host {
     name: "adb",
 
+    stl: "libc++_static",
     defaults: ["adb_defaults"],
 
     srcs: [
@@ -276,6 +296,8 @@
         "client/line_printer.cpp",
         "client/fastdeploy.cpp",
         "client/fastdeploycallbacks.cpp",
+        "client/incremental.cpp",
+        "client/incremental_server.cpp",
         "shell_service_protocol.cpp",
     ],
 
@@ -285,7 +307,12 @@
     ],
 
     static_libs: [
+        "libadb_crypto",
         "libadb_host",
+	"libadb_pairing_auth",
+	"libadb_pairing_connection",
+        "libadb_protos",
+        "libadb_tls_connection",
         "libandroidfw",
         "libbase",
         "libcutils",
@@ -294,8 +321,10 @@
         "libfastdeploy_host",
         "libdiagnose_usb",
         "liblog",
+        "liblz4",
         "libmdnssd",
         "libprotobuf-cpp-lite",
+        "libssl",
         "libusb",
         "libutils",
         "liblog",
@@ -303,8 +332,6 @@
         "libz",
     ],
 
-    stl: "libc++_static",
-
     // Don't add anything here, we don't want additional shared dependencies
     // on the host adb tool, and shared libraries that link against libc++
     // will violate ODR
@@ -348,6 +375,7 @@
     srcs: libadb_srcs + libadb_linux_srcs + libadb_posix_srcs + [
         "daemon/auth.cpp",
         "daemon/jdwp_service.cpp",
+        "daemon/adb_wifi.cpp",
     ],
 
     local_include_dirs: [
@@ -357,17 +385,20 @@
     generated_headers: ["platform_tools_version"],
 
     static_libs: [
+        "libadbconnection_server",
         "libdiagnose_usb",
     ],
 
     shared_libs: [
-        "libadbconnection_server",
+        "libadb_crypto",
+        "libadb_pairing_connection",
+        "libadb_protos",
+        "libadb_tls_connection",
         "libadbd_auth",
         "libasyncio",
         "libbase",
         "libcrypto",
         "libcrypto_utils",
-        "libcutils",
         "liblog",
     ],
 
@@ -391,7 +422,7 @@
     },
 }
 
-cc_library {
+cc_library_static {
     name: "libadbd_services",
     defaults: ["adbd_defaults", "host_adbd_supported"],
     recovery_available: true,
@@ -410,18 +441,22 @@
     ],
 
     static_libs: [
+        "libadbconnection_server",
         "libadbd_core",
         "libdiagnose_usb",
     ],
 
     shared_libs: [
-        "libadbconnection_server",
+        "libadb_crypto",
+        "libadb_pairing_connection",
+        "libadb_protos",
+        "libadb_tls_connection",
         "libadbd_auth",
+        "libadbd_fs",
         "libasyncio",
         "libbase",
         "libcrypto",
         "libcrypto_utils",
-        "libcutils",
         "liblog",
     ],
 
@@ -451,7 +486,7 @@
     defaults: ["adbd_defaults", "host_adbd_supported"],
     recovery_available: true,
 
-    // Avoid getting duplicate symbol of android::build::GetBuildNumber().
+    // avoid getting duplicate symbol of android::build::getbuildnumber().
     use_version_lib: false,
 
     // libminadbd wants both, as it's used to build native tests.
@@ -459,19 +494,29 @@
 
     // libadbd doesn't build any additional source, but to expose libadbd_core as a shared library.
     whole_static_libs: [
+        "libadbconnection_server",
         "libadbd_core",
     ],
 
     shared_libs: [
-        "libadbconnection_server",
+        "libadb_crypto",
+        "libadb_pairing_connection",
+        "libadb_tls_connection",
         "libadbd_auth",
-        "libadbd_services",
+        "libadbd_fs",
         "libasyncio",
         "libbase",
         "libcrypto",
         "libcrypto_utils",
-        "libcutils",
         "liblog",
+        "libselinux",
+    ],
+
+    static_libs: [
+        "libadbd_services",
+        "libcutils_sockets",
+        "libdiagnose_usb",
+        "libmdnssd",
     ],
 
     export_include_dirs: [
@@ -482,6 +527,7 @@
 cc_binary {
     name: "adbd",
     defaults: ["adbd_defaults", "host_adbd_supported"],
+    stl: "libc++_static",
     recovery_available: true,
 
     srcs: [
@@ -497,34 +543,43 @@
         keep_symbols: true,
     },
 
-    stl: "libc++_static",
     static_libs: [
+        "libadbconnection_server",
         "libadbd",
-        "libadbd_auth",
         "libadbd_services",
         "libasyncio",
         "libbase",
         "libcap",
         "libcrypto_utils",
-        "libcutils",
+        "libcutils_sockets",
         "libdiagnose_usb",
         "liblog",
         "libmdnssd",
         "libminijail",
         "libselinux",
+        "libssl",
     ],
 
     shared_libs: [
-        "libadbconnection_server",
+        "libadb_crypto",
+        "libadb_pairing_connection",
+        "libadb_protos",
+        "libadb_tls_connection",
+        "libadbd_auth",
+        "libadbd_fs",
         "libcrypto",
     ],
+
+    required: [
+        "libadbd_auth",
+        "libadbd_fs",
+    ],
 }
 
 phony {
     name: "adbd_system_binaries",
     required: [
         "abb",
-        "libadbd_auth",
         "reboot",
         "set-verity-state",
     ]
@@ -541,6 +596,7 @@
     name: "abb",
 
     defaults: ["adbd_defaults"],
+    stl: "libc++",
     recovery_available: false,
 
     srcs: [
@@ -573,7 +629,11 @@
 
 cc_test {
     name: "adbd_test",
+
     defaults: ["adbd_defaults"],
+    stl: "libc++_static",
+
+    recovery_available: false,
     srcs: libadb_test_srcs + [
         "daemon/services.cpp",
         "daemon/shell_service.cpp",
@@ -585,17 +645,20 @@
     static_libs: [
         "libadbd",
         "libadbd_auth",
+        "libadb_crypto_static",
+        "libadb_pairing_connection_static",
+        "libadb_tls_connection_static",
         "libbase",
-        "libcutils",
         "libcrypto_utils",
         "libcrypto_static",
+        "libcutils_sockets",
         "libdiagnose_usb",
         "liblog",
         "libusb",
         "libmdnssd",
         "libselinux",
     ],
-    test_suites: ["device-tests"],
+    test_suites: ["device-tests", "mts"],
     require_root: true,
 }
 
@@ -630,10 +693,10 @@
     test_suites: ["general-tests"],
     version: {
         py2: {
-            enabled: true,
+            enabled: false,
         },
         py3: {
-            enabled: false,
+            enabled: true,
         },
     },
 }
@@ -678,7 +741,6 @@
         "libziparchive",
         "libz",
     ],
-    stl: "libc++_static",
     proto: {
         type: "lite",
         export_proto_headers: true,
@@ -700,7 +762,12 @@
         "fastdeploy/deploypatchgenerator/patch_utils_test.cpp",
     ],
     static_libs: [
+        "libadb_crypto_static",
         "libadb_host",
+        "libadb_pairing_auth_static",
+        "libadb_pairing_connection_static",
+        "libadb_protos_static",
+        "libadb_tls_connection_static",
         "libandroidfw",
         "libbase",
         "libcutils",
@@ -711,6 +778,7 @@
         "liblog",
         "libmdnssd",
         "libprotobuf-cpp-lite",
+        "libssl",
         "libusb",
         "libutils",
         "libziparchive",
@@ -729,39 +797,3 @@
         "fastdeploy/testdata/sample.cd",
     ],
 }
-
-prebuilt_etc {
-    name: "com.android.adbd.ld.config.txt",
-    src: "apex/ld.config.txt",
-    filename: "ld.config.txt",
-    installable: false,
-}
-
-apex {
-    name: "com.android.adbd",
-    manifest: "apex/apex_manifest.json",
-
-    binaries: ["adbd"],
-    prebuilts: ["com.android.adbd.init.rc", "com.android.adbd.ld.config.txt"],
-
-    key: "com.android.adbd.key",
-    certificate: ":com.android.adbd.certificate",
-}
-
-apex_key {
-    name: "com.android.adbd.key",
-    public_key: "apex/com.android.adbd.avbpubkey",
-    private_key: "apex/com.android.adbd.pem",
-}
-
-android_app_certificate {
-    name: "com.android.adbd.certificate",
-    certificate: "apex/com.android.adbd",
-}
-
-prebuilt_etc {
-    name: "com.android.adbd.init.rc",
-    src: "apex/adbd.rc",
-    filename: "init.rc",
-    installable: false,
-}
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 460ddde..554a754 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -52,6 +52,7 @@
 #include "adb_listeners.h"
 #include "adb_unique_fd.h"
 #include "adb_utils.h"
+#include "adb_wifi.h"
 #include "sysdeps/chrono.h"
 #include "transport.h"
 
@@ -140,6 +141,9 @@
     case A_CLSE: tag = "CLSE"; break;
     case A_WRTE: tag = "WRTE"; break;
     case A_AUTH: tag = "AUTH"; break;
+    case A_STLS:
+        tag = "ATLS";
+        break;
     default: tag = "????"; break;
     }
 
@@ -209,6 +213,15 @@
         android::base::Join(connection_properties, ';').c_str());
 }
 
+void send_tls_request(atransport* t) {
+    D("Calling send_tls_request");
+    apacket* p = get_apacket();
+    p->msg.command = A_STLS;
+    p->msg.arg0 = A_STLS_VERSION;
+    p->msg.data_length = 0;
+    send_packet(p, t);
+}
+
 void send_connect(atransport* t) {
     D("Calling send_connect");
     apacket* cp = get_apacket();
@@ -299,7 +312,12 @@
 #if ADB_HOST
     handle_online(t);
 #else
-    if (!auth_required) {
+    if (t->use_tls) {
+        // We still handshake in TLS mode. If auth_required is disabled,
+        // we'll just not verify the client's certificate. This should be the
+        // first packet the client receives to indicate the new protocol.
+        send_tls_request(t);
+    } else if (!auth_required) {
         LOG(INFO) << "authentication not required";
         handle_online(t);
         send_connect(t);
@@ -324,8 +342,21 @@
     case A_CNXN:  // CONNECT(version, maxdata, "system-id-string")
         handle_new_connection(t, p);
         break;
+    case A_STLS:  // TLS(version, "")
+        t->use_tls = true;
+#if ADB_HOST
+        send_tls_request(t);
+        adb_auth_tls_handshake(t);
+#else
+        adbd_auth_tls_handshake(t);
+#endif
+        break;
 
     case A_AUTH:
+        // All AUTH commands are ignored in TLS mode
+        if (t->use_tls) {
+            break;
+        }
         switch (p->msg.arg0) {
 #if ADB_HOST
             case ADB_AUTH_TOKEN:
diff --git a/adb/adb.h b/adb/adb.h
index e7fcc91..86d205c 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -44,6 +44,7 @@
 #define A_CLSE 0x45534c43
 #define A_WRTE 0x45545257
 #define A_AUTH 0x48545541
+#define A_STLS 0x534C5453
 
 // ADB protocol version.
 // Version revision:
@@ -53,6 +54,10 @@
 #define A_VERSION_SKIP_CHECKSUM 0x01000001
 #define A_VERSION 0x01000001
 
+// Stream-based TLS protocol version
+#define A_STLS_VERSION_MIN 0x01000000
+#define A_STLS_VERSION 0x01000000
+
 // Used for help/version information.
 #define ADB_VERSION_MAJOR 1
 #define ADB_VERSION_MINOR 0
@@ -185,14 +190,7 @@
     } while (0)
 #endif
 
-#if ADB_HOST_ON_TARGET
-/* adb and adbd are coexisting on the target, so use 5038 for adb
- * to avoid conflicting with adbd's usage of 5037
- */
-#define DEFAULT_ADB_PORT 5038
-#else
 #define DEFAULT_ADB_PORT 5037
-#endif
 
 #define DEFAULT_ADB_LOCAL_TRANSPORT_PORT 5555
 
@@ -236,6 +234,7 @@
 void handle_offline(atransport* t);
 
 void send_connect(atransport* t);
+void send_tls_request(atransport* t);
 
 void parse_banner(const std::string&, atransport* t);
 
diff --git a/adb/adb_auth.h b/adb/adb_auth.h
index 2be9a76..7e858dc 100644
--- a/adb/adb_auth.h
+++ b/adb/adb_auth.h
@@ -38,10 +38,14 @@
 int adb_auth_keygen(const char* filename);
 int adb_auth_pubkey(const char* filename);
 std::string adb_auth_get_userkey();
+bssl::UniquePtr<EVP_PKEY> adb_auth_get_user_privkey();
 std::deque<std::shared_ptr<RSA>> adb_auth_get_private_keys();
 
 void send_auth_response(const char* token, size_t token_size, atransport* t);
 
+int adb_tls_set_certificate(SSL* ssl);
+void adb_auth_tls_handshake(atransport* t);
+
 #else // !ADB_HOST
 
 extern bool auth_required;
@@ -57,6 +61,10 @@
 
 void send_auth_request(atransport *t);
 
+void adbd_auth_tls_handshake(atransport* t);
+int adbd_tls_verify_cert(X509_STORE_CTX* ctx, std::string* auth_key);
+bssl::UniquePtr<STACK_OF(X509_NAME)> adbd_tls_client_ca_list();
+
 #endif // ADB_HOST
 
 #endif // __ADB_AUTH_H
diff --git a/adb/adb_mdns.h b/adb/adb_mdns.h
index 2e544d7..6b37355 100644
--- a/adb/adb_mdns.h
+++ b/adb/adb_mdns.h
@@ -17,6 +17,72 @@
 #ifndef _ADB_MDNS_H_
 #define _ADB_MDNS_H_
 
+#include <android-base/macros.h>
+
 const char* kADBServiceType = "_adb._tcp";
+const char* kADBSecurePairingServiceType = "_adb_secure_pairing._tcp";
+const char* kADBSecureConnectServiceType = "_adb_secure_connect._tcp";
+
+const int kADBTransportServiceRefIndex = 0;
+const int kADBSecurePairingServiceRefIndex = 1;
+const int kADBSecureConnectServiceRefIndex = 2;
+
+// Each ADB Secure service advertises with a TXT record indicating the version
+// using a key/value pair per RFC 6763 (https://tools.ietf.org/html/rfc6763).
+//
+// The first key/value pair is always the version of the protocol.
+// There may be more key/value pairs added after.
+//
+// The version is purposely represented as the single letter "v" due to the
+// need to minimize DNS traffic. The version starts at 1.  With each breaking
+// protocol change, the version is incremented by 1.
+//
+// Newer adb clients/daemons need to recognize and either reject
+// or be backward-compatible with older verseions if there is a mismatch.
+//
+// Relevant sections:
+//
+// """
+// 6.4.  Rules for Keys in DNS-SD Key/Value Pairs
+//
+// The key MUST be at least one character.  DNS-SD TXT record strings
+// beginning with an '=' character (i.e., the key is missing) MUST be
+// silently ignored.
+//
+// ...
+//
+// 6.5.  Rules for Values in DNS-SD Key/Value Pairs
+//
+// If there is an '=' in a DNS-SD TXT record string, then everything
+// after the first '=' to the end of the string is the value.  The value
+// can contain any eight-bit values including '='.
+// """
+
+#define ADB_SECURE_SERVICE_VERSION_TXT_RECORD(ver) ("v=" #ver)
+
+// Client/service versions are initially defined to be matching,
+// but may go out of sync as different clients and services
+// try to talk to each other.
+#define ADB_SECURE_SERVICE_VERSION 1
+#define ADB_SECURE_CLIENT_VERSION ADB_SECURE_SERVICE_VERSION
+
+const char* kADBSecurePairingServiceTxtRecord =
+        ADB_SECURE_SERVICE_VERSION_TXT_RECORD(ADB_SECURE_SERVICE_VERSION);
+const char* kADBSecureConnectServiceTxtRecord =
+        ADB_SECURE_SERVICE_VERSION_TXT_RECORD(ADB_SECURE_SERVICE_VERSION);
+
+const char* kADBDNSServices[] = {
+        kADBServiceType,
+        kADBSecurePairingServiceType,
+        kADBSecureConnectServiceType,
+};
+
+const char* kADBDNSServiceTxtRecords[] = {
+        nullptr,
+        kADBSecurePairingServiceTxtRecord,
+        kADBSecureConnectServiceTxtRecord,
+};
+
+const int kNumADBDNSServices = arraysize(kADBDNSServices);
 
 #endif
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
index 80f146c..cea24fe 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -118,22 +118,22 @@
         return;
     }
 
-    std::unordered_map<std::string, int> trace_flags = {
-        {"1", -1},
-        {"all", -1},
-        {"adb", ADB},
-        {"sockets", SOCKETS},
-        {"packets", PACKETS},
-        {"rwx", RWX},
-        {"usb", USB},
-        {"sync", SYNC},
-        {"sysdeps", SYSDEPS},
-        {"transport", TRANSPORT},
-        {"jdwp", JDWP},
-        {"services", SERVICES},
-        {"auth", AUTH},
-        {"fdevent", FDEVENT},
-        {"shell", SHELL}};
+    std::unordered_map<std::string, int> trace_flags = {{"1", -1},
+                                                        {"all", -1},
+                                                        {"adb", ADB},
+                                                        {"sockets", SOCKETS},
+                                                        {"packets", PACKETS},
+                                                        {"rwx", RWX},
+                                                        {"usb", USB},
+                                                        {"sync", SYNC},
+                                                        {"sysdeps", SYSDEPS},
+                                                        {"transport", TRANSPORT},
+                                                        {"jdwp", JDWP},
+                                                        {"services", SERVICES},
+                                                        {"auth", AUTH},
+                                                        {"fdevent", FDEVENT},
+                                                        {"shell", SHELL},
+                                                        {"incremental", INCREMENTAL}};
 
     std::vector<std::string> elements = android::base::Split(trace_setting, " ");
     for (const auto& elem : elements) {
diff --git a/adb/adb_trace.h b/adb/adb_trace.h
index 1d2c8c7..3421a02 100644
--- a/adb/adb_trace.h
+++ b/adb/adb_trace.h
@@ -25,19 +25,20 @@
  * the adb_trace_init() function implemented in adb_trace.cpp.
  */
 enum AdbTrace {
-    ADB = 0,   /* 0x001 */
+    ADB = 0, /* 0x001 */
     SOCKETS,
     PACKETS,
     TRANSPORT,
-    RWX,       /* 0x010 */
+    RWX, /* 0x010 */
     USB,
     SYNC,
     SYSDEPS,
-    JDWP,      /* 0x100 */
+    JDWP, /* 0x100 */
     SERVICES,
     AUTH,
     FDEVENT,
-    SHELL
+    SHELL,
+    INCREMENTAL,
 };
 
 #define VLOG_IS_ON(TAG) \
@@ -58,11 +59,4 @@
 void adb_trace_init(char**);
 void adb_trace_enable(AdbTrace trace_tag);
 
-// Include <atomic> before stdatomic.h (introduced in cutils/trace.h) to avoid compile error.
-#include <atomic>
-
-#define ATRACE_TAG ATRACE_TAG_ADB
-#include <cutils/trace.h>
-#include <utils/Trace.h>
-
 #endif /* __ADB_TRACE_H */
diff --git a/adb/adb_wifi.h b/adb/adb_wifi.h
new file mode 100644
index 0000000..585748c
--- /dev/null
+++ b/adb/adb_wifi.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "adb.h"
+
+#if ADB_HOST
+
+void adb_wifi_init(void);
+void adb_wifi_pair_device(const std::string& host, const std::string& password,
+                          std::string& response);
+bool adb_wifi_is_known_host(const std::string& host);
+
+#else  // !ADB_HOST
+
+struct AdbdAuthContext;
+
+void adbd_wifi_init(AdbdAuthContext* ctx);
+void adbd_wifi_secure_connect(atransport* t);
+
+#endif
diff --git a/adb/apex/Android.bp b/adb/apex/Android.bp
new file mode 100644
index 0000000..0189455
--- /dev/null
+++ b/adb/apex/Android.bp
@@ -0,0 +1,53 @@
+apex_defaults {
+    name: "com.android.adbd-defaults",
+
+    binaries: ["adbd"],
+    compile_multilib: "both",
+    multilib: {
+        both: {
+            native_shared_libs: [
+                "libadb_pairing_auth",
+                "libadb_pairing_connection",
+                "libadb_pairing_server",
+                "libadbconnection_client",
+            ],
+        },
+    },
+    prebuilts: ["com.android.adbd.init.rc"],
+
+    key: "com.android.adbd.key",
+    certificate: ":com.android.adbd.certificate",
+}
+
+apex {
+    name: "com.android.adbd",
+    defaults: ["com.android.adbd-defaults"],
+    manifest: "apex_manifest.json",
+}
+
+// adbd apex with INT_MAX version code, to allow for upgrade/rollback testing.
+apex_test {
+    name: "test_com.android.adbd",
+    defaults: ["com.android.adbd-defaults"],
+    manifest: "test_apex_manifest.json",
+    file_contexts: ":com.android.adbd-file_contexts",
+    installable: false,
+}
+
+prebuilt_etc {
+    name: "com.android.adbd.init.rc",
+    src: "adbd.rc",
+    filename: "init.rc",
+    installable: false,
+}
+
+apex_key {
+    name: "com.android.adbd.key",
+    public_key: "com.android.adbd.avbpubkey",
+    private_key: "com.android.adbd.pem",
+}
+
+android_app_certificate {
+    name: "com.android.adbd.certificate",
+    certificate: "com.android.adbd",
+}
diff --git a/adb/apex/ld.config.txt b/adb/apex/ld.config.txt
deleted file mode 100644
index 85f9b29..0000000
--- a/adb/apex/ld.config.txt
+++ /dev/null
@@ -1,51 +0,0 @@
-# Copyright (C) 2019 The Android Open Source Project
-#
-# Bionic loader config file for the adbd APEX.
-
-dir.adbd = /apex/com.android.adbd/bin/
-
-[adbd]
-additional.namespaces = platform,art,adbd
-
-namespace.default.isolated = true
-namespace.default.links = art,adbd,platform
-namespace.default.link.art.shared_libs = libadbconnection_server.so
-namespace.default.link.platform.allow_all_shared_libs = true
-namespace.default.link.adbd.allow_all_shared_libs = true
-
-###############################################################################
-# "adbd" APEX namespace
-###############################################################################
-namespace.adbd.isolated = true
-namespace.adbd.search.paths = /apex/com.android.adbd/${LIB}
-namespace.adbd.asan.search.paths = /apex/com.android.adbd/${LIB}
-namespace.adbd.links = platform
-namespace.adbd.link.platform.allow_all_shared_libs = true
-
-###############################################################################
-# "art" APEX namespace: used for libadbdconnection_server
-###############################################################################
-namespace.art.isolated = true
-namespace.art.search.paths = /apex/com.android.art/${LIB}
-namespace.art.asan.search.paths = /apex/com.android.art/${LIB}
-namespace.art.links = platform
-namespace.art.link.platform.allow_all_shared_libs = true
-
-###############################################################################
-# "platform" namespace: used for NDK libraries, and libadbd_auth
-###############################################################################
-namespace.platform.isolated = true
-namespace.platform.search.paths = /system/${LIB}
-namespace.platform.asan.search.paths = /data/asan/system/${LIB}
-
-# /system/lib/libc.so, etc are symlinks to
-# /apex/com.android.runtime/lib/bionic/libc.so, etc. Add the path to the
-# permitted paths because linker uses realpath(3) to check the accessibility
-# of the lib. We could add this to search.paths instead but that makes the
-# resolution of bionic libs be dependent on the order of /system/lib and
-# /apex/.../lib/bionic in search.paths. If the latter is after the former,
-# then the latter is never tried because libc.so is always found in
-# /system/lib but fails to pass the accessibility test because of its realpath.
-# It's better to not depend on the ordering if possible.
-namespace.platform.permitted.paths = /apex/com.android.runtime/${LIB}/bionic
-namespace.platform.asan.permitted.paths = /apex/com.android.runtime/${LIB}/bionic
diff --git a/adb/apex/test_apex_manifest.json b/adb/apex/test_apex_manifest.json
new file mode 100644
index 0000000..7131977
--- /dev/null
+++ b/adb/apex/test_apex_manifest.json
@@ -0,0 +1,4 @@
+{
+  "name": "com.android.adbd",
+  "version": 2147483647
+}
diff --git a/adb/client/adb_client.h b/adb/client/adb_client.h
index ba53041..1c6cde7 100644
--- a/adb/client/adb_client.h
+++ b/adb/client/adb_client.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <functional>
 #include <optional>
 #include <string>
 
@@ -86,3 +87,19 @@
 // Globally acccesible argv/envp, for the purpose of re-execing adb.
 extern const char* _Nullable * _Nullable __adb_argv;
 extern const char* _Nullable * _Nullable __adb_envp;
+
+// 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)>;
+
+// Queries pairing/connect services that have been discovered and resolved.
+// If |host_name| is not null, run |cb| only for services
+// matching |host_name|. Otherwise, run for all services.
+void adb_secure_foreach_pairing_service(const char* _Nullable service_name,
+                                        adb_secure_foreach_service_callback cb);
+void adb_secure_foreach_connect_service(const char* _Nullable service_name,
+                                        adb_secure_foreach_service_callback cb);
+// Tries to connect to a |service_name| if found. Returns true if found and
+// connected, false otherwise.
+bool adb_secure_connect_by_service_name(const char* _Nonnull service_name);
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index 73dcde1..21b8f49 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -17,6 +17,7 @@
 #include "adb_install.h"
 
 #include <fcntl.h>
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -36,8 +37,10 @@
 #include "client/file_sync_client.h"
 #include "commandline.h"
 #include "fastdeploy.h"
+#include "incremental.h"
 
 static constexpr int kFastDeployMinApi = 24;
+static constexpr int kIncrementalMinApi = 29;
 
 namespace {
 
@@ -45,8 +48,8 @@
     INSTALL_DEFAULT,
     INSTALL_PUSH,
     INSTALL_STREAM,
+    INSTALL_INCREMENTAL,
 };
-
 }
 
 static bool can_use_feature(const char* feature) {
@@ -70,6 +73,10 @@
     return can_use_feature(kFeatureApex);
 }
 
+static bool is_abb_exec_supported() {
+    return can_use_feature(kFeatureAbbExec);
+}
+
 static int pm_command(int argc, const char** argv) {
     std::string cmd = "pm";
 
@@ -193,14 +200,14 @@
     posix_fadvise(local_fd.get(), 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE);
 #endif
 
-    const bool use_abb = can_use_feature(kFeatureAbbExec);
+    const bool use_abb_exec = is_abb_exec_supported();
     std::string error;
-    std::vector<std::string> cmd_args = {use_abb ? "package" : "exec:cmd package"};
+    std::vector<std::string> cmd_args = {use_abb_exec ? "package" : "exec:cmd package"};
     cmd_args.reserve(argc + 3);
 
     // don't copy the APK name, but, copy the rest of the arguments as-is
     while (argc-- > 1) {
-        if (use_abb) {
+        if (use_abb_exec) {
             cmd_args.push_back(*argv++);
         } else {
             cmd_args.push_back(escape_arg(*argv++));
@@ -217,7 +224,7 @@
     }
 
     unique_fd remote_fd;
-    if (use_abb) {
+    if (use_abb_exec) {
         remote_fd = send_abb_exec_command(cmd_args, &error);
     } else {
         remote_fd.reset(adb_connect(android::base::Join(cmd_args, " "), &error));
@@ -227,16 +234,20 @@
         return 1;
     }
 
-    copy_to_file(local_fd.get(), remote_fd.get());
+    if (!copy_to_file(local_fd.get(), remote_fd.get())) {
+        fprintf(stderr, "adb: failed to install: copy_to_file: %s: %s", file, strerror(errno));
+        return 1;
+    }
 
     char buf[BUFSIZ];
     read_status_line(remote_fd.get(), buf, sizeof(buf));
-    if (!strncmp("Success", buf, 7)) {
-        fputs(buf, stdout);
-        return 0;
+    if (strncmp("Success", buf, 7) != 0) {
+        fprintf(stderr, "adb: failed to install %s: %s", file, buf);
+        return 1;
     }
-    fprintf(stderr, "adb: failed to install %s: %s", file, buf);
-    return 1;
+
+    fputs(buf, stdout);
+    return 0;
 }
 
 static int install_app_legacy(int argc, const char** argv, bool use_fastdeploy) {
@@ -283,8 +294,60 @@
     return result;
 }
 
+template <class TimePoint>
+static int msBetween(TimePoint start, TimePoint end) {
+    return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
+}
+
+static int install_app_incremental(int argc, const char** argv) {
+    printf("Performing Incremental Install\n");
+    using clock = std::chrono::high_resolution_clock;
+    const auto start = clock::now();
+    int first_apk = -1;
+    int last_apk = -1;
+    std::string cert_path;
+    bool wait = false;
+    std::vector<std::string_view> args = {"package"};
+    for (int i = 0; i < argc; ++i) {
+        const auto arg = std::string_view(argv[i]);
+        if (android::base::EndsWithIgnoreCase(arg, ".apk")) {
+            last_apk = i;
+            if (first_apk == -1) {
+                first_apk = i;
+            }
+        } else if (arg == "--wait") {
+            wait = true;
+        } else if (arg.starts_with("install-")) {
+            // incremental installation command on the device is the same for all its variations in
+            // the adb, e.g. install-multiple or install-multi-package
+            args.push_back("install");
+        } else {
+            args.push_back(arg);
+        }
+    }
+
+    if (first_apk == -1) error_exit("Need at least one APK file on command line");
+
+    const auto afterApk = clock::now();
+
+    auto server_process = incremental::install({argv + first_apk, argv + last_apk + 1});
+    if (!server_process) {
+        return -1;
+    }
+
+    const auto end = clock::now();
+    printf("Install command complete (ms: %d total, %d apk prep, %d install)\n",
+           msBetween(start, end), msBetween(start, afterApk), msBetween(afterApk, end));
+
+    if (wait) {
+        (*server_process).wait();
+    }
+
+    return 0;
+}
+
 int install_app(int argc, const char** argv) {
-    std::vector<int> processedArgIndicies;
+    std::vector<int> processedArgIndices;
     InstallMode installMode = INSTALL_DEFAULT;
     bool use_fastdeploy = false;
     bool is_reinstall = false;
@@ -292,30 +355,42 @@
 
     for (int i = 1; i < argc; i++) {
         if (!strcmp(argv[i], "--streaming")) {
-            processedArgIndicies.push_back(i);
+            processedArgIndices.push_back(i);
             installMode = INSTALL_STREAM;
         } else if (!strcmp(argv[i], "--no-streaming")) {
-            processedArgIndicies.push_back(i);
+            processedArgIndices.push_back(i);
             installMode = INSTALL_PUSH;
         } else if (!strcmp(argv[i], "-r")) {
-            // Note that this argument is not added to processedArgIndicies because it
+            // Note that this argument is not added to processedArgIndices because it
             // must be passed through to pm
             is_reinstall = true;
         } else if (!strcmp(argv[i], "--fastdeploy")) {
-            processedArgIndicies.push_back(i);
+            processedArgIndices.push_back(i);
             use_fastdeploy = true;
         } else if (!strcmp(argv[i], "--no-fastdeploy")) {
-            processedArgIndicies.push_back(i);
+            processedArgIndices.push_back(i);
             use_fastdeploy = false;
         } else if (!strcmp(argv[i], "--force-agent")) {
-            processedArgIndicies.push_back(i);
+            processedArgIndices.push_back(i);
             agent_update_strategy = FastDeploy_AgentUpdateAlways;
         } else if (!strcmp(argv[i], "--date-check-agent")) {
-            processedArgIndicies.push_back(i);
+            processedArgIndices.push_back(i);
             agent_update_strategy = FastDeploy_AgentUpdateNewerTimeStamp;
         } else if (!strcmp(argv[i], "--version-check-agent")) {
-            processedArgIndicies.push_back(i);
+            processedArgIndices.push_back(i);
             agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
+        } else if (!strcmp(argv[i], "--incremental")) {
+            processedArgIndices.push_back(i);
+            installMode = INSTALL_INCREMENTAL;
+        } else if (!strcmp(argv[i], "--no-incremental")) {
+            processedArgIndices.push_back(i);
+            installMode = INSTALL_DEFAULT;
+        }
+    }
+
+    if (installMode == INSTALL_INCREMENTAL) {
+        if (get_device_api_level() < kIncrementalMinApi || !is_abb_exec_supported()) {
+            error_exit("Attempting to use incremental install on unsupported device");
         }
     }
 
@@ -337,8 +412,8 @@
 
     std::vector<const char*> passthrough_argv;
     for (int i = 0; i < argc; i++) {
-        if (std::find(processedArgIndicies.begin(), processedArgIndicies.end(), i) ==
-            processedArgIndicies.end()) {
+        if (std::find(processedArgIndices.begin(), processedArgIndices.end(), i) ==
+            processedArgIndices.end()) {
             passthrough_argv.push_back(argv[i]);
         }
     }
@@ -353,6 +428,8 @@
         case INSTALL_STREAM:
             return install_app_streamed(passthrough_argv.size(), passthrough_argv.data(),
                                         use_fastdeploy);
+        case INSTALL_INCREMENTAL:
+            return install_app_incremental(passthrough_argv.size(), passthrough_argv.data());
         case INSTALL_DEFAULT:
         default:
             return 1;
@@ -455,7 +532,12 @@
             goto finalize_session;
         }
 
-        copy_to_file(local_fd.get(), remote_fd.get());
+        if (!copy_to_file(local_fd.get(), remote_fd.get())) {
+            fprintf(stderr, "adb: failed to write \"%s\": %s\n", file, strerror(errno));
+            success = false;
+            goto finalize_session;
+        }
+
         read_status_line(remote_fd.get(), buf, sizeof(buf));
 
         if (strncmp("Success", buf, 7)) {
@@ -634,7 +716,11 @@
                 goto finalize_multi_package_session;
             }
 
-            copy_to_file(local_fd.get(), remote_fd.get());
+            if (!copy_to_file(local_fd.get(), remote_fd.get())) {
+                fprintf(stderr, "adb: failed to write %s: %s\n", split.c_str(), strerror(errno));
+                goto finalize_multi_package_session;
+            }
+
             read_status_line(remote_fd.get(), buf, sizeof(buf));
 
             if (strncmp("Success", buf, 7)) {
diff --git a/adb/client/adb_wifi.cpp b/adb/client/adb_wifi.cpp
new file mode 100644
index 0000000..fa71028
--- /dev/null
+++ b/adb/client/adb_wifi.cpp
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "adb_wifi.h"
+
+#include <fstream>
+#include <random>
+#include <thread>
+
+#include <adb/crypto/key.h>
+#include <adb/crypto/x509_generator.h>
+#include <android-base/file.h>
+#include <android-base/parsenetaddress.h>
+#include "client/pairing/pairing_client.h"
+
+#include "adb_auth.h"
+#include "adb_known_hosts.pb.h"
+#include "adb_utils.h"
+#include "client/adb_client.h"
+#include "sysdeps.h"
+
+using adbwifi::pairing::PairingClient;
+using namespace adb::crypto;
+
+struct PairingResultWaiter {
+    std::mutex mutex_;
+    std::condition_variable cv_;
+    std::optional<bool> is_valid_;
+    PeerInfo peer_info_;
+
+    static void OnResult(const PeerInfo* peer_info, void* opaque) {
+        CHECK(opaque);
+        auto* p = reinterpret_cast<PairingResultWaiter*>(opaque);
+        {
+            std::lock_guard<std::mutex> lock(p->mutex_);
+            if (peer_info) {
+                memcpy(&(p->peer_info_), peer_info, sizeof(PeerInfo));
+            }
+            p->is_valid_ = (peer_info != nullptr);
+        }
+        p->cv_.notify_one();
+    }
+};  // PairingResultWaiter
+
+void adb_wifi_init() {}
+
+static std::vector<uint8_t> stringToUint8(const std::string& str) {
+    auto* p8 = reinterpret_cast<const uint8_t*>(str.data());
+    return std::vector<uint8_t>(p8, p8 + str.length());
+}
+
+// Tries to replace the |old_file| with |new_file|.
+// On success, then |old_file| has been removed and replaced with the
+// contents of |new_file|, |new_file| will be removed, and only |old_file| will
+// remain.
+// On failure, both files will be unchanged.
+// |new_file| must exist, but |old_file| does not need to exist.
+bool SafeReplaceFile(std::string_view old_file, std::string_view new_file) {
+    std::string to_be_deleted(old_file);
+    to_be_deleted += ".tbd";
+
+    bool old_renamed = true;
+    if (adb_rename(old_file.data(), to_be_deleted.c_str()) != 0) {
+        // Don't exit here. This is not necessarily an error, because |old_file|
+        // may not exist.
+        PLOG(INFO) << "Failed to rename " << old_file;
+        old_renamed = false;
+    }
+
+    if (adb_rename(new_file.data(), old_file.data()) != 0) {
+        PLOG(ERROR) << "Unable to rename file (" << new_file << " => " << old_file << ")";
+        if (old_renamed) {
+            // Rename the .tbd file back to it's original name
+            adb_rename(to_be_deleted.c_str(), old_file.data());
+        }
+        return false;
+    }
+
+    adb_unlink(to_be_deleted.c_str());
+    return true;
+}
+
+static std::string get_user_known_hosts_path() {
+    return adb_get_android_dir_path() + OS_PATH_SEPARATOR + "adb_known_hosts.pb";
+}
+
+bool load_known_hosts_from_file(const std::string& path, adb::proto::AdbKnownHosts& known_hosts) {
+    // Check for file existence.
+    struct stat buf;
+    if (stat(path.c_str(), &buf) == -1) {
+        LOG(INFO) << "Known hosts file [" << path << "] does not exist...";
+        return false;
+    }
+
+    std::ifstream file(path, std::ios::binary);
+    if (!file) {
+        PLOG(ERROR) << "Unable to open [" << path << "].";
+        return false;
+    }
+
+    if (!known_hosts.ParseFromIstream(&file)) {
+        PLOG(ERROR) << "Failed to parse [" << path << "]. Deleting it as it may be corrupted.";
+        adb_unlink(path.c_str());
+        return false;
+    }
+
+    return true;
+}
+
+static bool write_known_host_to_file(std::string& known_host) {
+    std::string path = get_user_known_hosts_path();
+    if (path.empty()) {
+        PLOG(ERROR) << "Error getting user known hosts filename";
+        return false;
+    }
+
+    adb::proto::AdbKnownHosts known_hosts;
+    load_known_hosts_from_file(path, known_hosts);
+    auto* host_info = known_hosts.add_host_infos();
+    host_info->set_guid(known_host);
+
+    std::unique_ptr<TemporaryFile> temp_file(new TemporaryFile(adb_get_android_dir_path()));
+    if (temp_file->fd == -1) {
+        PLOG(ERROR) << "Failed to open [" << temp_file->path << "] for writing";
+        return false;
+    }
+
+    if (!known_hosts.SerializeToFileDescriptor(temp_file->fd)) {
+        LOG(ERROR) << "Unable to write out adb_knowns_hosts";
+        return false;
+    }
+    temp_file->DoNotRemove();
+    std::string temp_file_name(temp_file->path);
+    temp_file.reset();
+
+    // Replace the existing adb_known_hosts with the new one
+    if (!SafeReplaceFile(path, temp_file_name.c_str())) {
+        LOG(ERROR) << "Failed to replace old adb_known_hosts";
+        adb_unlink(temp_file_name.c_str());
+        return false;
+    }
+    chmod(path.c_str(), S_IRUSR | S_IWUSR | S_IRGRP);
+
+    return true;
+}
+
+bool adb_wifi_is_known_host(const std::string& host) {
+    std::string path = get_user_known_hosts_path();
+    if (path.empty()) {
+        PLOG(ERROR) << "Error getting user known hosts filename";
+        return false;
+    }
+
+    adb::proto::AdbKnownHosts known_hosts;
+    if (!load_known_hosts_from_file(path, known_hosts)) {
+        return false;
+    }
+
+    for (const auto& host_info : known_hosts.host_infos()) {
+        if (host == host_info.guid()) {
+            return true;
+        }
+    }
+    return false;
+}
+
+void adb_wifi_pair_device(const std::string& host, const std::string& password,
+                          std::string& response) {
+    // Check the address for a valid address and port.
+    std::string parsed_host;
+    std::string err;
+    int port = -1;
+    if (!android::base::ParseNetAddress(host, &parsed_host, &port, nullptr, &err)) {
+        response = "Failed to parse address for pairing: " + err;
+        return;
+    }
+    if (port <= 0 || port > 65535) {
+        response = "Invalid port while parsing address [" + host + "]";
+        return;
+    }
+
+    auto priv_key = adb_auth_get_user_privkey();
+    auto x509_cert = GenerateX509Certificate(priv_key.get());
+    if (!x509_cert) {
+        LOG(ERROR) << "Unable to create X509 certificate for pairing";
+        return;
+    }
+    auto cert_str = X509ToPEMString(x509_cert.get());
+    auto priv_str = Key::ToPEMString(priv_key.get());
+
+    // Send our public key on pairing success
+    PeerInfo system_info = {};
+    system_info.type = ADB_RSA_PUB_KEY;
+    std::string public_key = adb_auth_get_userkey();
+    CHECK_LE(public_key.size(), sizeof(system_info.data) - 1);  // -1 for null byte
+    memcpy(system_info.data, public_key.data(), public_key.size());
+
+    auto pswd8 = stringToUint8(password);
+    auto cert8 = stringToUint8(cert_str);
+    auto priv8 = stringToUint8(priv_str);
+
+    auto client = PairingClient::Create(pswd8, system_info, cert8, priv8);
+    if (client == nullptr) {
+        response = "Failed: unable to create pairing client.";
+        return;
+    }
+
+    PairingResultWaiter waiter;
+    std::unique_lock<std::mutex> lock(waiter.mutex_);
+    if (!client->Start(host, waiter.OnResult, &waiter)) {
+        response = "Failed: Unable to start pairing client.";
+        return;
+    }
+    waiter.cv_.wait(lock, [&]() { return waiter.is_valid_.has_value(); });
+    if (!*(waiter.is_valid_)) {
+        response = "Failed: Wrong password or connection was dropped.";
+        return;
+    }
+
+    if (waiter.peer_info_.type != ADB_DEVICE_GUID) {
+        response = "Failed: Successfully paired but server returned unknown response=";
+        response += waiter.peer_info_.type;
+        return;
+    }
+
+    std::string device_guid = reinterpret_cast<const char*>(waiter.peer_info_.data);
+    response = "Successfully paired to " + host + " [guid=" + device_guid + "]";
+
+    // Write to adb_known_hosts
+    write_known_host_to_file(device_guid);
+    // Try to auto-connect.
+    adb_secure_connect_by_service_name(device_guid.c_str());
+}
diff --git a/adb/client/auth.cpp b/adb/client/auth.cpp
index e8be784..8738ce7 100644
--- a/adb/client/auth.cpp
+++ b/adb/client/auth.cpp
@@ -29,6 +29,10 @@
 #include <set>
 #include <string>
 
+#include <adb/crypto/rsa_2048_key.h>
+#include <adb/crypto/x509_generator.h>
+#include <adb/tls/adb_ca_list.h>
+#include <adb/tls/tls_connection.h>
 #include <android-base/errors.h>
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
@@ -53,100 +57,51 @@
     *new std::map<std::string, std::shared_ptr<RSA>>;
 static std::map<int, std::string>& g_monitored_paths = *new std::map<int, std::string>;
 
-static std::string get_user_info() {
-    std::string hostname;
-    if (getenv("HOSTNAME")) hostname = getenv("HOSTNAME");
-#if !defined(_WIN32)
-    char buf[64];
-    if (hostname.empty() && gethostname(buf, sizeof(buf)) != -1) hostname = buf;
-#endif
-    if (hostname.empty()) hostname = "unknown";
+using namespace adb::crypto;
+using namespace adb::tls;
 
-    std::string username;
-    if (getenv("LOGNAME")) username = getenv("LOGNAME");
-#if !defined(_WIN32)
-    if (username.empty() && getlogin()) username = getlogin();
-#endif
-    if (username.empty()) hostname = "unknown";
-
-    return " " + username + "@" + hostname;
-}
-
-static bool calculate_public_key(std::string* out, RSA* private_key) {
-    uint8_t binary_key_data[ANDROID_PUBKEY_ENCODED_SIZE];
-    if (!android_pubkey_encode(private_key, binary_key_data, sizeof(binary_key_data))) {
-        LOG(ERROR) << "Failed to convert to public key";
-        return false;
-    }
-
-    size_t expected_length;
-    if (!EVP_EncodedLength(&expected_length, sizeof(binary_key_data))) {
-        LOG(ERROR) << "Public key too large to base64 encode";
-        return false;
-    }
-
-    out->resize(expected_length);
-    size_t actual_length = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(out->data()), binary_key_data,
-                                           sizeof(binary_key_data));
-    out->resize(actual_length);
-    out->append(get_user_info());
-    return true;
-}
-
-static int generate_key(const std::string& file) {
+static bool generate_key(const std::string& file) {
     LOG(INFO) << "generate_key(" << file << ")...";
 
-    mode_t old_mask;
-    FILE *f = nullptr;
-    int ret = 0;
+    auto rsa_2048 = CreateRSA2048Key();
+    if (!rsa_2048) {
+        LOG(ERROR) << "Unable to create key";
+        return false;
+    }
     std::string pubkey;
 
-    EVP_PKEY* pkey = EVP_PKEY_new();
-    BIGNUM* exponent = BN_new();
-    RSA* rsa = RSA_new();
-    if (!pkey || !exponent || !rsa) {
-        LOG(ERROR) << "Failed to allocate key";
-        goto out;
-    }
+    RSA* rsa = EVP_PKEY_get0_RSA(rsa_2048->GetEvpPkey());
+    CHECK(rsa);
 
-    BN_set_word(exponent, RSA_F4);
-    RSA_generate_key_ex(rsa, 2048, exponent, nullptr);
-    EVP_PKEY_set1_RSA(pkey, rsa);
-
-    if (!calculate_public_key(&pubkey, rsa)) {
+    if (!CalculatePublicKey(&pubkey, rsa)) {
         LOG(ERROR) << "failed to calculate public key";
-        goto out;
+        return false;
     }
 
-    old_mask = umask(077);
+    mode_t old_mask = umask(077);
 
-    f = fopen(file.c_str(), "w");
+    std::unique_ptr<FILE, decltype(&fclose)> f(nullptr, &fclose);
+    f.reset(fopen(file.c_str(), "w"));
     if (!f) {
         PLOG(ERROR) << "Failed to open " << file;
         umask(old_mask);
-        goto out;
+        return false;
     }
 
     umask(old_mask);
 
-    if (!PEM_write_PrivateKey(f, pkey, nullptr, nullptr, 0, nullptr, nullptr)) {
+    if (!PEM_write_PrivateKey(f.get(), rsa_2048->GetEvpPkey(), nullptr, nullptr, 0, nullptr,
+                              nullptr)) {
         LOG(ERROR) << "Failed to write key";
-        goto out;
+        return false;
     }
 
     if (!android::base::WriteStringToFile(pubkey, file + ".pub")) {
         PLOG(ERROR) << "failed to write public key";
-        goto out;
+        return false;
     }
 
-    ret = 1;
-
-out:
-    if (f) fclose(f);
-    EVP_PKEY_free(pkey);
-    RSA_free(rsa);
-    BN_free(exponent);
-    return ret;
+    return true;
 }
 
 static std::string hash_key(RSA* key) {
@@ -193,6 +148,7 @@
     if (g_keys.find(fingerprint) != g_keys.end()) {
         LOG(INFO) << "ignoring already-loaded key: " << file;
     } else {
+        LOG(INFO) << "Loaded fingerprint=[" << SHA256BitsToHexString(fingerprint) << "]";
         g_keys[fingerprint] = std::move(key);
     }
     return true;
@@ -325,7 +281,29 @@
     if (!privkey) {
         return false;
     }
-    return calculate_public_key(out, privkey.get());
+    return CalculatePublicKey(out, privkey.get());
+}
+
+bssl::UniquePtr<EVP_PKEY> adb_auth_get_user_privkey() {
+    std::string path = get_user_key_path();
+    if (path.empty()) {
+        PLOG(ERROR) << "Error getting user key filename";
+        return nullptr;
+    }
+
+    std::shared_ptr<RSA> rsa_privkey = read_key_file(path);
+    if (!rsa_privkey) {
+        return nullptr;
+    }
+
+    bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
+    if (!pkey) {
+        LOG(ERROR) << "Failed to allocate key";
+        return nullptr;
+    }
+
+    EVP_PKEY_set1_RSA(pkey.get(), rsa_privkey.get());
+    return pkey;
 }
 
 std::string adb_auth_get_userkey() {
@@ -343,7 +321,7 @@
 }
 
 int adb_auth_keygen(const char* filename) {
-    return (generate_key(filename) == 0);
+    return !generate_key(filename);
 }
 
 int adb_auth_pubkey(const char* filename) {
@@ -502,3 +480,72 @@
     p->msg.data_length = p->payload.size();
     send_packet(p, t);
 }
+
+void adb_auth_tls_handshake(atransport* t) {
+    std::thread([t]() {
+        std::shared_ptr<RSA> key = t->Key();
+        if (key == nullptr) {
+            // Can happen if !auth_required
+            LOG(INFO) << "t->auth_key not set before handshake";
+            key = t->NextKey();
+            CHECK(key);
+        }
+
+        LOG(INFO) << "Attempting to TLS handshake";
+        bool success = t->connection()->DoTlsHandshake(key.get());
+        if (success) {
+            LOG(INFO) << "Handshake succeeded. Waiting for CNXN packet...";
+        } else {
+            LOG(INFO) << "Handshake failed. Kicking transport";
+            t->Kick();
+        }
+    }).detach();
+}
+
+int adb_tls_set_certificate(SSL* ssl) {
+    LOG(INFO) << __func__;
+
+    const STACK_OF(X509_NAME)* ca_list = SSL_get_client_CA_list(ssl);
+    if (ca_list == nullptr) {
+        // Either the device doesn't know any keys, or !auth_required.
+        // So let's just try with the default certificate and see what happens.
+        LOG(INFO) << "No client CA list. Trying with default certificate.";
+        return 1;
+    }
+
+    const size_t num_cas = sk_X509_NAME_num(ca_list);
+    for (size_t i = 0; i < num_cas; ++i) {
+        auto* x509_name = sk_X509_NAME_value(ca_list, i);
+        auto adbFingerprint = ParseEncodedKeyFromCAIssuer(x509_name);
+        if (!adbFingerprint.has_value()) {
+            // This could be a real CA issuer. Unfortunately, we don't support
+            // it ATM.
+            continue;
+        }
+
+        LOG(INFO) << "Checking for fingerprint match [" << *adbFingerprint << "]";
+        auto encoded_key = SHA256HexStringToBits(*adbFingerprint);
+        if (!encoded_key.has_value()) {
+            continue;
+        }
+        // Check against our list of encoded keys for a match
+        std::lock_guard<std::mutex> lock(g_keys_mutex);
+        auto rsa_priv_key = g_keys.find(*encoded_key);
+        if (rsa_priv_key != g_keys.end()) {
+            LOG(INFO) << "Got SHA256 match on a key";
+            bssl::UniquePtr<EVP_PKEY> evp_pkey(EVP_PKEY_new());
+            CHECK(EVP_PKEY_set1_RSA(evp_pkey.get(), rsa_priv_key->second.get()));
+            auto x509 = GenerateX509Certificate(evp_pkey.get());
+            auto x509_str = X509ToPEMString(x509.get());
+            auto evp_str = Key::ToPEMString(evp_pkey.get());
+            TlsConnection::SetCertAndKey(ssl, x509_str, evp_str);
+            return 1;
+        } else {
+            LOG(INFO) << "No match for [" << *adbFingerprint << "]";
+        }
+    }
+
+    // Let's just try with the default certificate anyways, because daemon might
+    // not require auth, even though it has a list of keys.
+    return 1;
+}
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 813a8a9..081bac4 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -30,6 +30,7 @@
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <iostream>
 
 #include <memory>
 #include <string>
@@ -60,6 +61,7 @@
 #include "client/file_sync_client.h"
 #include "commandline.h"
 #include "fastdeploy.h"
+#include "incremental_server.h"
 #include "services.h"
 #include "shell_protocol.h"
 #include "sysdeps/chrono.h"
@@ -96,8 +98,10 @@
         " version                  show version num\n"
         "\n"
         "networking:\n"
-        " connect HOST[:PORT]      connect to a device via TCP/IP\n"
-        " disconnect [[HOST]:PORT] disconnect from given TCP/IP device, or all\n"
+        " connect HOST[:PORT]      connect to a device via TCP/IP [default port=5555]\n"
+        " disconnect [HOST[:PORT]]\n"
+        "     disconnect from given TCP/IP device [default port=5555], or all\n"
+        " pair HOST[:PORT]         pair with a device for secure TCP/IP communication\n"
         " forward --list           list all forward socket connections\n"
         " forward [--no-rebind] LOCAL REMOTE\n"
         "     forward socket connection using:\n"
@@ -352,7 +356,8 @@
 #endif
 }
 
-void copy_to_file(int inFd, int outFd) {
+bool copy_to_file(int inFd, int outFd) {
+    bool result = true;
     std::vector<char> buf(64 * 1024);
     int len;
     long total = 0;
@@ -375,6 +380,7 @@
         }
         if (len < 0) {
             D("copy_to_file(): read failed: %s", strerror(errno));
+            result = false;
             break;
         }
         if (outFd == STDOUT_FILENO) {
@@ -388,7 +394,8 @@
 
     stdinout_raw_epilogue(inFd, outFd, old_stdin_mode, old_stdout_mode);
 
-    D("copy_to_file() finished after %lu bytes", total);
+    D("copy_to_file() finished with %s after %lu bytes", result ? "success" : "failure", total);
+    return result;
 }
 
 static void send_window_size_change(int fd, std::unique_ptr<ShellProtocol>& shell) {
@@ -790,6 +797,15 @@
                        service_string);
 }
 
+static int adb_shell_noinput(int argc, const char** argv) {
+#if !defined(_WIN32)
+    unique_fd fd(adb_open("/dev/null", O_RDONLY));
+    CHECK_NE(STDIN_FILENO, fd.get());
+    dup2(fd.get(), STDIN_FILENO);
+#endif
+    return adb_shell(argc, argv);
+}
+
 static int adb_sideload_legacy(const char* filename, int in_fd, int size) {
     std::string error;
     unique_fd out_fd(adb_connect(android::base::StringPrintf("sideload:%d", size), &error));
@@ -1116,8 +1132,8 @@
         return false;
     }
 
+    fwrite(buf, 1, sizeof(buf) - bytes_left, stdout);
     fflush(stdout);
-    WriteFdExactly(STDOUT_FILENO, buf, sizeof(buf) - bytes_left);
     if (cur != buf && strstr(buf, "restarting") == nullptr) {
         return true;
     }
@@ -1612,7 +1628,7 @@
         return adb_query_command(query);
     }
     else if (!strcmp(argv[0], "connect")) {
-        if (argc != 2) error_exit("usage: adb connect HOST[:PORT>]");
+        if (argc != 2) error_exit("usage: adb connect HOST[:PORT]");
 
         std::string query = android::base::StringPrintf("host:connect:%s", argv[1]);
         return adb_query_command(query);
@@ -1625,6 +1641,19 @@
         return adb_query_command(query);
     } else if (!strcmp(argv[0], "abb")) {
         return adb_abb(argc, argv);
+    } else if (!strcmp(argv[0], "pair")) {
+        if (argc != 2) error_exit("usage: adb pair <host>[:<port>]");
+
+        std::string password;
+        printf("Enter pairing code: ");
+        fflush(stdout);
+        if (!std::getline(std::cin, password) || password.empty()) {
+            error_exit("No pairing code provided");
+        }
+        std::string query =
+                android::base::StringPrintf("host:pair:%s:%s", password.c_str(), argv[1]);
+
+        return adb_query_command(query);
     } else if (!strcmp(argv[0], "emu")) {
         return adb_send_emulator_command(argc, argv, serial);
     } else if (!strcmp(argv[0], "shell")) {
@@ -1711,7 +1740,7 @@
         if (CanUseFeature(features, kFeatureRemountShell)) {
             std::vector<const char*> args = {"shell"};
             args.insert(args.cend(), argv, argv + argc);
-            return adb_shell(args.size(), args.data());
+            return adb_shell_noinput(args.size(), args.data());
         } else if (argc > 1) {
             auto command = android::base::StringPrintf("%s:%s", argv[0], argv[1]);
             return adb_connect_command(command);
@@ -1947,6 +1976,18 @@
                 error_exit("usage: adb reconnect [device|offline]");
             }
         }
+    } else if (!strcmp(argv[0], "inc-server")) {
+        if (argc < 3) {
+            error_exit("usage: adb inc-server FD FILE1 FILE2 ...");
+        }
+        int fd = atoi(argv[1]);
+        if (fd < 3) {
+            // Disallow invalid FDs and stdin/out/err as well.
+            error_exit("Invalid fd number given: %d", fd);
+        }
+        fd = adb_register_socket(fd);
+        close_on_exec(fd);
+        return incremental::serve(fd, argc - 2, argv + 2);
     }
 
     error_exit("unknown command %s", argv[0]);
diff --git a/adb/client/commandline.h b/adb/client/commandline.h
index ab77b29..b9dee56 100644
--- a/adb/client/commandline.h
+++ b/adb/client/commandline.h
@@ -100,7 +100,7 @@
 
 int adb_commandline(int argc, const char** argv);
 
-void copy_to_file(int inFd, int outFd);
+bool copy_to_file(int inFd, int outFd);
 
 // Connects to the device "shell" service with |command| and prints the
 // resulting output.
diff --git a/adb/client/fastdeploy.cpp b/adb/client/fastdeploy.cpp
index 5fa0edb..c5fc12f 100644
--- a/adb/client/fastdeploy.cpp
+++ b/adb/client/fastdeploy.cpp
@@ -81,17 +81,21 @@
 }  // namespace
 
 int get_device_api_level() {
-    REPORT_FUNC_TIME();
-    std::vector<char> sdk_version_output_buffer;
-    std::vector<char> sdk_version_error_buffer;
-    int api_level = -1;
+    static const int api_level = [] {
+        REPORT_FUNC_TIME();
+        std::vector<char> sdk_version_output_buffer;
+        std::vector<char> sdk_version_error_buffer;
+        int api_level = -1;
 
-    int statusCode = capture_shell_command("getprop ro.build.version.sdk",
-                                           &sdk_version_output_buffer, &sdk_version_error_buffer);
-    if (statusCode == 0 && sdk_version_output_buffer.size() > 0) {
-        api_level = strtol((char*)sdk_version_output_buffer.data(), NULL, 10);
-    }
+        int status_code =
+                capture_shell_command("getprop ro.build.version.sdk", &sdk_version_output_buffer,
+                                      &sdk_version_error_buffer);
+        if (status_code == 0 && sdk_version_output_buffer.size() > 0) {
+            api_level = strtol((char*)sdk_version_output_buffer.data(), nullptr, 10);
+        }
 
+        return api_level;
+    }();
     return api_level;
 }
 
diff --git a/adb/client/file_sync_client.cpp b/adb/client/file_sync_client.cpp
index fbfeb53..922f2ba 100644
--- a/adb/client/file_sync_client.cpp
+++ b/adb/client/file_sync_client.cpp
@@ -187,14 +187,13 @@
         const char* direction_str = (direction == TransferDirection::push) ? "pushed" : "pulled";
         std::stringstream ss;
         if (!name.empty()) {
-            ss << name << ": ";
+            std::string_view display_name(name);
+            char* out = getenv("ANDROID_PRODUCT_OUT");
+            if (out) android::base::ConsumePrefix(&display_name, out);
+            ss << display_name << ": ";
         }
         ss << files_transferred << " file" << ((files_transferred == 1) ? "" : "s") << " "
-           << direction_str << ".";
-        if (files_skipped > 0) {
-            ss << " " << files_skipped << " file" << ((files_skipped == 1) ? "" : "s")
-               << " skipped.";
-        }
+           << direction_str << ", " << files_skipped << " skipped.";
         ss << TransferRate();
 
         lp.Print(ss.str(), LinePrinter::LineType::INFO);
diff --git a/adb/client/incremental.cpp b/adb/client/incremental.cpp
new file mode 100644
index 0000000..6499d46
--- /dev/null
+++ b/adb/client/incremental.cpp
@@ -0,0 +1,216 @@
+/*
+ * 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 "incremental.h"
+
+#include <android-base/endian.h>
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <openssl/base64.h>
+
+#include "adb_client.h"
+#include "adb_io.h"
+#include "adb_utils.h"
+#include "commandline.h"
+#include "sysdeps.h"
+
+using namespace std::literals;
+
+namespace incremental {
+
+namespace {
+
+static constexpr auto IDSIG = ".idsig"sv;
+
+using android::base::StringPrintf;
+
+using Size = int64_t;
+
+static inline int32_t read_int32(borrowed_fd fd) {
+    int32_t result;
+    ReadFully(fd, &result, sizeof(result));
+    return result;
+}
+
+static inline int32_t read_be_int32(borrowed_fd fd) {
+    return int32_t(be32toh(read_int32(fd)));
+}
+
+static inline void append_int(borrowed_fd fd, std::vector<char>* bytes) {
+    int32_t be_val = read_int32(fd);
+    auto old_size = bytes->size();
+    bytes->resize(old_size + sizeof(be_val));
+    memcpy(bytes->data() + old_size, &be_val, sizeof(be_val));
+}
+
+static inline void append_bytes_with_size(borrowed_fd fd, std::vector<char>* bytes) {
+    int32_t be_size = read_int32(fd);
+    int32_t size = int32_t(be32toh(be_size));
+    auto old_size = bytes->size();
+    bytes->resize(old_size + sizeof(be_size) + size);
+    memcpy(bytes->data() + old_size, &be_size, sizeof(be_size));
+    ReadFully(fd, bytes->data() + old_size + sizeof(be_size), size);
+}
+
+static inline std::pair<std::vector<char>, int32_t> read_id_sig_headers(borrowed_fd fd) {
+    std::vector<char> result;
+    append_int(fd, &result);              // version
+    append_bytes_with_size(fd, &result);  // verityRootHash
+    append_bytes_with_size(fd, &result);  // v3Digest
+    append_bytes_with_size(fd, &result);  // pkcs7SignatureBlock
+    auto tree_size = read_be_int32(fd);   // size of the verity tree
+    return {std::move(result), tree_size};
+}
+
+static inline Size verity_tree_size_for_file(Size fileSize) {
+    constexpr int INCFS_DATA_FILE_BLOCK_SIZE = 4096;
+    constexpr int SHA256_DIGEST_SIZE = 32;
+    constexpr int digest_size = SHA256_DIGEST_SIZE;
+    constexpr int hash_per_block = INCFS_DATA_FILE_BLOCK_SIZE / digest_size;
+
+    Size total_tree_block_count = 0;
+
+    auto block_count = 1 + (fileSize - 1) / INCFS_DATA_FILE_BLOCK_SIZE;
+    auto hash_block_count = block_count;
+    for (auto i = 0; hash_block_count > 1; i++) {
+        hash_block_count = (hash_block_count + hash_per_block - 1) / hash_per_block;
+        total_tree_block_count += hash_block_count;
+    }
+    return total_tree_block_count * INCFS_DATA_FILE_BLOCK_SIZE;
+}
+
+// Base64-encode signature bytes. Keeping fd at the position of start of verity tree.
+static std::pair<unique_fd, std::string> read_and_encode_signature(Size file_size,
+                                                                   std::string signature_file) {
+    signature_file += IDSIG;
+
+    struct stat st;
+    if (stat(signature_file.c_str(), &st)) {
+        fprintf(stderr, "Failed to stat signature file %s. Abort.\n", signature_file.c_str());
+        return {};
+    }
+
+    unique_fd fd(adb_open(signature_file.c_str(), O_RDONLY | O_CLOEXEC));
+    if (fd < 0) {
+        fprintf(stderr, "Failed to open signature file: %s. Abort.\n", signature_file.c_str());
+        return {};
+    }
+
+    auto [signature, tree_size] = read_id_sig_headers(fd);
+    if (auto expected = verity_tree_size_for_file(file_size); tree_size != expected) {
+        fprintf(stderr,
+                "Verity tree size mismatch in signature file: %s [was %lld, expected %lld].\n",
+                signature_file.c_str(), (long long)tree_size, (long long)expected);
+        return {};
+    }
+
+    size_t base64_len = 0;
+    if (!EVP_EncodedLength(&base64_len, signature.size())) {
+        fprintf(stderr, "Fail to estimate base64 encoded length. Abort.\n");
+        return {};
+    }
+    std::string encoded_signature;
+    encoded_signature.resize(base64_len);
+    encoded_signature.resize(EVP_EncodeBlock((uint8_t*)encoded_signature.data(),
+                                             (const uint8_t*)signature.data(), signature.size()));
+
+    return {std::move(fd), std::move(encoded_signature)};
+}
+
+// Send install-incremental to the device along with properly configured file descriptors in
+// streaming format. Once connection established, send all fs-verity tree bytes.
+static unique_fd start_install(const std::vector<std::string>& files) {
+    std::vector<std::string> command_args{"package", "install-incremental"};
+
+    // fd's with positions at the beginning of fs-verity
+    std::vector<unique_fd> signature_fds;
+    signature_fds.reserve(files.size());
+    for (int i = 0, size = files.size(); i < size; ++i) {
+        const auto& file = files[i];
+
+        struct stat st;
+        if (stat(file.c_str(), &st)) {
+            fprintf(stderr, "Failed to stat input file %s. Abort.\n", file.c_str());
+            return {};
+        }
+
+        auto [signature_fd, signature] = read_and_encode_signature(st.st_size, file);
+        if (!signature_fd.ok()) {
+            return {};
+        }
+
+        auto file_desc =
+                StringPrintf("%s:%lld:%s:%s", android::base::Basename(file).c_str(),
+                             (long long)st.st_size, std::to_string(i).c_str(), signature.c_str());
+        command_args.push_back(std::move(file_desc));
+
+        signature_fds.push_back(std::move(signature_fd));
+    }
+
+    std::string error;
+    auto connection_fd = unique_fd(send_abb_exec_command(command_args, &error));
+    if (connection_fd < 0) {
+        fprintf(stderr, "Failed to run: %s, error: %s\n",
+                android::base::Join(command_args, " ").c_str(), error.c_str());
+        return {};
+    }
+
+    // Pushing verity trees for all installation files.
+    for (auto&& local_fd : signature_fds) {
+        if (!copy_to_file(local_fd.get(), connection_fd.get())) {
+            fprintf(stderr, "Failed to stream tree bytes: %s. Abort.\n", strerror(errno));
+            return {};
+        }
+    }
+
+    return connection_fd;
+}
+
+}  // namespace
+
+std::optional<Process> install(std::vector<std::string> files) {
+    auto connection_fd = start_install(files);
+    if (connection_fd < 0) {
+        fprintf(stderr, "adb: failed to initiate installation on device.\n");
+        return {};
+    }
+
+    std::string adb_path = android::base::GetExecutablePath();
+
+    auto osh = adb_get_os_handle(connection_fd.get());
+#ifdef _WIN32
+    auto fd_param = std::to_string(reinterpret_cast<intptr_t>(osh));
+#else /* !_WIN32 a.k.a. Unix */
+    auto fd_param = std::to_string(osh);
+#endif
+
+    std::vector<std::string> args(std::move(files));
+    args.insert(args.begin(), {"inc-server", fd_param});
+    auto child = adb_launch_process(adb_path, std::move(args), {connection_fd.get()});
+    if (!child) {
+        fprintf(stderr, "adb: failed to fork: %s\n", strerror(errno));
+        return {};
+    }
+
+    auto killOnExit = [](Process* p) { p->kill(); };
+    std::unique_ptr<Process, decltype(killOnExit)> serverKiller(&child, killOnExit);
+    // TODO: Terminate server process if installation fails.
+    serverKiller.release();
+
+    return child;
+}
+
+}  // namespace incremental
diff --git a/adb/client/incremental.h b/adb/client/incremental.h
new file mode 100644
index 0000000..4b9f6bd
--- /dev/null
+++ b/adb/client/incremental.h
@@ -0,0 +1,30 @@
+/*
+ * 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 "adb_unique_fd.h"
+
+#include <optional>
+#include <string>
+
+#include "sysdeps.h"
+
+namespace incremental {
+
+std::optional<Process> install(std::vector<std::string> files);
+
+}  // namespace incremental
diff --git a/adb/client/incremental_server.cpp b/adb/client/incremental_server.cpp
new file mode 100644
index 0000000..2512d05
--- /dev/null
+++ b/adb/client/incremental_server.cpp
@@ -0,0 +1,536 @@
+/*
+ * 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.
+ */
+
+#define TRACE_TAG INCREMENTAL
+
+#include "incremental_server.h"
+
+#include "adb.h"
+#include "adb_io.h"
+#include "adb_trace.h"
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+#include "sysdeps.h"
+
+#include <android-base/endian.h>
+#include <android-base/strings.h>
+#include <inttypes.h>
+#include <lz4.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <array>
+#include <deque>
+#include <fstream>
+#include <thread>
+#include <type_traits>
+#include <unordered_set>
+
+namespace incremental {
+
+static constexpr int kBlockSize = 4096;
+static constexpr int kCompressedSizeMax = kBlockSize * 0.95;
+static constexpr short kCompressionNone = 0;
+static constexpr short kCompressionLZ4 = 1;
+static constexpr int kCompressBound = std::max(kBlockSize, LZ4_COMPRESSBOUND(kBlockSize));
+static constexpr auto kReadBufferSize = 128 * 1024;
+
+using BlockSize = int16_t;
+using FileId = int16_t;
+using BlockIdx = int32_t;
+using NumBlocks = int32_t;
+using CompressionType = int16_t;
+using RequestType = int16_t;
+using ChunkHeader = int32_t;
+using MagicType = uint32_t;
+
+static constexpr MagicType INCR = 0x494e4352;  // LE INCR
+
+static constexpr RequestType EXIT = 0;
+static constexpr RequestType BLOCK_MISSING = 1;
+static constexpr RequestType PREFETCH = 2;
+
+static constexpr inline int64_t roundDownToBlockOffset(int64_t val) {
+    return val & ~(kBlockSize - 1);
+}
+
+static constexpr inline int64_t roundUpToBlockOffset(int64_t val) {
+    return roundDownToBlockOffset(val + kBlockSize - 1);
+}
+
+static constexpr inline NumBlocks numBytesToNumBlocks(int64_t bytes) {
+    return roundUpToBlockOffset(bytes) / kBlockSize;
+}
+
+static constexpr inline off64_t blockIndexToOffset(BlockIdx blockIdx) {
+    return static_cast<off64_t>(blockIdx) * kBlockSize;
+}
+
+template <typename T>
+static inline constexpr T toBigEndian(T t) {
+    using unsigned_type = std::make_unsigned_t<T>;
+    if constexpr (std::is_same_v<T, int16_t>) {
+        return htobe16(static_cast<unsigned_type>(t));
+    } else if constexpr (std::is_same_v<T, int32_t>) {
+        return htobe32(static_cast<unsigned_type>(t));
+    } else if constexpr (std::is_same_v<T, int64_t>) {
+        return htobe64(static_cast<unsigned_type>(t));
+    } else {
+        return t;
+    }
+}
+
+template <typename T>
+static inline constexpr T readBigEndian(void* data) {
+    using unsigned_type = std::make_unsigned_t<T>;
+    if constexpr (std::is_same_v<T, int16_t>) {
+        return static_cast<T>(be16toh(*reinterpret_cast<unsigned_type*>(data)));
+    } else if constexpr (std::is_same_v<T, int32_t>) {
+        return static_cast<T>(be32toh(*reinterpret_cast<unsigned_type*>(data)));
+    } else if constexpr (std::is_same_v<T, int64_t>) {
+        return static_cast<T>(be64toh(*reinterpret_cast<unsigned_type*>(data)));
+    } else {
+        return T();
+    }
+}
+
+// Received from device
+// !Does not include magic!
+struct RequestCommand {
+    RequestType request_type;  // 2 bytes
+    FileId file_id;            // 2 bytes
+    union {
+        BlockIdx block_idx;
+        NumBlocks num_blocks;
+    };  // 4 bytes
+} __attribute__((packed));
+
+// Placed before actual data bytes of each block
+struct ResponseHeader {
+    FileId file_id;                    // 2 bytes
+    CompressionType compression_type;  // 2 bytes
+    BlockIdx block_idx;                // 4 bytes
+    BlockSize block_size;              // 2 bytes
+} __attribute__((packed));
+
+// Holds streaming state for a file
+class File {
+  public:
+    // Plain file
+    File(const char* filepath, FileId id, int64_t size, unique_fd fd) : File(filepath, id, size) {
+        this->fd_ = std::move(fd);
+    }
+    int64_t ReadBlock(BlockIdx block_idx, void* buf, bool* is_zip_compressed,
+                      std::string* error) const {
+        char* buf_ptr = static_cast<char*>(buf);
+        int64_t bytes_read = -1;
+        const off64_t offsetStart = blockIndexToOffset(block_idx);
+        bytes_read = adb_pread(fd_, &buf_ptr[sizeof(ResponseHeader)], kBlockSize, offsetStart);
+        return bytes_read;
+    }
+
+    const unique_fd& RawFd() const { return fd_; }
+
+    std::vector<bool> sentBlocks;
+    NumBlocks sentBlocksCount = 0;
+
+    const char* const filepath;
+    const FileId id;
+    const int64_t size;
+
+  private:
+    File(const char* filepath, FileId id, int64_t size) : filepath(filepath), id(id), size(size) {
+        sentBlocks.resize(numBytesToNumBlocks(size));
+    }
+    unique_fd fd_;
+};
+
+class IncrementalServer {
+  public:
+    IncrementalServer(unique_fd fd, std::vector<File> files)
+        : adb_fd_(std::move(fd)), files_(std::move(files)) {
+        buffer_.reserve(kReadBufferSize);
+    }
+
+    bool Serve();
+
+  private:
+    struct PrefetchState {
+        const File* file;
+        BlockIdx overallIndex = 0;
+        BlockIdx overallEnd = 0;
+
+        PrefetchState(const File& f) : file(&f), overallEnd((BlockIdx)f.sentBlocks.size()) {}
+        PrefetchState(const File& f, BlockIdx start, int count)
+            : file(&f),
+              overallIndex(start),
+              overallEnd(std::min<BlockIdx>(start + count, f.sentBlocks.size())) {}
+
+        bool done() const { return overallIndex >= overallEnd; }
+    };
+
+    bool SkipToRequest(void* buffer, size_t* size, bool blocking);
+    std::optional<RequestCommand> ReadRequest(bool blocking);
+
+    void erase_buffer_head(int count) { buffer_.erase(buffer_.begin(), buffer_.begin() + count); }
+
+    enum class SendResult { Sent, Skipped, Error };
+    SendResult SendBlock(FileId fileId, BlockIdx blockIdx, bool flush = false);
+    bool SendDone();
+    void RunPrefetching();
+
+    void Send(const void* data, size_t size, bool flush);
+    void Flush();
+    using TimePoint = decltype(std::chrono::high_resolution_clock::now());
+    bool Exit(std::optional<TimePoint> startTime, int missesCount, int missesSent);
+
+    unique_fd const adb_fd_;
+    std::vector<File> files_;
+
+    // Incoming data buffer.
+    std::vector<char> buffer_;
+
+    std::deque<PrefetchState> prefetches_;
+    int compressed_ = 0, uncompressed_ = 0;
+    long long sentSize_ = 0;
+
+    std::vector<char> pendingBlocks_;
+};
+
+bool IncrementalServer::SkipToRequest(void* buffer, size_t* size, bool blocking) {
+    while (true) {
+        // Looking for INCR magic.
+        bool magic_found = false;
+        int bcur = 0;
+        for (int bsize = buffer_.size(); bcur + 4 < bsize; ++bcur) {
+            uint32_t magic = be32toh(*(uint32_t*)(buffer_.data() + bcur));
+            if (magic == INCR) {
+                magic_found = true;
+                break;
+            }
+        }
+
+        if (bcur > 0) {
+            // Stream the rest to stderr.
+            fprintf(stderr, "%.*s", bcur, buffer_.data());
+            erase_buffer_head(bcur);
+        }
+
+        if (magic_found && buffer_.size() >= *size + sizeof(INCR)) {
+            // fine, return
+            memcpy(buffer, buffer_.data() + sizeof(INCR), *size);
+            erase_buffer_head(*size + sizeof(INCR));
+            return true;
+        }
+
+        adb_pollfd pfd = {adb_fd_.get(), POLLIN, 0};
+        auto res = adb_poll(&pfd, 1, blocking ? -1 : 0);
+        if (res != 1) {
+            if (res < 0) {
+                fprintf(stderr, "Failed to poll: %s\n", strerror(errno));
+                return false;
+            }
+            *size = 0;
+            return true;
+        }
+
+        auto bsize = buffer_.size();
+        buffer_.resize(kReadBufferSize);
+        int r = adb_read(adb_fd_, buffer_.data() + bsize, kReadBufferSize - bsize);
+        if (r > 0) {
+            buffer_.resize(bsize + r);
+            continue;
+        }
+
+        if (r == -1) {
+            fprintf(stderr, "Failed to read from fd %d: %d. Exit\n", adb_fd_.get(), errno);
+            return false;
+        }
+
+        // socket is closed
+        return false;
+    }
+}
+
+std::optional<RequestCommand> IncrementalServer::ReadRequest(bool blocking) {
+    uint8_t commandBuf[sizeof(RequestCommand)];
+    auto size = sizeof(commandBuf);
+    if (!SkipToRequest(&commandBuf, &size, blocking)) {
+        return {{EXIT}};
+    }
+    if (size < sizeof(RequestCommand)) {
+        return {};
+    }
+    RequestCommand request;
+    request.request_type = readBigEndian<RequestType>(&commandBuf[0]);
+    request.file_id = readBigEndian<FileId>(&commandBuf[2]);
+    request.block_idx = readBigEndian<BlockIdx>(&commandBuf[4]);
+    return request;
+}
+
+auto IncrementalServer::SendBlock(FileId fileId, BlockIdx blockIdx, bool flush) -> SendResult {
+    auto& file = files_[fileId];
+    if (blockIdx >= static_cast<long>(file.sentBlocks.size())) {
+        fprintf(stderr, "Failed to read file %s at block %" PRId32 " (past end).\n", file.filepath,
+                blockIdx);
+        return SendResult::Error;
+    }
+    if (file.sentBlocks[blockIdx]) {
+        return SendResult::Skipped;
+    }
+    std::string error;
+    char raw[sizeof(ResponseHeader) + kBlockSize];
+    bool isZipCompressed = false;
+    const int64_t bytesRead = file.ReadBlock(blockIdx, &raw, &isZipCompressed, &error);
+    if (bytesRead < 0) {
+        fprintf(stderr, "Failed to get data for %s at blockIdx=%d (%s).\n", file.filepath, blockIdx,
+                error.c_str());
+        return SendResult::Error;
+    }
+
+    ResponseHeader* header = nullptr;
+    char data[sizeof(ResponseHeader) + kCompressBound];
+    char* compressed = data + sizeof(*header);
+    int16_t compressedSize = 0;
+    if (!isZipCompressed) {
+        compressedSize =
+                LZ4_compress_default(raw + sizeof(*header), compressed, bytesRead, kCompressBound);
+    }
+    int16_t blockSize;
+    if (compressedSize > 0 && compressedSize < kCompressedSizeMax) {
+        ++compressed_;
+        blockSize = compressedSize;
+        header = reinterpret_cast<ResponseHeader*>(data);
+        header->compression_type = toBigEndian(kCompressionLZ4);
+    } else {
+        ++uncompressed_;
+        blockSize = bytesRead;
+        header = reinterpret_cast<ResponseHeader*>(raw);
+        header->compression_type = toBigEndian(kCompressionNone);
+    }
+
+    header->file_id = toBigEndian(fileId);
+    header->block_size = toBigEndian(blockSize);
+    header->block_idx = toBigEndian(blockIdx);
+
+    file.sentBlocks[blockIdx] = true;
+    file.sentBlocksCount += 1;
+    Send(header, sizeof(*header) + blockSize, flush);
+    return SendResult::Sent;
+}
+
+bool IncrementalServer::SendDone() {
+    ResponseHeader header;
+    header.file_id = -1;
+    header.compression_type = 0;
+    header.block_idx = 0;
+    header.block_size = 0;
+    Send(&header, sizeof(header), true);
+    return true;
+}
+
+void IncrementalServer::RunPrefetching() {
+    constexpr auto kPrefetchBlocksPerIteration = 128;
+
+    int blocksToSend = kPrefetchBlocksPerIteration;
+    while (!prefetches_.empty() && blocksToSend > 0) {
+        auto& prefetch = prefetches_.front();
+        const auto& file = *prefetch.file;
+        for (auto& i = prefetch.overallIndex; blocksToSend > 0 && i < prefetch.overallEnd; ++i) {
+            if (auto res = SendBlock(file.id, i); res == SendResult::Sent) {
+                --blocksToSend;
+            } else if (res == SendResult::Error) {
+                fprintf(stderr, "Failed to send block %" PRId32 "\n", i);
+            }
+        }
+        if (prefetch.done()) {
+            prefetches_.pop_front();
+        }
+    }
+}
+
+void IncrementalServer::Send(const void* data, size_t size, bool flush) {
+    constexpr auto kChunkFlushSize = 31 * kBlockSize;
+
+    if (pendingBlocks_.empty()) {
+        pendingBlocks_.resize(sizeof(ChunkHeader));
+    }
+    pendingBlocks_.insert(pendingBlocks_.end(), static_cast<const char*>(data),
+                          static_cast<const char*>(data) + size);
+    if (flush || pendingBlocks_.size() > kChunkFlushSize) {
+        Flush();
+    }
+}
+
+void IncrementalServer::Flush() {
+    if (pendingBlocks_.empty()) {
+        return;
+    }
+
+    *(ChunkHeader*)pendingBlocks_.data() =
+            toBigEndian<int32_t>(pendingBlocks_.size() - sizeof(ChunkHeader));
+    if (!WriteFdExactly(adb_fd_, pendingBlocks_.data(), pendingBlocks_.size())) {
+        fprintf(stderr, "Failed to write %d bytes\n", int(pendingBlocks_.size()));
+    }
+    sentSize_ += pendingBlocks_.size();
+    pendingBlocks_.clear();
+}
+
+bool IncrementalServer::Exit(std::optional<TimePoint> startTime, int missesCount, int missesSent) {
+    using namespace std::chrono;
+    auto endTime = high_resolution_clock::now();
+    fprintf(stderr,
+            "Connection failed or received exit command. Exit.\n"
+            "Misses: %d, of those unique: %d; sent compressed: %d, uncompressed: "
+            "%d, mb: %.3f\n"
+            "Total time taken: %.3fms\n",
+            missesCount, missesSent, compressed_, uncompressed_, sentSize_ / 1024.0 / 1024.0,
+            duration_cast<microseconds>(endTime - (startTime ? *startTime : endTime)).count() /
+                    1000.0);
+    return true;
+}
+
+bool IncrementalServer::Serve() {
+    // Initial handshake to verify connection is still alive
+    if (!SendOkay(adb_fd_)) {
+        fprintf(stderr, "Connection is dead. Abort.\n");
+        return false;
+    }
+
+    std::unordered_set<FileId> prefetchedFiles;
+    bool doneSent = false;
+    int missesCount = 0;
+    int missesSent = 0;
+
+    using namespace std::chrono;
+    std::optional<TimePoint> startTime;
+
+    while (true) {
+        if (!doneSent && prefetches_.empty() &&
+            std::all_of(files_.begin(), files_.end(), [](const File& f) {
+                return f.sentBlocksCount == NumBlocks(f.sentBlocks.size());
+            })) {
+            fprintf(stdout, "All files should be loaded. Notifying the device.\n");
+            SendDone();
+            doneSent = true;
+        }
+
+        const bool blocking = prefetches_.empty();
+        if (blocking) {
+            // We've no idea how long the blocking call is, so let's flush whatever is still unsent.
+            Flush();
+        }
+        auto request = ReadRequest(blocking);
+
+        if (!startTime) {
+            startTime = high_resolution_clock::now();
+        }
+
+        if (request) {
+            FileId fileId = request->file_id;
+            BlockIdx blockIdx = request->block_idx;
+
+            switch (request->request_type) {
+                case EXIT: {
+                    // Stop everything.
+                    return Exit(startTime, missesCount, missesSent);
+                }
+                case BLOCK_MISSING: {
+                    ++missesCount;
+                    // Sends one single block ASAP.
+                    if (fileId < 0 || fileId >= (FileId)files_.size() || blockIdx < 0 ||
+                        blockIdx >= (BlockIdx)files_[fileId].sentBlocks.size()) {
+                        fprintf(stderr,
+                                "Received invalid data request for file_id %" PRId16
+                                " block_idx %" PRId32 ".\n",
+                                fileId, blockIdx);
+                        break;
+                    }
+                    // fprintf(stderr, "\treading file %d block %04d\n", (int)fileId,
+                    // (int)blockIdx);
+                    if (auto res = SendBlock(fileId, blockIdx, true); res == SendResult::Error) {
+                        fprintf(stderr, "Failed to send block %" PRId32 ".\n", blockIdx);
+                    } else if (res == SendResult::Sent) {
+                        ++missesSent;
+                        // Make sure we send more pages from this place onward, in case if the OS is
+                        // reading a bigger block.
+                        prefetches_.emplace_front(files_[fileId], blockIdx + 1, 7);
+                    }
+                    break;
+                }
+                case PREFETCH: {
+                    // Start prefetching for a file
+                    if (fileId < 0) {
+                        fprintf(stderr,
+                                "Received invalid prefetch request for file_id %" PRId16 "\n",
+                                fileId);
+                        break;
+                    }
+                    if (!prefetchedFiles.insert(fileId).second) {
+                        fprintf(stderr,
+                                "Received duplicate prefetch request for file_id %" PRId16 "\n",
+                                fileId);
+                        break;
+                    }
+                    D("Received prefetch request for file_id %" PRId16 ".\n", fileId);
+                    prefetches_.emplace_back(files_[fileId]);
+                    break;
+                }
+                default:
+                    fprintf(stderr, "Invalid request %" PRId16 ",%" PRId16 ",%" PRId32 ".\n",
+                            request->request_type, fileId, blockIdx);
+                    break;
+            }
+        }
+
+        RunPrefetching();
+    }
+}
+
+bool serve(int adb_fd, int argc, const char** argv) {
+    auto connection_fd = unique_fd(adb_fd);
+    if (argc <= 0) {
+        error_exit("inc-server: must specify at least one file.");
+    }
+
+    std::vector<File> files;
+    files.reserve(argc);
+    for (int i = 0; i < argc; ++i) {
+        auto filepath = argv[i];
+
+        struct stat st;
+        if (stat(filepath, &st)) {
+            fprintf(stderr, "Failed to stat input file %s. Abort.\n", filepath);
+            return {};
+        }
+
+        unique_fd fd(adb_open(filepath, O_RDONLY));
+        if (fd < 0) {
+            error_exit("inc-server: failed to open file '%s'.", filepath);
+        }
+        files.emplace_back(filepath, i, st.st_size, std::move(fd));
+    }
+
+    IncrementalServer server(std::move(connection_fd), std::move(files));
+    printf("Serving...\n");
+    fclose(stdin);
+    fclose(stdout);
+    return server.Serve();
+}
+
+}  // namespace incremental
diff --git a/adb/client/incremental_server.h b/adb/client/incremental_server.h
new file mode 100644
index 0000000..53f011e
--- /dev/null
+++ b/adb/client/incremental_server.h
@@ -0,0 +1,26 @@
+/*
+ * 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
+
+namespace incremental {
+
+// Expecting arguments like:
+// {FILE1 FILE2 ...}
+// Where FILE* are files to serve.
+bool serve(int adbFd, int argc, const char** argv);
+
+}  // namespace incremental
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index e5ffe4c..a85a18c 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -35,6 +35,7 @@
 #include "adb_client.h"
 #include "adb_listeners.h"
 #include "adb_utils.h"
+#include "adb_wifi.h"
 #include "commandline.h"
 #include "sysdeps/chrono.h"
 #include "transport.h"
@@ -118,6 +119,7 @@
     init_transport_registration();
     init_reconnect_handler();
 
+    adb_wifi_init();
     if (!getenv("ADB_MDNS") || strcmp(getenv("ADB_MDNS"), "0") != 0) {
         init_mdns_transport_discovery();
     }
diff --git a/adb/client/pairing/pairing_client.cpp b/adb/client/pairing/pairing_client.cpp
new file mode 100644
index 0000000..2f878bf
--- /dev/null
+++ b/adb/client/pairing/pairing_client.cpp
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "client/pairing/pairing_client.h"
+
+#include <atomic>
+#include <iomanip>
+#include <mutex>
+#include <sstream>
+#include <thread>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/parsenetaddress.h>
+#include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+#include "sysdeps.h"
+
+namespace adbwifi {
+namespace pairing {
+
+using android::base::unique_fd;
+
+namespace {
+
+struct ConnectionDeleter {
+    void operator()(PairingConnectionCtx* p) { pairing_connection_destroy(p); }
+};  // ConnectionDeleter
+using ConnectionPtr = std::unique_ptr<PairingConnectionCtx, ConnectionDeleter>;
+
+class PairingClientImpl : public PairingClient {
+  public:
+    virtual ~PairingClientImpl();
+
+    explicit PairingClientImpl(const Data& pswd, const PeerInfo& peer_info, const Data& cert,
+                               const Data& priv_key);
+
+    // Starts the pairing client. This call is non-blocking. Upon pairing
+    // completion, |cb| will be called with the PeerInfo on success,
+    // or an empty value on failure.
+    //
+    // Returns true if PairingClient was successfully started. Otherwise,
+    // return false.
+    virtual bool Start(std::string_view ip_addr, pairing_client_result_cb cb,
+                       void* opaque) override;
+
+    static void OnPairingResult(const PeerInfo* peer_info, int fd, void* opaque);
+
+  private:
+    // Setup and start the PairingConnection
+    bool StartConnection();
+
+    enum class State {
+        Ready,
+        Running,
+        Stopped,
+    };
+
+    State state_ = State::Ready;
+    Data pswd_;
+    PeerInfo peer_info_;
+    Data cert_;
+    Data priv_key_;
+    std::string host_;
+    int port_;
+
+    ConnectionPtr connection_;
+    pairing_client_result_cb cb_;
+    void* opaque_ = nullptr;
+};  // PairingClientImpl
+
+PairingClientImpl::PairingClientImpl(const Data& pswd, const PeerInfo& peer_info, const Data& cert,
+                                     const Data& priv_key)
+    : pswd_(pswd), peer_info_(peer_info), cert_(cert), priv_key_(priv_key) {
+    CHECK(!pswd_.empty() && !cert_.empty() && !priv_key_.empty());
+
+    state_ = State::Ready;
+}
+
+PairingClientImpl::~PairingClientImpl() {
+    // Make sure to kill the PairingConnection before terminating the fdevent
+    // looper.
+    if (connection_ != nullptr) {
+        connection_.reset();
+    }
+}
+
+bool PairingClientImpl::Start(std::string_view ip_addr, pairing_client_result_cb cb, void* opaque) {
+    CHECK(!ip_addr.empty());
+    cb_ = cb;
+    opaque_ = opaque;
+
+    if (state_ != State::Ready) {
+        LOG(ERROR) << "PairingClient already running or finished";
+        return false;
+    }
+
+    // Try to parse the host address
+    std::string err;
+    CHECK(android::base::ParseNetAddress(std::string(ip_addr), &host_, &port_, nullptr, &err));
+    CHECK(port_ > 0 && port_ <= 65535);
+
+    if (!StartConnection()) {
+        LOG(ERROR) << "Unable to start PairingClient connection";
+        state_ = State::Stopped;
+        return false;
+    }
+
+    state_ = State::Running;
+    return true;
+}
+
+bool PairingClientImpl::StartConnection() {
+    std::string err;
+    const int timeout = 10;  // seconds
+    unique_fd fd(network_connect(host_, port_, SOCK_STREAM, timeout, &err));
+    if (fd.get() == -1) {
+        LOG(ERROR) << "Failed to start pairing connection client [" << err << "]";
+        return false;
+    }
+    int off = 1;
+    adb_setsockopt(fd.get(), IPPROTO_TCP, TCP_NODELAY, &off, sizeof(off));
+
+    connection_ = ConnectionPtr(
+            pairing_connection_client_new(pswd_.data(), pswd_.size(), &peer_info_, cert_.data(),
+                                          cert_.size(), priv_key_.data(), priv_key_.size()));
+    CHECK(connection_);
+
+    if (!pairing_connection_start(connection_.get(), fd.release(), OnPairingResult, this)) {
+        LOG(ERROR) << "PairingClient failed to start the PairingConnection";
+        state_ = State::Stopped;
+        return false;
+    }
+
+    return true;
+}
+
+// static
+void PairingClientImpl::OnPairingResult(const PeerInfo* peer_info, int /* fd */, void* opaque) {
+    auto* p = reinterpret_cast<PairingClientImpl*>(opaque);
+    p->cb_(peer_info, p->opaque_);
+}
+
+}  // namespace
+
+// static
+std::unique_ptr<PairingClient> PairingClient::Create(const Data& pswd, const PeerInfo& peer_info,
+                                                     const Data& cert, const Data& priv_key) {
+    CHECK(!pswd.empty());
+    CHECK(!cert.empty());
+    CHECK(!priv_key.empty());
+
+    return std::unique_ptr<PairingClient>(new PairingClientImpl(pswd, peer_info, cert, priv_key));
+}
+
+}  // namespace pairing
+}  // namespace adbwifi
diff --git a/adb/client/pairing/pairing_client.h b/adb/client/pairing/pairing_client.h
new file mode 100644
index 0000000..dbd72a5
--- /dev/null
+++ b/adb/client/pairing/pairing_client.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <functional>
+#include <memory>
+#include <string_view>
+#include <vector>
+
+#include "adb/pairing/pairing_connection.h"
+
+namespace adbwifi {
+namespace pairing {
+
+typedef void (*pairing_client_result_cb)(const PeerInfo*, void*);
+
+// PairingClient is the client side of the PairingConnection protocol. It will
+// attempt to connect to a PairingServer specified at |host| and |port|, and
+// allocate a new PairingConnection for processing.
+//
+// See pairing_connection_test.cpp for example usage.
+//
+class PairingClient {
+  public:
+    using Data = std::vector<uint8_t>;
+
+    virtual ~PairingClient() = default;
+
+    // Starts the pairing client. This call is non-blocking. Upon completion,
+    // if the pairing was successful, then |cb| will be called with the PeerInfo
+    // containing the info of the trusted peer. Otherwise, |cb| will be
+    // called with an empty value. Start can only be called once in the lifetime
+    // of this object.
+    //
+    // Returns true if PairingClient was successfully started. Otherwise,
+    // returns false.
+    virtual bool Start(std::string_view ip_addr, pairing_client_result_cb cb, void* opaque) = 0;
+
+    // Creates a new PairingClient instance. May return null if unable
+    // to create an instance. |pswd|, |certificate|, |priv_key| and
+    // |ip_addr| cannot be empty. |peer_info| must contain non-empty strings for
+    // the guid and name fields.
+    static std::unique_ptr<PairingClient> Create(const Data& pswd, const PeerInfo& peer_info,
+                                                 const Data& certificate, const Data& priv_key);
+
+  protected:
+    PairingClient() = default;
+};  // class PairingClient
+
+}  // namespace pairing
+}  // namespace adbwifi
diff --git a/adb/client/pairing/tests/pairing_connection_test.cpp b/adb/client/pairing/tests/pairing_connection_test.cpp
new file mode 100644
index 0000000..c69c1c2
--- /dev/null
+++ b/adb/client/pairing/tests/pairing_connection_test.cpp
@@ -0,0 +1,473 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AdbWifiPairingConnectionTest"
+
+#include <condition_variable>
+#include <mutex>
+#include <thread>
+
+#include <adbwifi/pairing/pairing_server.h>
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+
+#include "adb/client/pairing/tests/pairing_client.h"
+
+namespace adbwifi {
+namespace pairing {
+
+static const std::string kTestServerCert =
+        "-----BEGIN CERTIFICATE-----\n"
+        "MIIBljCCAT2gAwIBAgIBATAKBggqhkjOPQQDAjAzMQswCQYDVQQGEwJVUzEQMA4G\n"
+        "A1UECgwHQW5kcm9pZDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTE5MTEwNzAyMDkx\n"
+        "NVoXDTI5MTEwNDAyMDkxNVowMzELMAkGA1UEBhMCVVMxEDAOBgNVBAoMB0FuZHJv\n"
+        "aWQxEjAQBgNVBAMMCWxvY2FsaG9zdDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA\n"
+        "BCXRovy3RhtK0Khle48vUmkcuI0OF7K8o9sVPE4oVnp24l+cCYr3BtrgifoHPgj4\n"
+        "vq7n105qzK7ngBHH+LBmYIijQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/\n"
+        "BAQDAgGGMB0GA1UdDgQWBBQi4eskzqVG3SCX2CwJF/aTZqUcuTAKBggqhkjOPQQD\n"
+        "AgNHADBEAiBPYvLOCIvPDtq3vMF7A2z7t7JfcCmbC7g8ftEVJucJBwIgepf+XjTb\n"
+        "L7RCE16p7iVkpHUrWAOl7zDxqD+jaji5MkQ=\n"
+        "-----END CERTIFICATE-----\n";
+
+static const std::string kTestServerPrivKey =
+        "-----BEGIN PRIVATE KEY-----\n"
+        "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgSCaskWPtutIgh8uQ\n"
+        "UBH6ZIea5Kxm7m6kkGNkd8FYPSOhRANCAAQl0aL8t0YbStCoZXuPL1JpHLiNDhey\n"
+        "vKPbFTxOKFZ6duJfnAmK9wba4In6Bz4I+L6u59dOasyu54ARx/iwZmCI\n"
+        "-----END PRIVATE KEY-----\n";
+
+static const std::string kTestClientCert =
+        "-----BEGIN CERTIFICATE-----\n"
+        "MIIBlzCCAT2gAwIBAgIBATAKBggqhkjOPQQDAjAzMQswCQYDVQQGEwJVUzEQMA4G\n"
+        "A1UECgwHQW5kcm9pZDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTE5MTEwOTAxNTAy\n"
+        "OFoXDTI5MTEwNjAxNTAyOFowMzELMAkGA1UEBhMCVVMxEDAOBgNVBAoMB0FuZHJv\n"
+        "aWQxEjAQBgNVBAMMCWxvY2FsaG9zdDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA\n"
+        "BGW+RuoEIzbt42zAuZzbXaC0bvh8n4OLFDnqkkW6kWA43GYg/mUMVc9vg/nuxyuM\n"
+        "aT0KqbTaLhm+NjCXVRnxBrajQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/\n"
+        "BAQDAgGGMB0GA1UdDgQWBBTjCaC8/NXgdBz9WlMVCNwhx7jn0jAKBggqhkjOPQQD\n"
+        "AgNIADBFAiB/xp2boj7b1KK2saS6BL59deo/TvfgZ+u8HPq4k4VP3gIhAMXswp9W\n"
+        "XdlziccQdj+0KpbUojDKeHOr4fIj/+LxsWPa\n"
+        "-----END CERTIFICATE-----\n";
+
+static const std::string kTestClientPrivKey =
+        "-----BEGIN PRIVATE KEY-----\n"
+        "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgFw/CWY1f6TSB70AF\n"
+        "yVe8n6QdYFu8HW5t/tij2SrXx42hRANCAARlvkbqBCM27eNswLmc212gtG74fJ+D\n"
+        "ixQ56pJFupFgONxmIP5lDFXPb4P57scrjGk9Cqm02i4ZvjYwl1UZ8Qa2\n"
+        "-----END PRIVATE KEY-----\n";
+
+class AdbWifiPairingConnectionTest : public testing::Test {
+  protected:
+    virtual void SetUp() override {}
+
+    virtual void TearDown() override {}
+
+    void initPairing(const std::vector<uint8_t> server_pswd,
+                     const std::vector<uint8_t> client_pswd) {
+        std::vector<uint8_t> cert;
+        std::vector<uint8_t> key;
+        // Include the null-byte as well.
+        cert.assign(reinterpret_cast<const uint8_t*>(kTestServerCert.data()),
+                    reinterpret_cast<const uint8_t*>(kTestServerCert.data()) +
+                            kTestServerCert.size() + 1);
+        key.assign(reinterpret_cast<const uint8_t*>(kTestServerPrivKey.data()),
+                   reinterpret_cast<const uint8_t*>(kTestServerPrivKey.data()) +
+                           kTestServerPrivKey.size() + 1);
+        server_ = PairingServer::create(server_pswd, server_info_, cert, key, kDefaultPairingPort);
+        cert.assign(reinterpret_cast<const uint8_t*>(kTestClientCert.data()),
+                    reinterpret_cast<const uint8_t*>(kTestClientCert.data()) +
+                            kTestClientCert.size() + 1);
+        key.assign(reinterpret_cast<const uint8_t*>(kTestClientPrivKey.data()),
+                   reinterpret_cast<const uint8_t*>(kTestClientPrivKey.data()) +
+                           kTestClientPrivKey.size() + 1);
+        client_ = PairingClient::create(client_pswd, client_info_, cert, key, "127.0.0.1");
+    }
+
+    std::unique_ptr<PairingServer> createServer(const std::vector<uint8_t> pswd) {
+        std::vector<uint8_t> cert;
+        std::vector<uint8_t> key;
+        // Include the null-byte as well.
+        cert.assign(reinterpret_cast<const uint8_t*>(kTestServerCert.data()),
+                    reinterpret_cast<const uint8_t*>(kTestServerCert.data()) +
+                            kTestServerCert.size() + 1);
+        key.assign(reinterpret_cast<const uint8_t*>(kTestServerPrivKey.data()),
+                   reinterpret_cast<const uint8_t*>(kTestServerPrivKey.data()) +
+                           kTestServerPrivKey.size() + 1);
+        return PairingServer::create(pswd, server_info_, cert, key, kDefaultPairingPort);
+    }
+
+    std::unique_ptr<PairingClient> createClient(const std::vector<uint8_t> pswd) {
+        std::vector<uint8_t> cert;
+        std::vector<uint8_t> key;
+        // Include the null-byte as well.
+        cert.assign(reinterpret_cast<const uint8_t*>(kTestClientCert.data()),
+                    reinterpret_cast<const uint8_t*>(kTestClientCert.data()) +
+                            kTestClientCert.size() + 1);
+        key.assign(reinterpret_cast<const uint8_t*>(kTestClientPrivKey.data()),
+                   reinterpret_cast<const uint8_t*>(kTestClientPrivKey.data()) +
+                           kTestClientPrivKey.size() + 1);
+        return PairingClient::create(pswd, client_info_, cert, key, "127.0.0.1");
+    }
+
+    std::unique_ptr<PairingServer> server_;
+    const PeerInfo server_info_ = {
+            .name = "my_server_name",
+            .guid = "my_server_guid",
+    };
+    std::unique_ptr<PairingClient> client_;
+    const PeerInfo client_info_ = {
+            .name = "my_client_name",
+            .guid = "my_client_guid",
+    };
+};
+
+TEST_F(AdbWifiPairingConnectionTest, ServerCreation) {
+    // All parameters bad
+    auto server = PairingServer::create({}, {}, {}, {}, -1);
+    EXPECT_EQ(nullptr, server);
+    // Bad password
+    server = PairingServer::create({}, server_info_, {0x01}, {0x01}, -1);
+    EXPECT_EQ(nullptr, server);
+    // Bad peer_info
+    server = PairingServer::create({0x01}, {}, {0x01}, {0x01}, -1);
+    EXPECT_EQ(nullptr, server);
+    // Bad certificate
+    server = PairingServer::create({0x01}, server_info_, {}, {0x01}, -1);
+    EXPECT_EQ(nullptr, server);
+    // Bad private key
+    server = PairingServer::create({0x01}, server_info_, {0x01}, {}, -1);
+    EXPECT_EQ(nullptr, server);
+    // Bad port
+    server = PairingServer::create({0x01}, server_info_, {0x01}, {0x01}, -1);
+    EXPECT_EQ(nullptr, server);
+    // Valid params
+    server = PairingServer::create({0x01}, server_info_, {0x01}, {0x01}, 7776);
+    EXPECT_NE(nullptr, server);
+}
+
+TEST_F(AdbWifiPairingConnectionTest, ClientCreation) {
+    // All parameters bad
+    auto client = PairingClient::create({}, client_info_, {}, {}, "");
+    EXPECT_EQ(nullptr, client);
+    // Bad password
+    client = PairingClient::create({}, client_info_, {0x01}, {0x01}, "127.0.0.1");
+    EXPECT_EQ(nullptr, client);
+    // Bad peer_info
+    client = PairingClient::create({0x01}, {}, {0x01}, {0x01}, "127.0.0.1");
+    EXPECT_EQ(nullptr, client);
+    // Bad certificate
+    client = PairingClient::create({0x01}, client_info_, {}, {0x01}, "127.0.0.1");
+    EXPECT_EQ(nullptr, client);
+    // Bad private key
+    client = PairingClient::create({0x01}, client_info_, {0x01}, {}, "127.0.0.1");
+    EXPECT_EQ(nullptr, client);
+    // Bad ip address
+    client = PairingClient::create({0x01}, client_info_, {0x01}, {0x01}, "");
+    EXPECT_EQ(nullptr, client);
+    // Valid params
+    client = PairingClient::create({0x01}, client_info_, {0x01}, {0x01}, "127.0.0.1");
+    EXPECT_NE(nullptr, client);
+}
+
+TEST_F(AdbWifiPairingConnectionTest, SmokeValidPairing) {
+    std::vector<uint8_t> pswd{0x01, 0x03, 0x05, 0x07};
+    initPairing(pswd, pswd);
+
+    // Start the server first, to open the port for connections
+    std::mutex server_mutex;
+    std::condition_variable server_cv;
+    std::unique_lock<std::mutex> server_lock(server_mutex);
+
+    auto server_callback = [&](const PeerInfo* peer_info, const std::vector<uint8_t>* cert,
+                               void* opaque) {
+        ASSERT_NE(nullptr, peer_info);
+        ASSERT_NE(nullptr, cert);
+        EXPECT_FALSE(cert->empty());
+        EXPECT_EQ(nullptr, opaque);
+
+        // Verify the peer_info and cert
+        ASSERT_EQ(strlen(peer_info->name), strlen(client_info_.name));
+        EXPECT_EQ(::memcmp(peer_info->name, client_info_.name, strlen(client_info_.name)), 0);
+        ASSERT_EQ(strlen(peer_info->guid), strlen(client_info_.guid));
+        EXPECT_EQ(::memcmp(peer_info->guid, client_info_.guid, strlen(client_info_.guid)), 0);
+        ASSERT_EQ(cert->size(), kTestClientCert.size() + 1);
+        EXPECT_EQ(::memcmp(cert->data(), kTestClientCert.data(), kTestClientCert.size() + 1), 0);
+
+        std::lock_guard<std::mutex> lock(server_mutex);
+        server_cv.notify_one();
+    };
+    ASSERT_TRUE(server_->start(server_callback, nullptr));
+
+    // Start the client
+    bool got_valid_pairing = false;
+    std::mutex client_mutex;
+    std::condition_variable client_cv;
+    std::unique_lock<std::mutex> client_lock(client_mutex);
+    auto client_callback = [&](const PeerInfo* peer_info, const std::vector<uint8_t>* cert,
+                               void* opaque) {
+        ASSERT_NE(nullptr, peer_info);
+        ASSERT_NE(nullptr, cert);
+        EXPECT_FALSE(cert->empty());
+        EXPECT_EQ(nullptr, opaque);
+
+        // Verify the peer_info and cert
+        ASSERT_EQ(strlen(peer_info->name), strlen(server_info_.name));
+        EXPECT_EQ(::memcmp(peer_info->name, server_info_.name, strlen(server_info_.name)), 0);
+        ASSERT_EQ(strlen(peer_info->guid), strlen(server_info_.guid));
+        EXPECT_EQ(::memcmp(peer_info->guid, server_info_.guid, strlen(server_info_.guid)), 0);
+        ASSERT_EQ(cert->size(), kTestServerCert.size() + 1);
+        EXPECT_EQ(::memcmp(cert->data(), kTestServerCert.data(), kTestServerCert.size() + 1), 0);
+
+        got_valid_pairing = (peer_info != nullptr && cert != nullptr && !cert->empty());
+        std::lock_guard<std::mutex> lock(client_mutex);
+        client_cv.notify_one();
+    };
+    ASSERT_TRUE(client_->start(client_callback, nullptr));
+    client_cv.wait(client_lock);
+
+    // Kill server if the pairing failed, since server only shuts down when
+    // it gets a valid pairing.
+    if (!got_valid_pairing) {
+        server_lock.unlock();
+        server_.reset();
+    } else {
+        server_cv.wait(server_lock);
+    }
+}
+
+TEST_F(AdbWifiPairingConnectionTest, CancelPairing) {
+    std::vector<uint8_t> pswd{0x01, 0x03, 0x05, 0x07};
+    std::vector<uint8_t> pswd2{0x01, 0x03, 0x05, 0x06};
+    initPairing(pswd, pswd2);
+
+    // Start the server first, to open the port for connections
+    std::mutex server_mutex;
+    std::condition_variable server_cv;
+    std::unique_lock<std::mutex> server_lock(server_mutex);
+
+    bool server_got_valid_pairing = true;
+    auto server_callback = [&](const PeerInfo* peer_info, const std::vector<uint8_t>* cert,
+                               void* opaque) {
+        // Pairing will be cancelled, which should initiate this callback with
+        // empty values.
+        ASSERT_EQ(nullptr, peer_info);
+        ASSERT_EQ(nullptr, cert);
+        EXPECT_EQ(nullptr, opaque);
+        std::lock_guard<std::mutex> lock(server_mutex);
+        server_cv.notify_one();
+        server_got_valid_pairing = false;
+    };
+    ASSERT_TRUE(server_->start(server_callback, nullptr));
+
+    // Start the client (should fail because of different passwords).
+    bool got_valid_pairing = false;
+    std::mutex client_mutex;
+    std::condition_variable client_cv;
+    std::unique_lock<std::mutex> client_lock(client_mutex);
+    auto client_callback = [&](const PeerInfo* peer_info, const std::vector<uint8_t>* cert,
+                               void* opaque) {
+        ASSERT_EQ(nullptr, peer_info);
+        ASSERT_EQ(nullptr, cert);
+        EXPECT_EQ(nullptr, opaque);
+
+        got_valid_pairing = (peer_info != nullptr && cert != nullptr && !cert->empty());
+        std::lock_guard<std::mutex> lock(client_mutex);
+        client_cv.notify_one();
+    };
+    ASSERT_TRUE(client_->start(client_callback, nullptr));
+    client_cv.wait(client_lock);
+
+    server_lock.unlock();
+    // This should trigger the callback to be on the same thread.
+    server_.reset();
+    EXPECT_FALSE(server_got_valid_pairing);
+}
+
+TEST_F(AdbWifiPairingConnectionTest, MultipleClientsAllFail) {
+    std::vector<uint8_t> pswd{0x01, 0x03, 0x05, 0x07};
+    std::vector<uint8_t> pswd2{0x01, 0x03, 0x05, 0x06};
+
+    auto server = createServer(pswd);
+    ASSERT_NE(nullptr, server);
+    // Start the server first, to open the port for connections
+    std::mutex server_mutex;
+    std::condition_variable server_cv;
+    std::unique_lock<std::mutex> server_lock(server_mutex);
+
+    bool server_got_valid_pairing = true;
+    auto server_callback = [&](const PeerInfo* peer_info, const std::vector<uint8_t>* cert,
+                               void* opaque) {
+        // Pairing will be cancelled, which should initiate this callback with
+        // empty values.
+        ASSERT_EQ(nullptr, peer_info);
+        ASSERT_EQ(nullptr, cert);
+        EXPECT_EQ(nullptr, opaque);
+        std::lock_guard<std::mutex> lock(server_mutex);
+        server_cv.notify_one();
+        server_got_valid_pairing = false;
+    };
+    ASSERT_TRUE(server->start(server_callback, nullptr));
+
+    // Start multiple clients, all with bad passwords
+    std::vector<std::unique_ptr<PairingClient>> clients;
+    int num_clients_done = 0;
+    int test_num_clients = 5;
+    std::mutex client_mutex;
+    std::condition_variable client_cv;
+    std::unique_lock<std::mutex> client_lock(client_mutex);
+    while (clients.size() < test_num_clients) {
+        auto client = createClient(pswd2);
+        ASSERT_NE(nullptr, client);
+        auto callback = [&](const PeerInfo* peer_info, const std::vector<uint8_t>* cert,
+                            void* opaque) {
+            ASSERT_EQ(nullptr, peer_info);
+            ASSERT_EQ(nullptr, cert);
+            EXPECT_EQ(nullptr, opaque);
+
+            {
+                std::lock_guard<std::mutex> lock(client_mutex);
+                num_clients_done++;
+            }
+            client_cv.notify_one();
+        };
+        ASSERT_TRUE(client->start(callback, nullptr));
+        clients.push_back(std::move(client));
+    }
+
+    client_cv.wait(client_lock, [&]() { return (num_clients_done == test_num_clients); });
+    EXPECT_EQ(num_clients_done, test_num_clients);
+
+    server_lock.unlock();
+    // This should trigger the callback to be on the same thread.
+    server.reset();
+    EXPECT_FALSE(server_got_valid_pairing);
+}
+
+TEST_F(AdbWifiPairingConnectionTest, MultipleClientsOnePass) {
+    // Send multiple clients with bad passwords, but send the last one with the
+    // correct password.
+    std::vector<uint8_t> pswd{0x01, 0x03, 0x05, 0x07};
+    std::vector<uint8_t> pswd2{0x01, 0x03, 0x05, 0x06};
+
+    auto server = createServer(pswd);
+    ASSERT_NE(nullptr, server);
+    // Start the server first, to open the port for connections
+    std::mutex server_mutex;
+    std::condition_variable server_cv;
+    std::unique_lock<std::mutex> server_lock(server_mutex);
+
+    bool server_got_valid_pairing = false;
+    auto server_callback = [&](const PeerInfo* peer_info, const std::vector<uint8_t>* cert,
+                               void* opaque) {
+        // Pairing will be cancelled, which should initiate this callback with
+        // empty values.
+
+        ASSERT_NE(nullptr, peer_info);
+        ASSERT_NE(nullptr, cert);
+        EXPECT_FALSE(cert->empty());
+        EXPECT_EQ(nullptr, opaque);
+
+        // Verify the peer_info and cert
+        ASSERT_EQ(strlen(peer_info->name), strlen(client_info_.name));
+        EXPECT_EQ(::memcmp(peer_info->name, client_info_.name, strlen(client_info_.name)), 0);
+        ASSERT_EQ(strlen(peer_info->guid), strlen(client_info_.guid));
+        EXPECT_EQ(::memcmp(peer_info->guid, client_info_.guid, strlen(client_info_.guid)), 0);
+        ASSERT_EQ(cert->size(), kTestClientCert.size() + 1);
+        EXPECT_EQ(::memcmp(cert->data(), kTestClientCert.data(), kTestClientCert.size() + 1), 0);
+
+        std::lock_guard<std::mutex> lock(server_mutex);
+        server_got_valid_pairing = true;
+        server_cv.notify_one();
+    };
+    ASSERT_TRUE(server->start(server_callback, nullptr));
+
+    // Start multiple clients, all with bad passwords (except for the last one)
+    std::vector<std::unique_ptr<PairingClient>> clients;
+    int num_clients_done = 0;
+    int test_num_clients = 5;
+    std::mutex client_mutex;
+    std::condition_variable client_cv;
+    std::unique_lock<std::mutex> client_lock(client_mutex);
+    bool got_valid_pairing = false;
+    while (clients.size() < test_num_clients) {
+        std::unique_ptr<PairingClient> client;
+        if (clients.size() == test_num_clients - 1) {
+            // Make this one have the valid password
+            client = createClient(pswd);
+            ASSERT_NE(nullptr, client);
+            auto callback = [&](const PeerInfo* peer_info, const std::vector<uint8_t>* cert,
+                                void* opaque) {
+                ASSERT_NE(nullptr, peer_info);
+                ASSERT_NE(nullptr, cert);
+                EXPECT_FALSE(cert->empty());
+                EXPECT_EQ(nullptr, opaque);
+
+                // Verify the peer_info and cert
+                ASSERT_EQ(strlen(peer_info->name), strlen(server_info_.name));
+                EXPECT_EQ(::memcmp(peer_info->name, server_info_.name, strlen(server_info_.name)),
+                          0);
+                ASSERT_EQ(strlen(peer_info->guid), strlen(server_info_.guid));
+                EXPECT_EQ(::memcmp(peer_info->guid, server_info_.guid, strlen(server_info_.guid)),
+                          0);
+                ASSERT_EQ(cert->size(), kTestServerCert.size() + 1);
+                EXPECT_EQ(
+                        ::memcmp(cert->data(), kTestServerCert.data(), kTestServerCert.size() + 1),
+                        0);
+                got_valid_pairing = (peer_info != nullptr && cert != nullptr && !cert->empty());
+
+                {
+                    std::lock_guard<std::mutex> lock(client_mutex);
+                    num_clients_done++;
+                }
+                client_cv.notify_one();
+            };
+            ASSERT_TRUE(client->start(callback, nullptr));
+        } else {
+            client = createClient(pswd2);
+            ASSERT_NE(nullptr, client);
+            auto callback = [&](const PeerInfo* peer_info, const std::vector<uint8_t>* cert,
+                                void* opaque) {
+                ASSERT_EQ(nullptr, peer_info);
+                ASSERT_EQ(nullptr, cert);
+                EXPECT_EQ(nullptr, opaque);
+
+                {
+                    std::lock_guard<std::mutex> lock(client_mutex);
+                    num_clients_done++;
+                }
+                client_cv.notify_one();
+            };
+            ASSERT_TRUE(client->start(callback, nullptr));
+        }
+        clients.push_back(std::move(client));
+    }
+
+    client_cv.wait(client_lock, [&]() { return (num_clients_done == test_num_clients); });
+    EXPECT_EQ(num_clients_done, test_num_clients);
+
+    // Kill server if the pairing failed, since server only shuts down when
+    // it gets a valid pairing.
+    if (!got_valid_pairing) {
+        server_lock.unlock();
+        server_.reset();
+    } else {
+        server_cv.wait(server_lock);
+    }
+    EXPECT_TRUE(server_got_valid_pairing);
+}
+
+}  // namespace pairing
+}  // namespace adbwifi
diff --git a/adb/client/pairing/tests/pairing_server.cpp b/adb/client/pairing/tests/pairing_server.cpp
new file mode 100644
index 0000000..9201e7a
--- /dev/null
+++ b/adb/client/pairing/tests/pairing_server.cpp
@@ -0,0 +1,426 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "adbwifi/pairing/pairing_server.h"
+
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+
+#include <atomic>
+#include <deque>
+#include <iomanip>
+#include <mutex>
+#include <sstream>
+#include <thread>
+#include <tuple>
+#include <unordered_map>
+#include <variant>
+#include <vector>
+
+#include <adbwifi/pairing/pairing_connection.h>
+#include <android-base/logging.h>
+#include <android-base/parsenetaddress.h>
+#include <android-base/thread_annotations.h>
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+
+namespace adbwifi {
+namespace pairing {
+
+using android::base::ScopedLockAssertion;
+using android::base::unique_fd;
+
+namespace {
+
+// The implimentation has two background threads running: one to handle and
+// accept any new pairing connection requests (socket accept), and the other to
+// handle connection events (connection started, connection finished).
+class PairingServerImpl : public PairingServer {
+  public:
+    virtual ~PairingServerImpl();
+
+    // All parameters must be non-empty.
+    explicit PairingServerImpl(const Data& pswd, const PeerInfo& peer_info, const Data& cert,
+                               const Data& priv_key, int port);
+
+    // Starts the pairing server. This call is non-blocking. Upon completion,
+    // if the pairing was successful, then |cb| will be called with the PublicKeyHeader
+    // containing the info of the trusted peer. Otherwise, |cb| will be
+    // called with an empty value. Start can only be called once in the lifetime
+    // of this object.
+    //
+    // Returns true if PairingServer was successfully started. Otherwise,
+    // returns false.
+    virtual bool start(PairingConnection::ResultCallback cb, void* opaque) override;
+
+  private:
+    // Setup the server socket to accept incoming connections
+    bool setupServer();
+    // Force stop the server thread.
+    void stopServer();
+
+    // handles a new pairing client connection
+    bool handleNewClientConnection(int fd) EXCLUDES(conn_mutex_);
+
+    // ======== connection events thread =============
+    std::mutex conn_mutex_;
+    std::condition_variable conn_cv_;
+
+    using FdVal = int;
+    using ConnectionPtr = std::unique_ptr<PairingConnection>;
+    using NewConnectionEvent = std::tuple<unique_fd, ConnectionPtr>;
+    // <fd, PeerInfo.name, PeerInfo.guid, certificate>
+    using ConnectionFinishedEvent = std::tuple<FdVal, std::optional<std::string>,
+                                               std::optional<std::string>, std::optional<Data>>;
+    using ConnectionEvent = std::variant<NewConnectionEvent, ConnectionFinishedEvent>;
+    // Queue for connections to write into. We have a separate queue to read
+    // from, in order to minimize the time the server thread is blocked.
+    std::deque<ConnectionEvent> conn_write_queue_ GUARDED_BY(conn_mutex_);
+    std::deque<ConnectionEvent> conn_read_queue_;
+    // Map of fds to their PairingConnections currently running.
+    std::unordered_map<FdVal, ConnectionPtr> connections_;
+
+    // Two threads launched when starting the pairing server:
+    // 1) A server thread that waits for incoming client connections, and
+    // 2) A connection events thread that synchonizes events from all of the
+    //    clients, since each PairingConnection is running in it's own thread.
+    void startConnectionEventsThread();
+    void startServerThread();
+
+    std::thread conn_events_thread_;
+    void connectionEventsWorker();
+    std::thread server_thread_;
+    void serverWorker();
+    bool is_terminate_ GUARDED_BY(conn_mutex_) = false;
+
+    enum class State {
+        Ready,
+        Running,
+        Stopped,
+    };
+    State state_ = State::Ready;
+    Data pswd_;
+    PeerInfo peer_info_;
+    Data cert_;
+    Data priv_key_;
+    int port_ = -1;
+
+    PairingConnection::ResultCallback cb_;
+    void* opaque_ = nullptr;
+    bool got_valid_pairing_ = false;
+
+    static const int kEpollConstSocket = 0;
+    // Used to break the server thread from epoll_wait
+    static const int kEpollConstEventFd = 1;
+    unique_fd epoll_fd_;
+    unique_fd server_fd_;
+    unique_fd event_fd_;
+};  // PairingServerImpl
+
+PairingServerImpl::PairingServerImpl(const Data& pswd, const PeerInfo& peer_info, const Data& cert,
+                                     const Data& priv_key, int port)
+    : pswd_(pswd), peer_info_(peer_info), cert_(cert), priv_key_(priv_key), port_(port) {
+    CHECK(!pswd_.empty() && !cert_.empty() && !priv_key_.empty() && port_ > 0);
+    CHECK('\0' == peer_info.name[kPeerNameLength - 1] &&
+          '\0' == peer_info.guid[kPeerGuidLength - 1] && strlen(peer_info.name) > 0 &&
+          strlen(peer_info.guid) > 0);
+}
+
+PairingServerImpl::~PairingServerImpl() {
+    // Since these connections have references to us, let's make sure they
+    // destruct before us.
+    if (server_thread_.joinable()) {
+        stopServer();
+        server_thread_.join();
+    }
+
+    {
+        std::lock_guard<std::mutex> lock(conn_mutex_);
+        is_terminate_ = true;
+    }
+    conn_cv_.notify_one();
+    if (conn_events_thread_.joinable()) {
+        conn_events_thread_.join();
+    }
+
+    // Notify the cb_ if it hasn't already.
+    if (!got_valid_pairing_ && cb_ != nullptr) {
+        cb_(nullptr, nullptr, opaque_);
+    }
+}
+
+bool PairingServerImpl::start(PairingConnection::ResultCallback cb, void* opaque) {
+    cb_ = cb;
+    opaque_ = opaque;
+
+    if (state_ != State::Ready) {
+        LOG(ERROR) << "PairingServer already running or stopped";
+        return false;
+    }
+
+    if (!setupServer()) {
+        LOG(ERROR) << "Unable to start PairingServer";
+        state_ = State::Stopped;
+        return false;
+    }
+
+    state_ = State::Running;
+    return true;
+}
+
+void PairingServerImpl::stopServer() {
+    if (event_fd_.get() == -1) {
+        return;
+    }
+    uint64_t value = 1;
+    ssize_t rc = write(event_fd_.get(), &value, sizeof(value));
+    if (rc == -1) {
+        // This can happen if the server didn't start.
+        PLOG(ERROR) << "write to eventfd failed";
+    } else if (rc != sizeof(value)) {
+        LOG(FATAL) << "write to event returned short (" << rc << ")";
+    }
+}
+
+bool PairingServerImpl::setupServer() {
+    epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC));
+    if (epoll_fd_ == -1) {
+        PLOG(ERROR) << "failed to create epoll fd";
+        return false;
+    }
+
+    event_fd_.reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
+    if (event_fd_ == -1) {
+        PLOG(ERROR) << "failed to create eventfd";
+        return false;
+    }
+
+    server_fd_.reset(socket_inaddr_any_server(port_, SOCK_STREAM));
+    if (server_fd_.get() == -1) {
+        PLOG(ERROR) << "Failed to start pairing connection server";
+        return false;
+    } else if (fcntl(server_fd_.get(), F_SETFD, FD_CLOEXEC) != 0) {
+        PLOG(ERROR) << "Failed to make server socket cloexec";
+        return false;
+    } else if (fcntl(server_fd_.get(), F_SETFD, O_NONBLOCK) != 0) {
+        PLOG(ERROR) << "Failed to make server socket nonblocking";
+        return false;
+    }
+
+    startConnectionEventsThread();
+    startServerThread();
+    return true;
+}
+
+void PairingServerImpl::startServerThread() {
+    server_thread_ = std::thread([this]() { serverWorker(); });
+}
+
+void PairingServerImpl::startConnectionEventsThread() {
+    conn_events_thread_ = std::thread([this]() { connectionEventsWorker(); });
+}
+
+void PairingServerImpl::serverWorker() {
+    {
+        struct epoll_event event;
+        event.events = EPOLLIN;
+        event.data.u64 = kEpollConstSocket;
+        CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, server_fd_.get(), &event));
+    }
+
+    {
+        struct epoll_event event;
+        event.events = EPOLLIN;
+        event.data.u64 = kEpollConstEventFd;
+        CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, event_fd_.get(), &event));
+    }
+
+    while (true) {
+        struct epoll_event events[2];
+        int rc = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd_.get(), events, 2, -1));
+        if (rc == -1) {
+            PLOG(ERROR) << "epoll_wait failed";
+            return;
+        } else if (rc == 0) {
+            LOG(ERROR) << "epoll_wait returned 0";
+            return;
+        }
+
+        for (int i = 0; i < rc; ++i) {
+            struct epoll_event& event = events[i];
+            switch (event.data.u64) {
+                case kEpollConstSocket:
+                    handleNewClientConnection(server_fd_.get());
+                    break;
+                case kEpollConstEventFd:
+                    uint64_t dummy;
+                    int rc = TEMP_FAILURE_RETRY(read(event_fd_.get(), &dummy, sizeof(dummy)));
+                    if (rc != sizeof(dummy)) {
+                        PLOG(FATAL) << "failed to read from eventfd (rc=" << rc << ")";
+                    }
+                    return;
+            }
+        }
+    }
+}
+
+void PairingServerImpl::connectionEventsWorker() {
+    for (;;) {
+        // Transfer the write queue to the read queue.
+        {
+            std::unique_lock<std::mutex> lock(conn_mutex_);
+            ScopedLockAssertion assume_locked(conn_mutex_);
+
+            if (is_terminate_) {
+                // We check |is_terminate_| twice because condition_variable's
+                // notify() only wakes up a thread if it is in the wait state
+                // prior to notify(). Furthermore, we aren't holding the mutex
+                // when processing the events in |conn_read_queue_|.
+                return;
+            }
+            if (conn_write_queue_.empty()) {
+                // We need to wait for new events, or the termination signal.
+                conn_cv_.wait(lock, [this]() REQUIRES(conn_mutex_) {
+                    return (is_terminate_ || !conn_write_queue_.empty());
+                });
+            }
+            if (is_terminate_) {
+                // We're done.
+                return;
+            }
+            // Move all events into the read queue.
+            conn_read_queue_ = std::move(conn_write_queue_);
+            conn_write_queue_.clear();
+        }
+
+        // Process all events in the read queue.
+        while (conn_read_queue_.size() > 0) {
+            auto& event = conn_read_queue_.front();
+            if (auto* p = std::get_if<NewConnectionEvent>(&event)) {
+                // Ignore if we are already at the max number of connections
+                if (connections_.size() >= internal::kMaxConnections) {
+                    conn_read_queue_.pop_front();
+                    continue;
+                }
+                auto [ufd, connection] = std::move(*p);
+                int fd = ufd.release();
+                bool started = connection->start(
+                        fd,
+                        [fd](const PeerInfo* peer_info, const Data* cert, void* opaque) {
+                            auto* p = reinterpret_cast<PairingServerImpl*>(opaque);
+
+                            ConnectionFinishedEvent event;
+                            if (peer_info != nullptr && cert != nullptr) {
+                                event = std::make_tuple(fd, std::string(peer_info->name),
+                                                        std::string(peer_info->guid), Data(*cert));
+                            } else {
+                                event = std::make_tuple(fd, std::nullopt, std::nullopt,
+                                                        std::nullopt);
+                            }
+                            {
+                                std::lock_guard<std::mutex> lock(p->conn_mutex_);
+                                p->conn_write_queue_.push_back(std::move(event));
+                            }
+                            p->conn_cv_.notify_one();
+                        },
+                        this);
+                if (!started) {
+                    LOG(ERROR) << "PairingServer unable to start a PairingConnection fd=" << fd;
+                    ufd.reset(fd);
+                } else {
+                    connections_[fd] = std::move(connection);
+                }
+            } else if (auto* p = std::get_if<ConnectionFinishedEvent>(&event)) {
+                auto [fd, name, guid, cert] = std::move(*p);
+                if (name.has_value() && guid.has_value() && cert.has_value() && !name->empty() &&
+                    !guid->empty() && !cert->empty()) {
+                    // Valid pairing. Let's shutdown the server and close any
+                    // pairing connections in progress.
+                    stopServer();
+                    connections_.clear();
+
+                    CHECK_LE(name->size(), kPeerNameLength);
+                    CHECK_LE(guid->size(), kPeerGuidLength);
+                    PeerInfo info = {};
+                    strncpy(info.name, name->data(), name->size());
+                    strncpy(info.guid, guid->data(), guid->size());
+
+                    cb_(&info, &*cert, opaque_);
+
+                    got_valid_pairing_ = true;
+                    return;
+                }
+                // Invalid pairing. Close the invalid connection.
+                if (connections_.find(fd) != connections_.end()) {
+                    connections_.erase(fd);
+                }
+            }
+            conn_read_queue_.pop_front();
+        }
+    }
+}
+
+bool PairingServerImpl::handleNewClientConnection(int fd) {
+    unique_fd ufd(TEMP_FAILURE_RETRY(accept4(fd, nullptr, nullptr, SOCK_CLOEXEC)));
+    if (ufd == -1) {
+        PLOG(WARNING) << "adb_socket_accept failed fd=" << fd;
+        return false;
+    }
+    auto connection = PairingConnection::create(PairingConnection::Role::Server, pswd_, peer_info_,
+                                                cert_, priv_key_);
+    if (connection == nullptr) {
+        LOG(ERROR) << "PairingServer unable to create a PairingConnection fd=" << fd;
+        return false;
+    }
+    // send the new connection to the connection thread for further processing
+    NewConnectionEvent event = std::make_tuple(std::move(ufd), std::move(connection));
+    {
+        std::lock_guard<std::mutex> lock(conn_mutex_);
+        conn_write_queue_.push_back(std::move(event));
+    }
+    conn_cv_.notify_one();
+
+    return true;
+}
+
+}  // namespace
+
+// static
+std::unique_ptr<PairingServer> PairingServer::create(const Data& pswd, const PeerInfo& peer_info,
+                                                     const Data& cert, const Data& priv_key,
+                                                     int port) {
+    if (pswd.empty() || cert.empty() || priv_key.empty() || port <= 0) {
+        return nullptr;
+    }
+    // Make sure peer_info has a non-empty, null-terminated string for guid and
+    // name.
+    if ('\0' != peer_info.name[kPeerNameLength - 1] ||
+        '\0' != peer_info.guid[kPeerGuidLength - 1] || strlen(peer_info.name) == 0 ||
+        strlen(peer_info.guid) == 0) {
+        LOG(ERROR) << "The GUID/short name fields are empty or not null-terminated";
+        return nullptr;
+    }
+
+    if (port != kDefaultPairingPort) {
+        LOG(WARNING) << "Starting server with non-default pairing port=" << port;
+    }
+
+    return std::unique_ptr<PairingServer>(
+            new PairingServerImpl(pswd, peer_info, cert, priv_key, port));
+}
+
+}  // namespace pairing
+}  // namespace adbwifi
diff --git a/adb/client/pairing/tests/pairing_server.h b/adb/client/pairing/tests/pairing_server.h
new file mode 100644
index 0000000..6fb51cc
--- /dev/null
+++ b/adb/client/pairing/tests/pairing_server.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <functional>
+#include <memory>
+#include <string_view>
+#include <vector>
+
+#include <adbwifi/pairing/pairing_connection.h>
+
+namespace adbwifi {
+namespace pairing {
+
+// PairingServer is the server side of the PairingConnection protocol. It will
+// listen for incoming PairingClient connections, and allocate a new
+// PairingConnection per client for processing. PairingServer can handle multiple
+// connections, but the first one to establish the pairing will be the only one
+// to succeed. All others will be disconnected.
+//
+// See pairing_connection_test.cpp for example usage.
+//
+class PairingServer {
+  public:
+    using Data = std::vector<uint8_t>;
+
+    virtual ~PairingServer() = default;
+
+    // Starts the pairing server. This call is non-blocking. Upon completion,
+    // if the pairing was successful, then |cb| will be called with the PeerInfo
+    // containing the info of the trusted peer. Otherwise, |cb| will be
+    // called with an empty value. Start can only be called once in the lifetime
+    // of this object.
+    //
+    // Returns true if PairingServer was successfully started. Otherwise,
+    // returns false.
+    virtual bool start(PairingConnection::ResultCallback cb, void* opaque) = 0;
+
+    // Creates a new PairingServer instance. May return null if unable
+    // to create an instance. |pswd|, |certificate| and |priv_key| cannot
+    // be empty. |port| is the port PairingServer will listen to PairingClient
+    // connections on. |peer_info| must contain non-empty strings for the guid
+    // and name fields.
+    static std::unique_ptr<PairingServer> create(const Data& pswd, const PeerInfo& peer_info,
+                                                 const Data& certificate, const Data& priv_key,
+                                                 int port);
+
+  protected:
+    PairingServer() = default;
+};  // class PairingServer
+
+}  // namespace pairing
+}  // namespace adbwifi
diff --git a/adb/client/transport_mdns.cpp b/adb/client/transport_mdns.cpp
index 1a34384..ff1f7b4 100644
--- a/adb/client/transport_mdns.cpp
+++ b/adb/client/transport_mdns.cpp
@@ -24,18 +24,46 @@
 #include <arpa/inet.h>
 #endif
 
+#include <memory>
 #include <thread>
+#include <vector>
 
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <dns_sd.h>
 
+#include "adb_client.h"
 #include "adb_mdns.h"
 #include "adb_trace.h"
+#include "adb_utils.h"
+#include "adb_wifi.h"
 #include "fdevent/fdevent.h"
 #include "sysdeps.h"
 
-static DNSServiceRef service_ref;
-static fdevent* service_ref_fde;
+static DNSServiceRef service_refs[kNumADBDNSServices];
+static fdevent* service_ref_fdes[kNumADBDNSServices];
+
+static int adb_DNSServiceIndexByName(const char* regType) {
+    for (int i = 0; i < kNumADBDNSServices; ++i) {
+        if (!strncmp(regType, kADBDNSServices[i], strlen(kADBDNSServices[i]))) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+static bool adb_DNSServiceShouldConnect(const char* regType, const char* serviceName) {
+    int index = adb_DNSServiceIndexByName(regType);
+    if (index == kADBTransportServiceRefIndex) {
+        // Ignore adb-EMULATOR* service names, as it interferes with the
+        // emulator ports that are already connected.
+        if (android::base::StartsWith(serviceName, "adb-EMULATOR")) {
+            LOG(INFO) << "Ignoring emulator transport service [" << serviceName << "]";
+            return false;
+        }
+    }
+    return (index == kADBTransportServiceRefIndex || index == kADBSecureConnectServiceRefIndex);
+}
 
 // Use adb_DNSServiceRefSockFD() instead of calling DNSServiceRefSockFD()
 // directly so that the socket is put through the appropriate compatibility
@@ -72,8 +100,10 @@
             return;
         }
 
-        DNSServiceRefDeallocate(sdRef_);
+        // Order matters here! Must destroy the fdevent first since it has a
+        // reference to |sdRef_|.
         fdevent_destroy(fde_);
+        DNSServiceRefDeallocate(sdRef_);
     }
 
   protected:
@@ -81,6 +111,10 @@
 
     void Initialize() {
         fde_ = fdevent_create(adb_DNSServiceRefSockFD(sdRef_), pump_service_ref, &sdRef_);
+        if (fde_ == nullptr) {
+            D("Unable to create fdevent");
+            return;
+        }
         fdevent_set(fde_, FDE_READ);
         initialized_ = true;
     }
@@ -94,10 +128,16 @@
   public:
     virtual ~ResolvedService() = default;
 
-    ResolvedService(std::string name, uint32_t interfaceIndex,
-                    const char* hosttarget, uint16_t port) :
-            name_(name),
-            port_(port) {
+    ResolvedService(std::string serviceName, std::string regType, uint32_t interfaceIndex,
+                    const char* hosttarget, uint16_t port, int version)
+        : serviceName_(serviceName),
+          regType_(regType),
+          hosttarget_(hosttarget),
+          port_(port),
+          sa_family_(0),
+          ip_addr_data_(NULL),
+          serviceVersion_(version) {
+        memset(ip_addr_, 0, sizeof(ip_addr_));
 
         /* TODO: We should be able to get IPv6 support by adding
          * kDNSServiceProtocol_IPv6 to the flags below. However, when we do
@@ -116,45 +156,179 @@
         } else {
             Initialize();
         }
+
+        D("Client version: %d Service version: %d\n", clientVersion_, serviceVersion_);
+    }
+
+    bool ConnectSecureWifiDevice() {
+        if (!adb_wifi_is_known_host(serviceName_)) {
+            LOG(INFO) << "serviceName=" << serviceName_ << " not in keystore";
+            return false;
+        }
+
+        std::string response;
+        connect_device(android::base::StringPrintf(addr_format_.c_str(), ip_addr_, port_),
+                       &response);
+        D("Secure connect to %s regtype %s (%s:%hu) : %s", serviceName_.c_str(), regType_.c_str(),
+          ip_addr_, port_, response.c_str());
+        return true;
     }
 
     void Connect(const sockaddr* address) {
-        char ip_addr[INET6_ADDRSTRLEN];
-        const void* ip_addr_data;
-        const char* addr_format;
+        sa_family_ = address->sa_family;
 
-        if (address->sa_family == AF_INET) {
-            ip_addr_data =
-                &reinterpret_cast<const sockaddr_in*>(address)->sin_addr;
-            addr_format = "%s:%hu";
-        } else if (address->sa_family == AF_INET6) {
-            ip_addr_data =
-                &reinterpret_cast<const sockaddr_in6*>(address)->sin6_addr;
-            addr_format = "[%s]:%hu";
-        } else { // Should be impossible
+        if (sa_family_ == AF_INET) {
+            ip_addr_data_ = &reinterpret_cast<const sockaddr_in*>(address)->sin_addr;
+            addr_format_ = "%s:%hu";
+        } else if (sa_family_ == AF_INET6) {
+            ip_addr_data_ = &reinterpret_cast<const sockaddr_in6*>(address)->sin6_addr;
+            addr_format_ = "[%s]:%hu";
+        } else {  // Should be impossible
             D("mDNS resolved non-IP address.");
             return;
         }
 
         // Winsock version requires the const cast Because Microsoft.
-        if (!inet_ntop(address->sa_family, const_cast<void*>(ip_addr_data),
-                       ip_addr, INET6_ADDRSTRLEN)) {
+        if (!inet_ntop(sa_family_, const_cast<void*>(ip_addr_data_), ip_addr_, sizeof(ip_addr_))) {
             D("Could not convert IP address to string.");
             return;
         }
 
-        std::string response;
-        connect_device(android::base::StringPrintf(addr_format, ip_addr, port_),
-                       &response);
-        D("Connect to %s (%s:%hu) : %s", name_.c_str(), ip_addr, port_,
-          response.c_str());
+        // adb secure service needs to do something different from just
+        // connecting here.
+        if (adb_DNSServiceShouldConnect(regType_.c_str(), serviceName_.c_str())) {
+            std::string response;
+            D("Attempting to serviceName=[%s], regtype=[%s] ipaddr=(%s:%hu)", serviceName_.c_str(),
+              regType_.c_str(), ip_addr_, port_);
+            int index = adb_DNSServiceIndexByName(regType_.c_str());
+            if (index == kADBSecureConnectServiceRefIndex) {
+                ConnectSecureWifiDevice();
+            } else {
+                connect_device(android::base::StringPrintf(addr_format_.c_str(), ip_addr_, port_),
+                               &response);
+                D("Connect to %s regtype %s (%s:%hu) : %s", serviceName_.c_str(), regType_.c_str(),
+                  ip_addr_, port_, response.c_str());
+            }
+        } else {
+            D("Not immediately connecting to serviceName=[%s], regtype=[%s] ipaddr=(%s:%hu)",
+              serviceName_.c_str(), regType_.c_str(), ip_addr_, port_);
+        }
+
+        int adbSecureServiceType = serviceIndex();
+        switch (adbSecureServiceType) {
+            case kADBSecurePairingServiceRefIndex:
+                sAdbSecurePairingServices->push_back(this);
+                break;
+            case kADBSecureConnectServiceRefIndex:
+                sAdbSecureConnectServices->push_back(this);
+                break;
+            default:
+                break;
+        }
     }
 
+    int serviceIndex() const { return adb_DNSServiceIndexByName(regType_.c_str()); }
+
+    std::string hostTarget() const { return hosttarget_; }
+
+    std::string serviceName() const { return serviceName_; }
+
+    std::string ipAddress() const { return ip_addr_; }
+
+    uint16_t port() const { return port_; }
+
+    using ServiceRegistry = std::vector<ResolvedService*>;
+
+    static ServiceRegistry* sAdbSecurePairingServices;
+    static ServiceRegistry* sAdbSecureConnectServices;
+
+    static void initAdbSecure();
+
+    static void forEachService(const ServiceRegistry& services, const std::string& hostname,
+                               adb_secure_foreach_service_callback cb);
+
+    static bool connectByServiceName(const ServiceRegistry& services,
+                                     const std::string& service_name);
+
   private:
-    std::string name_;
+    int clientVersion_ = ADB_SECURE_CLIENT_VERSION;
+    std::string addr_format_;
+    std::string serviceName_;
+    std::string regType_;
+    std::string hosttarget_;
     const uint16_t port_;
+    int sa_family_;
+    const void* ip_addr_data_;
+    char ip_addr_[INET6_ADDRSTRLEN];
+    int serviceVersion_;
 };
 
+// static
+std::vector<ResolvedService*>* ResolvedService::sAdbSecurePairingServices = NULL;
+
+// static
+std::vector<ResolvedService*>* ResolvedService::sAdbSecureConnectServices = NULL;
+
+// static
+void ResolvedService::initAdbSecure() {
+    if (!sAdbSecurePairingServices) {
+        sAdbSecurePairingServices = new ServiceRegistry;
+    }
+    if (!sAdbSecureConnectServices) {
+        sAdbSecureConnectServices = new ServiceRegistry;
+    }
+}
+
+// static
+void ResolvedService::forEachService(const ServiceRegistry& services,
+                                     const std::string& wanted_service_name,
+                                     adb_secure_foreach_service_callback cb) {
+    initAdbSecure();
+
+    for (auto service : services) {
+        auto service_name = service->serviceName();
+        auto ip = service->ipAddress();
+        auto port = service->port();
+
+        if (wanted_service_name == "") {
+            cb(service_name.c_str(), ip.c_str(), port);
+        } else if (service_name == wanted_service_name) {
+            cb(service_name.c_str(), ip.c_str(), port);
+        }
+    }
+}
+
+// static
+bool ResolvedService::connectByServiceName(const ServiceRegistry& services,
+                                           const std::string& service_name) {
+    initAdbSecure();
+    for (auto service : services) {
+        if (service_name == service->serviceName()) {
+            D("Got service_name match [%s]", service->serviceName().c_str());
+            return service->ConnectSecureWifiDevice();
+        }
+    }
+    D("No registered serviceNames matched [%s]", service_name.c_str());
+    return false;
+}
+
+void adb_secure_foreach_pairing_service(const char* service_name,
+                                        adb_secure_foreach_service_callback cb) {
+    ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices,
+                                    service_name ? service_name : "", cb);
+}
+
+void adb_secure_foreach_connect_service(const char* service_name,
+                                        adb_secure_foreach_service_callback cb) {
+    ResolvedService::forEachService(*ResolvedService::sAdbSecureConnectServices,
+                                    service_name ? service_name : "", cb);
+}
+
+bool adb_secure_connect_by_service_name(const char* service_name) {
+    return ResolvedService::connectByServiceName(*ResolvedService::sAdbSecureConnectServices,
+                                                 service_name);
+}
+
 static void DNSSD_API register_service_ip(DNSServiceRef /*sdRef*/,
                                           DNSServiceFlags /*flags*/,
                                           uint32_t /*interfaceIndex*/,
@@ -167,6 +341,12 @@
     std::unique_ptr<ResolvedService> data(
         reinterpret_cast<ResolvedService*>(context));
     data->Connect(address);
+
+    // For ADB Secure services, keep those ResolvedService's around
+    // for later processing with secure connection establishment.
+    if (data->serviceIndex() != kADBTransportServiceRefIndex) {
+        data.release();
+    }
 }
 
 static void DNSSD_API register_resolved_mdns_service(DNSServiceRef sdRef,
@@ -182,18 +362,23 @@
 
 class DiscoveredService : public AsyncServiceRef {
   public:
-    DiscoveredService(uint32_t interfaceIndex, const char* serviceName,
-                      const char* regtype, const char* domain)
-        : serviceName_(serviceName) {
-
+    DiscoveredService(uint32_t interfaceIndex, const char* serviceName, const char* regtype,
+                      const char* domain)
+        : serviceName_(serviceName), regType_(regtype) {
         DNSServiceErrorType ret =
             DNSServiceResolve(&sdRef_, 0, interfaceIndex, serviceName, regtype,
                               domain, register_resolved_mdns_service,
                               reinterpret_cast<void*>(this));
 
-        if (ret != kDNSServiceErr_NoError) {
-            D("Got %d from DNSServiceResolve.", ret);
-        } else {
+        D("DNSServiceResolve for "
+          "interfaceIndex %u "
+          "serviceName %s "
+          "regtype %s "
+          "domain %s "
+          ": %d",
+          interfaceIndex, serviceName, regtype, domain, ret);
+
+        if (ret == kDNSServiceErr_NoError) {
             Initialize();
         }
     }
@@ -202,20 +387,82 @@
         return serviceName_.c_str();
     }
 
+    const char* RegType() { return regType_.c_str(); }
+
   private:
     std::string serviceName_;
+    std::string regType_;
 };
 
-static void DNSSD_API register_resolved_mdns_service(DNSServiceRef sdRef,
-                                                     DNSServiceFlags flags,
-                                                     uint32_t interfaceIndex,
-                                                     DNSServiceErrorType errorCode,
-                                                     const char* fullname,
-                                                     const char* hosttarget,
-                                                     uint16_t port,
-                                                     uint16_t /*txtLen*/,
-                                                     const unsigned char* /*txtRecord*/,
-                                                     void* context) {
+static void adb_RemoveDNSService(const char* regType, const char* serviceName) {
+    int index = adb_DNSServiceIndexByName(regType);
+    ResolvedService::ServiceRegistry* services;
+    switch (index) {
+        case kADBSecurePairingServiceRefIndex:
+            services = ResolvedService::sAdbSecurePairingServices;
+            break;
+        case kADBSecureConnectServiceRefIndex:
+            services = ResolvedService::sAdbSecureConnectServices;
+            break;
+        default:
+            return;
+    }
+
+    std::string sName(serviceName);
+    std::remove_if(services->begin(), services->end(), [&sName](ResolvedService* service) {
+        return (sName == service->serviceName());
+    });
+}
+
+// Returns the version the device wanted to advertise,
+// or -1 if parsing fails.
+static int parse_version_from_txt_record(uint16_t txtLen, const unsigned char* txtRecord) {
+    if (!txtLen) return -1;
+    if (!txtRecord) return -1;
+
+    // https://tools.ietf.org/html/rfc6763
+    // """
+    // 6.1.  General Format Rules for DNS TXT Records
+    //
+    // A DNS TXT record can be up to 65535 (0xFFFF) bytes long.  The total
+    // length is indicated by the length given in the resource record header
+    // in the DNS message.  There is no way to tell directly from the data
+    // alone how long it is (e.g., there is no length count at the start, or
+    // terminating NULL byte at the end).
+    // """
+
+    // Let's trust the TXT record's length byte
+    // Worst case, it wastes 255 bytes
+    std::vector<char> recordAsString(txtLen + 1, '\0');
+    char* str = recordAsString.data();
+
+    memcpy(str, txtRecord + 1 /* skip the length byte */, txtLen);
+
+    // Check if it's the version key
+    static const char* versionKey = "v=";
+    size_t versionKeyLen = strlen(versionKey);
+
+    if (strncmp(versionKey, str, versionKeyLen)) return -1;
+
+    auto valueStart = str + versionKeyLen;
+
+    long parsedNumber = strtol(valueStart, 0, 10);
+
+    // No valid conversion. Also, 0
+    // is not a valid version.
+    if (!parsedNumber) return -1;
+
+    // Outside bounds of long.
+    if (parsedNumber == LONG_MIN || parsedNumber == LONG_MAX) return -1;
+
+    // Possibly valid version
+    return static_cast<int>(parsedNumber);
+}
+
+static void DNSSD_API register_resolved_mdns_service(
+        DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
+        DNSServiceErrorType errorCode, const char* fullname, const char* hosttarget, uint16_t port,
+        uint16_t txtLen, const unsigned char* txtRecord, void* context) {
     D("Resolved a service.");
     std::unique_ptr<DiscoveredService> discovered(
         reinterpret_cast<DiscoveredService*>(context));
@@ -225,58 +472,76 @@
         return;
     }
 
+    // TODO: Reject certain combinations of invalid or mismatched client and
+    // service versions here before creating anything.
+    // At the moment, there is nothing to reject, so accept everything
+    // as an optimistic default.
+    auto serviceVersion = parse_version_from_txt_record(txtLen, txtRecord);
 
-    auto resolved =
-        new ResolvedService(discovered->ServiceName(),
-                            interfaceIndex, hosttarget, ntohs(port));
+    auto resolved = new ResolvedService(discovered->ServiceName(), discovered->RegType(),
+                                        interfaceIndex, hosttarget, ntohs(port), serviceVersion);
 
     if (! resolved->Initialized()) {
+        D("Unable to init resolved service");
         delete resolved;
     }
 
     if (flags) { /* Only ever equals MoreComing or 0 */
+        D("releasing discovered service");
         discovered.release();
     }
 }
 
-static void DNSSD_API register_mdns_transport(DNSServiceRef sdRef,
-                                              DNSServiceFlags flags,
-                                              uint32_t interfaceIndex,
-                                              DNSServiceErrorType errorCode,
-                                              const char* serviceName,
-                                              const char* regtype,
-                                              const char* domain,
-                                              void*  /*context*/) {
-    D("Registering a transport.");
+static void DNSSD_API on_service_browsed(DNSServiceRef sdRef, DNSServiceFlags flags,
+                                         uint32_t interfaceIndex, DNSServiceErrorType errorCode,
+                                         const char* serviceName, const char* regtype,
+                                         const char* domain, void* /*context*/) {
     if (errorCode != kDNSServiceErr_NoError) {
         D("Got error %d during mDNS browse.", errorCode);
         DNSServiceRefDeallocate(sdRef);
-        fdevent_destroy(service_ref_fde);
+        int serviceIndex = adb_DNSServiceIndexByName(regtype);
+        if (serviceIndex != -1) {
+            fdevent_destroy(service_ref_fdes[serviceIndex]);
+        }
         return;
     }
 
-    auto discovered = new DiscoveredService(interfaceIndex, serviceName, regtype, domain);
-    if (!discovered->Initialized()) {
-        delete discovered;
+    if (flags & kDNSServiceFlagsAdd) {
+        D("%s: Discover found new serviceName=[%s] regtype=[%s] domain=[%s]", __func__, serviceName,
+          regtype, domain);
+        auto discovered = new DiscoveredService(interfaceIndex, serviceName, regtype, domain);
+        if (!discovered->Initialized()) {
+            delete discovered;
+        }
+    } else {
+        D("%s: Discover lost serviceName=[%s] regtype=[%s] domain=[%s]", __func__, serviceName,
+          regtype, domain);
+        adb_RemoveDNSService(regtype, serviceName);
     }
 }
 
 void init_mdns_transport_discovery_thread(void) {
-    DNSServiceErrorType errorCode = DNSServiceBrowse(&service_ref, 0, 0, kADBServiceType, nullptr,
-                                                     register_mdns_transport, nullptr);
+    int errorCodes[kNumADBDNSServices];
 
-    if (errorCode != kDNSServiceErr_NoError) {
-        D("Got %d initiating mDNS browse.", errorCode);
-        return;
+    for (int i = 0; i < kNumADBDNSServices; ++i) {
+        errorCodes[i] = DNSServiceBrowse(&service_refs[i], 0, 0, kADBDNSServices[i], nullptr,
+                                         on_service_browsed, nullptr);
+
+        if (errorCodes[i] != kDNSServiceErr_NoError) {
+            D("Got %d browsing for mDNS service %s.", errorCodes[i], kADBDNSServices[i]);
+        }
+
+        if (errorCodes[i] == kDNSServiceErr_NoError) {
+            fdevent_run_on_main_thread([i]() {
+                service_ref_fdes[i] = fdevent_create(adb_DNSServiceRefSockFD(service_refs[i]),
+                                                     pump_service_ref, &service_refs[i]);
+                fdevent_set(service_ref_fdes[i], FDE_READ);
+            });
+        }
     }
-
-    fdevent_run_on_main_thread([]() {
-        service_ref_fde =
-            fdevent_create(adb_DNSServiceRefSockFD(service_ref), pump_service_ref, &service_ref);
-        fdevent_set(service_ref_fde, FDE_READ);
-    });
 }
 
 void init_mdns_transport_discovery(void) {
+    ResolvedService::initAdbSecure();
     std::thread(init_mdns_transport_discovery_thread).detach();
 }
diff --git a/adb/crypto/Android.bp b/adb/crypto/Android.bp
new file mode 100644
index 0000000..b7f75ed
--- /dev/null
+++ b/adb/crypto/Android.bp
@@ -0,0 +1,81 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_defaults {
+    name: "libadb_crypto_defaults",
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Wthread-safety",
+        "-Werror",
+    ],
+
+    compile_multilib: "both",
+
+    srcs: [
+        "key.cpp",
+        "rsa_2048_key.cpp",
+        "x509_generator.cpp",
+    ],
+
+    target: {
+        windows: {
+            compile_multilib: "first",
+            enabled: true,
+        },
+    },
+
+    export_include_dirs: ["include"],
+
+    visibility: [
+        "//system/core/adb:__subpackages__",
+    ],
+
+    host_supported: true,
+    recovery_available: true,
+
+    stl: "libc++_static",
+
+    shared_libs: [
+        "libadb_protos",
+        "libbase",
+        "liblog",
+        "libcrypto",
+        "libcrypto_utils",
+    ],
+}
+
+cc_library {
+    name: "libadb_crypto",
+    defaults: ["libadb_crypto_defaults"],
+
+    apex_available: [
+        "com.android.adbd",
+        "test_com.android.adbd",
+    ],
+}
+
+// For running atest (b/147158681)
+cc_library_static {
+    name: "libadb_crypto_static",
+    defaults: ["libadb_crypto_defaults"],
+
+    apex_available: [
+        "//apex_available:platform",
+    ],
+
+    static_libs: [
+        "libadb_protos_static",
+    ],
+}
diff --git a/adb/crypto/include/adb/crypto/key.h b/adb/crypto/include/adb/crypto/key.h
new file mode 100644
index 0000000..d9ce69e
--- /dev/null
+++ b/adb/crypto/include/adb/crypto/key.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <openssl/evp.h>
+
+#include "key_type.pb.h"
+
+namespace adb {
+namespace crypto {
+
+// Class that represents a public/private key pair.
+class Key {
+  public:
+    explicit Key(bssl::UniquePtr<EVP_PKEY>&& pkey, adb::proto::KeyType type)
+        : pkey_(std::move(pkey)), key_type_(type) {}
+    Key(Key&&) = default;
+    Key& operator=(Key&&) = default;
+
+    EVP_PKEY* GetEvpPkey() const { return pkey_.get(); }
+    adb::proto::KeyType GetKeyType() const { return key_type_; }
+    static std::string ToPEMString(EVP_PKEY* pkey);
+
+  private:
+    bssl::UniquePtr<EVP_PKEY> pkey_;
+    adb::proto::KeyType key_type_;
+};  // Key
+
+}  // namespace crypto
+}  // namespace adb
diff --git a/adb/crypto/include/adb/crypto/rsa_2048_key.h b/adb/crypto/include/adb/crypto/rsa_2048_key.h
new file mode 100644
index 0000000..2983a84
--- /dev/null
+++ b/adb/crypto/include/adb/crypto/rsa_2048_key.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+#include <optional>
+
+#include "adb/crypto/key.h"
+
+namespace adb {
+namespace crypto {
+
+// Create a new RSA2048 key pair.
+std::optional<Key> CreateRSA2048Key();
+
+// Generates the public key from the RSA private key.
+bool CalculatePublicKey(std::string* out, RSA* private_key);
+
+}  // namespace crypto
+}  // namespace adb
diff --git a/adb/crypto/include/adb/crypto/x509_generator.h b/adb/crypto/include/adb/crypto/x509_generator.h
new file mode 100644
index 0000000..a269243
--- /dev/null
+++ b/adb/crypto/include/adb/crypto/x509_generator.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <openssl/x509v3.h>
+
+namespace adb {
+namespace crypto {
+
+// Generate a X.509 certificate based on the key |pkey|.
+bssl::UniquePtr<X509> GenerateX509Certificate(EVP_PKEY* pkey);
+
+// Convert X509* to PEM string format
+std::string X509ToPEMString(X509* x509);
+
+}  // namespace crypto
+}  // namespace adb
diff --git a/adb/crypto/key.cpp b/adb/crypto/key.cpp
new file mode 100644
index 0000000..4d87006
--- /dev/null
+++ b/adb/crypto/key.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "adb/crypto/key.h"
+
+#include <android-base/logging.h>
+#include <openssl/bn.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+
+namespace adb {
+namespace crypto {
+
+// static
+std::string Key::ToPEMString(EVP_PKEY* pkey) {
+    bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
+    int rc = PEM_write_bio_PKCS8PrivateKey(bio.get(), pkey, nullptr, nullptr, 0, nullptr, nullptr);
+    if (rc != 1) {
+        LOG(ERROR) << "PEM_write_bio_PKCS8PrivateKey failed";
+        return "";
+    }
+
+    BUF_MEM* mem = nullptr;
+    BIO_get_mem_ptr(bio.get(), &mem);
+    if (!mem || !mem->data || !mem->length) {
+        LOG(ERROR) << "BIO_get_mem_ptr failed";
+        return "";
+    }
+
+    return std::string(mem->data, mem->length);
+}
+
+}  // namespace crypto
+}  // namespace adb
diff --git a/adb/crypto/rsa_2048_key.cpp b/adb/crypto/rsa_2048_key.cpp
new file mode 100644
index 0000000..7911af9
--- /dev/null
+++ b/adb/crypto/rsa_2048_key.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "adb/crypto/rsa_2048_key.h"
+
+#include <android-base/logging.h>
+#include <crypto_utils/android_pubkey.h>
+#include <openssl/bn.h>
+#include <openssl/rsa.h>
+
+namespace adb {
+namespace crypto {
+
+namespace {
+std::string get_user_info() {
+    std::string hostname;
+    if (getenv("HOSTNAME")) hostname = getenv("HOSTNAME");
+#if !defined(_WIN32)
+    char buf[64];
+    if (hostname.empty() && gethostname(buf, sizeof(buf)) != -1) hostname = buf;
+#endif
+    if (hostname.empty()) hostname = "unknown";
+
+    std::string username;
+    if (getenv("LOGNAME")) username = getenv("LOGNAME");
+#if !defined(_WIN32)
+    if (username.empty() && getlogin()) username = getlogin();
+#endif
+    if (username.empty()) hostname = "unknown";
+
+    return " " + username + "@" + hostname;
+}
+
+}  // namespace
+
+bool CalculatePublicKey(std::string* out, RSA* private_key) {
+    uint8_t binary_key_data[ANDROID_PUBKEY_ENCODED_SIZE];
+    if (!android_pubkey_encode(private_key, binary_key_data, sizeof(binary_key_data))) {
+        LOG(ERROR) << "Failed to convert to public key";
+        return false;
+    }
+
+    size_t expected_length;
+    if (!EVP_EncodedLength(&expected_length, sizeof(binary_key_data))) {
+        LOG(ERROR) << "Public key too large to base64 encode";
+        return false;
+    }
+
+    out->resize(expected_length);
+    size_t actual_length = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(out->data()), binary_key_data,
+                                           sizeof(binary_key_data));
+    out->resize(actual_length);
+    out->append(get_user_info());
+    return true;
+}
+
+std::optional<Key> CreateRSA2048Key() {
+    bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
+    bssl::UniquePtr<BIGNUM> exponent(BN_new());
+    bssl::UniquePtr<RSA> rsa(RSA_new());
+    if (!pkey || !exponent || !rsa) {
+        LOG(ERROR) << "Failed to allocate key";
+        return std::nullopt;
+    }
+
+    BN_set_word(exponent.get(), RSA_F4);
+    RSA_generate_key_ex(rsa.get(), 2048, exponent.get(), nullptr);
+    EVP_PKEY_set1_RSA(pkey.get(), rsa.get());
+
+    return std::optional<Key>{Key(std::move(pkey), adb::proto::KeyType::RSA_2048)};
+}
+
+}  // namespace crypto
+}  // namespace adb
diff --git a/adb/crypto/tests/Android.bp b/adb/crypto/tests/Android.bp
new file mode 100644
index 0000000..b32dcf7
--- /dev/null
+++ b/adb/crypto/tests/Android.bp
@@ -0,0 +1,41 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+    name: "adb_crypto_test",
+    srcs: [
+        "rsa_2048_key_test.cpp",
+        "x509_generator_test.cpp",
+    ],
+
+    compile_multilib: "first",
+
+    shared_libs: [
+        "libbase",
+        "libcrypto",
+        "libcrypto_utils",
+        "libprotobuf-cpp-lite",
+    ],
+
+    // Let's statically link them so we don't have to install it onto the
+    // system image for testing.
+    static_libs: [
+        "libadb_crypto_static",
+        "libadb_protos_static",
+    ],
+
+    test_suites: ["device-tests"],
+}
diff --git a/adb/crypto/tests/key_test.cpp b/adb/crypto/tests/key_test.cpp
new file mode 100644
index 0000000..1feb6e8
--- /dev/null
+++ b/adb/crypto/tests/key_test.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <resolv.h>
+
+#include <adb/crypto/rsa_2048_key.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <crypto_utils/android_pubkey.h>
+#include <openssl/err.h>
+#include <openssl/rsa.h>
+#include <openssl/sha.h>
+
+namespace adb {
+namespace crypto {
+
+TEST(RSA2048Key, Smoke) {
+    auto rsa_2048 = CreateRSA2048Key();
+    EXPECT_NE(rsa_2048, std::nullopt);
+    EXPECT_EQ(rsa_2048->GetKeyType(), adb::proto::KeyType::RSA_2048);
+    ASSERT_NE(rsa_2048->GetEvpPkey(), nullptr);
+
+    // The public key string format is expected to be: "<pub_key> <host_name>"
+    std::string pub_key_plus_name;
+    auto* rsa = EVP_PKEY_get0_RSA(rsa_2048->GetEvpPkey());
+    ASSERT_TRUE(CalculatePublicKey(&pub_key_plus_name, rsa));
+    std::vector<std::string> split = android::base::Split(std::string(pub_key_plus_name), " \t");
+    EXPECT_EQ(split.size(), 2);
+
+    LOG(INFO) << "pub_key=[" << pub_key_plus_name << "]";
+
+    // Try to sign something and decode it.
+    const char token[SHA_DIGEST_LENGTH] = "abcdefghij123456789";
+    std::vector<uint8_t> sig(RSA_size(rsa));
+    unsigned sig_len;
+    EXPECT_EQ(RSA_sign(NID_sha1, reinterpret_cast<const uint8_t*>(token), sizeof(token), sig.data(),
+                       &sig_len, rsa),
+              1);
+    sig.resize(sig_len);
+
+    {
+        uint8_t keybuf[ANDROID_PUBKEY_ENCODED_SIZE + 1];
+        const std::string& pubkey = split[0];
+        ASSERT_EQ(b64_pton(pubkey.c_str(), keybuf, sizeof(keybuf)), ANDROID_PUBKEY_ENCODED_SIZE);
+        RSA* key = nullptr;
+        ASSERT_TRUE(android_pubkey_decode(keybuf, ANDROID_PUBKEY_ENCODED_SIZE, &key));
+        EXPECT_EQ(RSA_verify(NID_sha1, reinterpret_cast<const uint8_t*>(token), sizeof(token),
+                             sig.data(), sig.size(), key),
+                  1);
+        RSA_free(key);
+    }
+}
+
+}  // namespace crypto
+}  // namespace adb
diff --git a/adb/crypto/tests/rsa_2048_key_test.cpp b/adb/crypto/tests/rsa_2048_key_test.cpp
new file mode 100644
index 0000000..1d8880e
--- /dev/null
+++ b/adb/crypto/tests/rsa_2048_key_test.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <resolv.h>
+
+#include <adb/crypto/rsa_2048_key.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <crypto_utils/android_pubkey.h>
+#include <openssl/err.h>
+#include <openssl/rsa.h>
+#include <openssl/sha.h>
+
+namespace adb {
+namespace crypto {
+
+TEST(RSA2048Key, Smoke) {
+    auto rsa_2048 = CreateRSA2048Key();
+    EXPECT_NE(rsa_2048, std::nullopt);
+    EXPECT_EQ(rsa_2048->GetKeyType(), adb::proto::KeyType::RSA_2048);
+    ASSERT_NE(rsa_2048->GetEvpPkey(), nullptr);
+
+    // The public key string format is expected to be: "<pub_key> <host_name>"
+    std::string pub_key_plus_name;
+    auto* rsa = EVP_PKEY_get0_RSA(rsa_2048->GetEvpPkey());
+    ASSERT_TRUE(CalculatePublicKey(&pub_key_plus_name, rsa));
+    std::vector<std::string> split = android::base::Split(std::string(pub_key_plus_name), " \t");
+    EXPECT_EQ(split.size(), 2);
+
+    LOG(INFO) << "pub_key=[" << pub_key_plus_name << "]";
+
+    std::string pemString = Key::ToPEMString(rsa_2048->GetEvpPkey());
+    ASSERT_FALSE(pemString.empty());
+
+    // Try to sign something and decode it.
+    const char token[SHA_DIGEST_LENGTH] = "abcdefghij123456789";
+    std::vector<uint8_t> sig(RSA_size(rsa));
+    unsigned sig_len;
+    EXPECT_EQ(RSA_sign(NID_sha1, reinterpret_cast<const uint8_t*>(token), sizeof(token), sig.data(),
+                       &sig_len, rsa),
+              1);
+    sig.resize(sig_len);
+
+    {
+        uint8_t keybuf[ANDROID_PUBKEY_ENCODED_SIZE + 1];
+        const std::string& pubkey = split[0];
+        ASSERT_EQ(b64_pton(pubkey.c_str(), keybuf, sizeof(keybuf)), ANDROID_PUBKEY_ENCODED_SIZE);
+        RSA* key = nullptr;
+        ASSERT_TRUE(android_pubkey_decode(keybuf, ANDROID_PUBKEY_ENCODED_SIZE, &key));
+        EXPECT_EQ(RSA_verify(NID_sha1, reinterpret_cast<const uint8_t*>(token), sizeof(token),
+                             sig.data(), sig.size(), key),
+                  1);
+        RSA_free(key);
+    }
+}
+
+}  // namespace crypto
+}  // namespace adb
diff --git a/adb/crypto/tests/x509_generator_test.cpp b/adb/crypto/tests/x509_generator_test.cpp
new file mode 100644
index 0000000..281776b
--- /dev/null
+++ b/adb/crypto/tests/x509_generator_test.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <adb/crypto/rsa_2048_key.h>
+#include <adb/crypto/x509_generator.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+namespace adb {
+namespace crypto {
+
+TEST(X509Generator, Smoke) {
+    auto rsa_2048 = CreateRSA2048Key();
+
+    std::string pub_key_plus_name;
+    auto* rsa = EVP_PKEY_get0_RSA(rsa_2048->GetEvpPkey());
+    ASSERT_TRUE(CalculatePublicKey(&pub_key_plus_name, rsa));
+    std::vector<std::string> split = android::base::Split(std::string(pub_key_plus_name), " \t");
+    EXPECT_EQ(split.size(), 2);
+
+    LOG(INFO) << "pub_key=[" << pub_key_plus_name << "]";
+    auto x509_cert = GenerateX509Certificate(rsa_2048->GetEvpPkey());
+    ASSERT_NE(x509_cert.get(), nullptr);
+
+    std::string x509_str = X509ToPEMString(x509_cert.get());
+    ASSERT_FALSE(x509_str.empty());
+}
+
+}  // namespace crypto
+}  // namespace adb
diff --git a/adb/crypto/x509_generator.cpp b/adb/crypto/x509_generator.cpp
new file mode 100644
index 0000000..43b8153
--- /dev/null
+++ b/adb/crypto/x509_generator.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "adb/crypto/x509_generator.h"
+
+#include <vector>
+
+#include <android-base/logging.h>
+#include <crypto_utils/android_pubkey.h>
+#include <openssl/bn.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+
+namespace adb {
+namespace crypto {
+
+namespace {
+
+const char kBasicConstraints[] = "critical,CA:TRUE";
+const char kKeyUsage[] = "critical,keyCertSign,cRLSign,digitalSignature";
+const char kSubjectKeyIdentifier[] = "hash";
+constexpr int kCertLifetimeSeconds = 10 * 365 * 24 * 60 * 60;
+
+bool add_ext(X509* cert, int nid, const char* value) {
+    size_t len = strlen(value) + 1;
+    std::vector<char> mutableValue(value, value + len);
+    X509V3_CTX context;
+
+    X509V3_set_ctx_nodb(&context);
+
+    X509V3_set_ctx(&context, cert, cert, nullptr, nullptr, 0);
+    X509_EXTENSION* ex = X509V3_EXT_nconf_nid(nullptr, &context, nid, mutableValue.data());
+    if (!ex) {
+        return false;
+    }
+
+    X509_add_ext(cert, ex, -1);
+    X509_EXTENSION_free(ex);
+    return true;
+}
+
+}  // namespace
+
+bssl::UniquePtr<X509> GenerateX509Certificate(EVP_PKEY* pkey) {
+    CHECK(pkey);
+    bssl::UniquePtr<X509> x509(X509_new());
+    if (!x509) {
+        LOG(ERROR) << "Unable to allocate x509 container";
+        return nullptr;
+    }
+    X509_set_version(x509.get(), 2);
+
+    ASN1_INTEGER_set(X509_get_serialNumber(x509.get()), 1);
+    X509_gmtime_adj(X509_get_notBefore(x509.get()), 0);
+    X509_gmtime_adj(X509_get_notAfter(x509.get()), kCertLifetimeSeconds);
+
+    if (!X509_set_pubkey(x509.get(), pkey)) {
+        LOG(ERROR) << "Unable to set x509 public key";
+        return nullptr;
+    }
+
+    X509_NAME* name = X509_get_subject_name(x509.get());
+    if (!name) {
+        LOG(ERROR) << "Unable to get x509 subject name";
+        return nullptr;
+    }
+    X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC,
+                               reinterpret_cast<const unsigned char*>("US"), -1, -1, 0);
+    X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC,
+                               reinterpret_cast<const unsigned char*>("Android"), -1, -1, 0);
+    X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
+                               reinterpret_cast<const unsigned char*>("Adb"), -1, -1, 0);
+    if (!X509_set_issuer_name(x509.get(), name)) {
+        LOG(ERROR) << "Unable to set x509 issuer name";
+        return nullptr;
+    }
+
+    add_ext(x509.get(), NID_basic_constraints, kBasicConstraints);
+    add_ext(x509.get(), NID_key_usage, kKeyUsage);
+    add_ext(x509.get(), NID_subject_key_identifier, kSubjectKeyIdentifier);
+
+    int bytes = X509_sign(x509.get(), pkey, EVP_sha256());
+    if (bytes <= 0) {
+        LOG(ERROR) << "Unable to sign x509 certificate";
+        return nullptr;
+    }
+
+    return x509;
+}
+
+std::string X509ToPEMString(X509* x509) {
+    bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
+    int rc = PEM_write_bio_X509(bio.get(), x509);
+    if (rc != 1) {
+        LOG(ERROR) << "PEM_write_bio_X509 failed";
+        return "";
+    }
+
+    BUF_MEM* mem = nullptr;
+    BIO_get_mem_ptr(bio.get(), &mem);
+    if (!mem || !mem->data || !mem->length) {
+        LOG(ERROR) << "BIO_get_mem_ptr failed";
+        return "";
+    }
+
+    return std::string(mem->data, mem->length);
+}
+
+}  // namespace crypto
+}  // namespace adb
diff --git a/adb/daemon/adb_wifi.cpp b/adb/daemon/adb_wifi.cpp
new file mode 100644
index 0000000..bce303b
--- /dev/null
+++ b/adb/daemon/adb_wifi.cpp
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if !ADB_HOST
+
+#define TRACE_TAG ADB_WIRELESS
+
+#include "adb_wifi.h"
+
+#include <unistd.h>
+#include <optional>
+
+#include <adbd_auth.h>
+#include <android-base/properties.h>
+
+#include "adb.h"
+#include "daemon/mdns.h"
+#include "sysdeps.h"
+#include "transport.h"
+
+using namespace android::base;
+
+namespace {
+
+static AdbdAuthContext* auth_ctx;
+
+static void adb_disconnected(void* unused, atransport* t);
+static struct adisconnect adb_disconnect = {adb_disconnected, nullptr};
+
+static void adb_disconnected(void* unused, atransport* t) {
+    LOG(INFO) << "ADB wifi device disconnected";
+    adbd_auth_tls_device_disconnected(auth_ctx, kAdbTransportTypeWifi, t->auth_id);
+}
+
+// TODO(b/31559095): need bionic host so that we can use 'prop_info' returned
+// from WaitForProperty
+#if defined(__ANDROID__)
+
+class TlsServer {
+  public:
+    explicit TlsServer(int port);
+    virtual ~TlsServer();
+    bool Start();
+    uint16_t port() { return port_; };
+
+  private:
+    void OnFdEvent(int fd, unsigned ev);
+    static void StaticOnFdEvent(int fd, unsigned ev, void* opaque);
+
+    fdevent* fd_event_ = nullptr;
+    uint16_t port_;
+};  // TlsServer
+
+TlsServer::TlsServer(int port) : port_(port) {}
+
+TlsServer::~TlsServer() {
+    fdevent* fde = fd_event_;
+    fdevent_run_on_main_thread([fde]() {
+        if (fde != nullptr) {
+            fdevent_destroy(fde);
+        }
+    });
+}
+
+bool TlsServer::Start() {
+    std::condition_variable cv;
+    std::mutex mutex;
+    std::optional<bool> success;
+    auto callback = [&](bool result) {
+        {
+            std::lock_guard<std::mutex> lock(mutex);
+            success = result;
+        }
+        cv.notify_one();
+    };
+
+    std::string err;
+    unique_fd fd(network_inaddr_any_server(port_, SOCK_STREAM, &err));
+    if (fd.get() == -1) {
+        LOG(ERROR) << "Failed to start TLS server [" << err << "]";
+        return false;
+    }
+    close_on_exec(fd.get());
+    int port = socket_get_local_port(fd.get());
+    if (port <= 0 || port > 65535) {
+        LOG(ERROR) << "Invalid port for tls server";
+        return false;
+    }
+    port_ = static_cast<uint16_t>(port);
+    LOG(INFO) << "adbwifi started on port " << port_;
+
+    std::unique_lock<std::mutex> lock(mutex);
+    fdevent_run_on_main_thread([&]() {
+        fd_event_ = fdevent_create(fd.release(), &TlsServer::StaticOnFdEvent, this);
+        if (fd_event_ == nullptr) {
+            LOG(ERROR) << "Failed to create fd event for TlsServer.";
+            callback(false);
+            return;
+        }
+        callback(true);
+    });
+
+    cv.wait(lock, [&]() { return success.has_value(); });
+    if (!*success) {
+        LOG(INFO) << "TlsServer fdevent_create failed";
+        return false;
+    }
+    fdevent_set(fd_event_, FDE_READ);
+    LOG(INFO) << "TlsServer running on port " << port_;
+
+    return *success;
+}
+
+// static
+void TlsServer::StaticOnFdEvent(int fd, unsigned ev, void* opaque) {
+    auto server = reinterpret_cast<TlsServer*>(opaque);
+    server->OnFdEvent(fd, ev);
+}
+
+void TlsServer::OnFdEvent(int fd, unsigned ev) {
+    if ((ev & FDE_READ) == 0 || fd != fd_event_->fd.get()) {
+        LOG(INFO) << __func__ << ": No read [ev=" << ev << " fd=" << fd << "]";
+        return;
+    }
+
+    unique_fd new_fd(adb_socket_accept(fd, nullptr, nullptr));
+    if (new_fd >= 0) {
+        LOG(INFO) << "New TLS connection [fd=" << new_fd.get() << "]";
+        close_on_exec(new_fd.get());
+        disable_tcp_nagle(new_fd.get());
+        std::string serial = android::base::StringPrintf("host-%d", new_fd.get());
+        register_socket_transport(
+                std::move(new_fd), std::move(serial), port_, 1,
+                [](atransport*) { return ReconnectResult::Abort; }, true);
+    }
+}
+
+TlsServer* sTlsServer = nullptr;
+const char kWifiPortProp[] = "service.adb.tls.port";
+
+const char kWifiEnabledProp[] = "persist.adb.tls_server.enable";
+
+static void enable_wifi_debugging() {
+    start_mdnsd();
+
+    if (sTlsServer != nullptr) {
+        delete sTlsServer;
+    }
+    sTlsServer = new TlsServer(0);
+    if (!sTlsServer->Start()) {
+        LOG(ERROR) << "Failed to start TlsServer";
+        delete sTlsServer;
+        sTlsServer = nullptr;
+        return;
+    }
+
+    // Start mdns connect service for discovery
+    register_adb_secure_connect_service(sTlsServer->port());
+    LOG(INFO) << "adb wifi started on port " << sTlsServer->port();
+    SetProperty(kWifiPortProp, std::to_string(sTlsServer->port()));
+}
+
+static void disable_wifi_debugging() {
+    if (sTlsServer != nullptr) {
+        delete sTlsServer;
+        sTlsServer = nullptr;
+    }
+    if (is_adb_secure_connect_service_registered()) {
+        unregister_adb_secure_connect_service();
+    }
+    kick_all_tcp_tls_transports();
+    LOG(INFO) << "adb wifi stopped";
+    SetProperty(kWifiPortProp, "");
+}
+
+// Watches for the #kWifiEnabledProp property to toggle the TlsServer
+static void start_wifi_enabled_observer() {
+    std::thread([]() {
+        bool wifi_enabled = false;
+        while (true) {
+            std::string toggled_val = wifi_enabled ? "0" : "1";
+            LOG(INFO) << "Waiting for " << kWifiEnabledProp << "=" << toggled_val;
+            if (WaitForProperty(kWifiEnabledProp, toggled_val)) {
+                wifi_enabled = !wifi_enabled;
+                LOG(INFO) << kWifiEnabledProp << " changed to " << toggled_val;
+                if (wifi_enabled) {
+                    enable_wifi_debugging();
+                } else {
+                    disable_wifi_debugging();
+                }
+            }
+        }
+    }).detach();
+}
+#endif  //__ANDROID__
+
+}  // namespace
+
+void adbd_wifi_init(AdbdAuthContext* ctx) {
+    auth_ctx = ctx;
+#if defined(__ANDROID__)
+    start_wifi_enabled_observer();
+#endif  //__ANDROID__
+}
+
+void adbd_wifi_secure_connect(atransport* t) {
+    t->AddDisconnect(&adb_disconnect);
+    handle_online(t);
+    send_connect(t);
+    LOG(INFO) << __func__ << ": connected " << t->serial;
+    t->auth_id = adbd_auth_tls_device_connected(auth_ctx, kAdbTransportTypeWifi, t->auth_key.data(),
+                                                t->auth_key.size());
+}
+
+#endif /* !HOST */
diff --git a/adb/daemon/auth.cpp b/adb/daemon/auth.cpp
index ec4ab4a..2edf582 100644
--- a/adb/daemon/auth.cpp
+++ b/adb/daemon/auth.cpp
@@ -23,10 +23,14 @@
 #include <string.h>
 
 #include <algorithm>
+#include <chrono>
 #include <iomanip>
 #include <map>
 #include <memory>
+#include <thread>
 
+#include <adb/crypto/rsa_2048_key.h>
+#include <adb/tls/adb_ca_list.h>
 #include <adbd_auth.h>
 #include <android-base/file.h>
 #include <android-base/no_destructor.h>
@@ -35,16 +39,24 @@
 #include <openssl/obj_mac.h>
 #include <openssl/rsa.h>
 #include <openssl/sha.h>
+#include <openssl/ssl.h>
 
 #include "adb.h"
 #include "adb_auth.h"
 #include "adb_io.h"
+#include "adb_wifi.h"
 #include "fdevent/fdevent.h"
 #include "transport.h"
 #include "types.h"
 
+using namespace adb::crypto;
+using namespace adb::tls;
+using namespace std::chrono_literals;
+
 static AdbdAuthContext* auth_ctx;
 
+static RSA* rsa_pkey = nullptr;
+
 static void adb_disconnected(void* unused, atransport* t);
 static struct adisconnect adb_disconnect = {adb_disconnected, nullptr};
 
@@ -85,12 +97,61 @@
 static void IteratePublicKeys(std::function<bool(std::string_view public_key)> f) {
     adbd_auth_get_public_keys(
             auth_ctx,
-            [](const char* public_key, size_t len, void* arg) {
-                return (*static_cast<decltype(f)*>(arg))(std::string_view(public_key, len));
+            [](void* opaque, const char* public_key, size_t len) {
+                return (*static_cast<decltype(f)*>(opaque))(std::string_view(public_key, len));
             },
             &f);
 }
 
+bssl::UniquePtr<STACK_OF(X509_NAME)> adbd_tls_client_ca_list() {
+    if (!auth_required) {
+        return nullptr;
+    }
+
+    bssl::UniquePtr<STACK_OF(X509_NAME)> ca_list(sk_X509_NAME_new_null());
+
+    IteratePublicKeys([&](std::string_view public_key) {
+        // TODO: do we really have to support both ' ' and '\t'?
+        std::vector<std::string> split = android::base::Split(std::string(public_key), " \t");
+        uint8_t keybuf[ANDROID_PUBKEY_ENCODED_SIZE + 1];
+        const std::string& pubkey = split[0];
+        if (b64_pton(pubkey.c_str(), keybuf, sizeof(keybuf)) != ANDROID_PUBKEY_ENCODED_SIZE) {
+            LOG(ERROR) << "Invalid base64 key " << pubkey;
+            return true;
+        }
+
+        RSA* key = nullptr;
+        if (!android_pubkey_decode(keybuf, ANDROID_PUBKEY_ENCODED_SIZE, &key)) {
+            LOG(ERROR) << "Failed to parse key " << pubkey;
+            return true;
+        }
+        bssl::UniquePtr<RSA> rsa_key(key);
+
+        unsigned char* dkey = nullptr;
+        int len = i2d_RSA_PUBKEY(rsa_key.get(), &dkey);
+        if (len <= 0 || dkey == nullptr) {
+            LOG(ERROR) << "Failed to encode RSA public key";
+            return true;
+        }
+
+        uint8_t digest[SHA256_DIGEST_LENGTH];
+        // Put the encoded key in the commonName attribute of the issuer name.
+        // Note that the commonName has a max length of 64 bytes, which is less
+        // than the SHA256_DIGEST_LENGTH.
+        SHA256(dkey, len, digest);
+        OPENSSL_free(dkey);
+
+        auto digest_str = SHA256BitsToHexString(
+                std::string_view(reinterpret_cast<const char*>(&digest[0]), sizeof(digest)));
+        LOG(INFO) << "fingerprint=[" << digest_str << "]";
+        auto issuer = CreateCAIssuerFromEncodedKey(digest_str);
+        CHECK(bssl::PushToStack(ca_list.get(), std::move(issuer)));
+        return true;
+    });
+
+    return ca_list;
+}
+
 bool adbd_auth_verify(const char* token, size_t token_size, const std::string& sig,
                       std::string* auth_key) {
     bool authorized = false;
@@ -159,11 +220,20 @@
     });
 }
 
+static void adbd_key_removed(const char* public_key, size_t len) {
+    // The framework removed the key from its keystore. We need to disconnect all
+    // devices using that key. Search by t->auth_key
+    std::string_view auth_key(public_key, len);
+    kick_all_transports_by_auth_key(auth_key);
+}
+
 void adbd_auth_init(void) {
-    AdbdAuthCallbacks cb;
+    AdbdAuthCallbacksV1 cb;
     cb.version = 1;
-    cb.callbacks.v1.key_authorized = adbd_auth_key_authorized;
+    cb.key_authorized = adbd_auth_key_authorized;
+    cb.key_removed = adbd_key_removed;
     auth_ctx = adbd_auth_new(&cb);
+    adbd_wifi_init(auth_ctx);
     std::thread([]() {
         adb_thread_setname("adbd auth");
         adbd_auth_run(auth_ctx);
@@ -206,5 +276,89 @@
 }
 
 void adbd_notify_framework_connected_key(atransport* t) {
-    adbd_auth_notify_auth(auth_ctx, t->auth_key.data(), t->auth_key.size());
+    t->auth_id = adbd_auth_notify_auth(auth_ctx, t->auth_key.data(), t->auth_key.size());
+}
+
+int adbd_tls_verify_cert(X509_STORE_CTX* ctx, std::string* auth_key) {
+    if (!auth_required) {
+        // Any key will do.
+        LOG(INFO) << __func__ << ": auth not required";
+        return 1;
+    }
+
+    bool authorized = false;
+    X509* cert = X509_STORE_CTX_get0_cert(ctx);
+    if (cert == nullptr) {
+        LOG(INFO) << "got null x509 certificate";
+        return 0;
+    }
+    bssl::UniquePtr<EVP_PKEY> evp_pkey(X509_get_pubkey(cert));
+    if (evp_pkey == nullptr) {
+        LOG(INFO) << "got null evp_pkey from x509 certificate";
+        return 0;
+    }
+
+    IteratePublicKeys([&](std::string_view public_key) {
+        // TODO: do we really have to support both ' ' and '\t'?
+        std::vector<std::string> split = android::base::Split(std::string(public_key), " \t");
+        uint8_t keybuf[ANDROID_PUBKEY_ENCODED_SIZE + 1];
+        const std::string& pubkey = split[0];
+        if (b64_pton(pubkey.c_str(), keybuf, sizeof(keybuf)) != ANDROID_PUBKEY_ENCODED_SIZE) {
+            LOG(ERROR) << "Invalid base64 key " << pubkey;
+            return true;
+        }
+
+        RSA* key = nullptr;
+        if (!android_pubkey_decode(keybuf, ANDROID_PUBKEY_ENCODED_SIZE, &key)) {
+            LOG(ERROR) << "Failed to parse key " << pubkey;
+            return true;
+        }
+
+        bool verified = false;
+        bssl::UniquePtr<EVP_PKEY> known_evp(EVP_PKEY_new());
+        EVP_PKEY_set1_RSA(known_evp.get(), key);
+        if (EVP_PKEY_cmp(known_evp.get(), evp_pkey.get())) {
+            LOG(INFO) << "Matched auth_key=" << public_key;
+            verified = true;
+        } else {
+            LOG(INFO) << "auth_key doesn't match [" << public_key << "]";
+        }
+        RSA_free(key);
+        if (verified) {
+            *auth_key = public_key;
+            authorized = true;
+            return false;
+        }
+
+        return true;
+    });
+
+    return authorized ? 1 : 0;
+}
+
+void adbd_auth_tls_handshake(atransport* t) {
+    if (rsa_pkey == nullptr) {
+        // Generate a random RSA key to feed into the X509 certificate
+        auto rsa_2048 = CreateRSA2048Key();
+        CHECK(rsa_2048.has_value());
+        rsa_pkey = EVP_PKEY_get1_RSA(rsa_2048->GetEvpPkey());
+        CHECK(rsa_pkey);
+    }
+
+    std::thread([t]() {
+        std::string auth_key;
+        if (t->connection()->DoTlsHandshake(rsa_pkey, &auth_key)) {
+            LOG(INFO) << "auth_key=" << auth_key;
+            if (t->IsTcpDevice()) {
+                t->auth_key = auth_key;
+                adbd_wifi_secure_connect(t);
+            } else {
+                adbd_auth_verified(t);
+                adbd_notify_framework_connected_key(t);
+            }
+        } else {
+            // Only allow one attempt at the handshake.
+            t->Kick();
+        }
+    }).detach();
 }
diff --git a/adb/daemon/file_sync_service.cpp b/adb/daemon/file_sync_service.cpp
index d6af708..edf5683 100644
--- a/adb/daemon/file_sync_service.cpp
+++ b/adb/daemon/file_sync_service.cpp
@@ -40,10 +40,13 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 
-#include <private/android_filesystem_config.h>
+#include <adbd_fs.h>
+
+// Needed for __android_log_security_bswrite.
 #include <private/android_logger.h>
 
 #if defined(__ANDROID__)
+#include <linux/capability.h>
 #include <selinux/android.h>
 #include <sys/xattr.h>
 #endif
@@ -98,7 +101,7 @@
     for (const auto& path_component : path_components) {
         uid_t uid = -1;
         gid_t gid = -1;
-        unsigned int mode = 0775;
+        mode_t mode = 0775;
         uint64_t capabilities = 0;
 
         if (path_component.empty()) {
@@ -111,7 +114,7 @@
         partial_path += path_component;
 
         if (should_use_fs_config(partial_path)) {
-            fs_config(partial_path.c_str(), 1, nullptr, &uid, &gid, &mode, &capabilities);
+            adbd_fs_config(partial_path.c_str(), 1, nullptr, &uid, &gid, &mode, &capabilities);
         }
         if (adb_mkdir(partial_path.c_str(), mode) == -1) {
             if (errno != EEXIST) {
@@ -468,9 +471,7 @@
         gid_t gid = -1;
         uint64_t capabilities = 0;
         if (should_use_fs_config(path)) {
-            unsigned int broken_api_hack = mode;
-            fs_config(path.c_str(), 0, nullptr, &uid, &gid, &broken_api_hack, &capabilities);
-            mode = broken_api_hack;
+            adbd_fs_config(path.c_str(), 0, nullptr, &uid, &gid, &mode, &capabilities);
         }
 
         result = handle_send_file(s, path.c_str(), &timestamp, uid, gid, capabilities, mode, buffer,
@@ -550,7 +551,6 @@
 static bool handle_sync_command(int fd, std::vector<char>& buffer) {
     D("sync: waiting for request");
 
-    ATRACE_CALL();
     SyncRequest request;
     if (!ReadFdExactly(fd, &request, sizeof(request))) {
         SendSyncFail(fd, "command read failure");
@@ -569,8 +569,6 @@
     name[path_length] = 0;
 
     std::string id_name = sync_id_to_name(request.id);
-    std::string trace_name = StringPrintf("%s(%s)", id_name.c_str(), name);
-    ATRACE_NAME(trace_name.c_str());
 
     D("sync: %s('%s')", id_name.c_str(), name);
     switch (request.id) {
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 3322574..9e02e89 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -53,6 +53,7 @@
 #include "adb_auth.h"
 #include "adb_listeners.h"
 #include "adb_utils.h"
+#include "adb_wifi.h"
 #include "socket_spec.h"
 #include "transport.h"
 
@@ -196,6 +197,7 @@
     if (port == -1) {
         port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
     }
+    LOG(INFO) << "Setup mdns on port= " << port;
     setup_mdns(port);
 #endif
     for (const auto& addr : addrs) {
@@ -317,9 +319,10 @@
 
     while (true) {
         static struct option opts[] = {
-            {"root_seclabel", required_argument, nullptr, 's'},
-            {"device_banner", required_argument, nullptr, 'b'},
-            {"version", no_argument, nullptr, 'v'},
+                {"root_seclabel", required_argument, nullptr, 's'},
+                {"device_banner", required_argument, nullptr, 'b'},
+                {"version", no_argument, nullptr, 'v'},
+                {"logpostfsdata", no_argument, nullptr, 'l'},
         };
 
         int option_index = 0;
@@ -341,6 +344,9 @@
                 printf("Android Debug Bridge Daemon version %d.%d.%d\n", ADB_VERSION_MAJOR,
                        ADB_VERSION_MINOR, ADB_SERVER_VERSION);
                 return 0;
+            case 'l':
+                LOG(ERROR) << "post-fs-data triggered";
+                return 0;
             default:
                 // getopt already prints "adbd: invalid option -- %c" for us.
                 return 1;
diff --git a/adb/daemon/mdns.cpp b/adb/daemon/mdns.cpp
index 3530f48..fa692c0 100644
--- a/adb/daemon/mdns.cpp
+++ b/adb/daemon/mdns.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "mdns.h"
 #include "adb_mdns.h"
 #include "sysdeps.h"
 
@@ -23,6 +24,7 @@
 
 #include <chrono>
 #include <mutex>
+#include <random>
 #include <thread>
 
 #include <android-base/logging.h>
@@ -32,10 +34,10 @@
 
 static std::mutex& mdns_lock = *new std::mutex();
 static int port;
-static DNSServiceRef mdns_ref;
-static bool mdns_registered = false;
+static DNSServiceRef mdns_refs[kNumADBDNSServices];
+static bool mdns_registered[kNumADBDNSServices];
 
-static void start_mdns() {
+void start_mdnsd() {
     if (android::base::GetProperty("init.svc.mdnsd", "") == "running") {
         return;
     }
@@ -60,38 +62,158 @@
     }
 }
 
-static void setup_mdns_thread() {
-    start_mdns();
+static void register_mdns_service(int index, int port, const std::string service_name) {
     std::lock_guard<std::mutex> lock(mdns_lock);
 
-    std::string hostname = "adb-";
-    hostname += android::base::GetProperty("ro.serialno", "unidentified");
 
-    auto error = DNSServiceRegister(&mdns_ref, 0, 0, hostname.c_str(),
-                                    kADBServiceType, nullptr, nullptr,
-                                    htobe16((uint16_t)port), 0, nullptr,
-                                    mdns_callback, nullptr);
+    // https://tools.ietf.org/html/rfc6763
+    // """
+    // The format of the data within a DNS TXT record is one or more
+    // strings, packed together in memory without any intervening gaps or
+    // padding bytes for word alignment.
+    //
+    // The format of each constituent string within the DNS TXT record is a
+    // single length byte, followed by 0-255 bytes of text data.
+    // """
+    //
+    // Therefore:
+    // 1. Begin with the string length
+    // 2. No null termination
+
+    std::vector<char> txtRecord;
+
+    if (kADBDNSServiceTxtRecords[index]) {
+        size_t txtRecordStringLength = strlen(kADBDNSServiceTxtRecords[index]);
+
+        txtRecord.resize(1 +                    // length byte
+                         txtRecordStringLength  // string bytes
+        );
+
+        txtRecord[0] = (char)txtRecordStringLength;
+        memcpy(txtRecord.data() + 1, kADBDNSServiceTxtRecords[index], txtRecordStringLength);
+    }
+
+    auto error = DNSServiceRegister(
+            &mdns_refs[index], 0, 0, service_name.c_str(), kADBDNSServices[index], nullptr, nullptr,
+            htobe16((uint16_t)port), (uint16_t)txtRecord.size(),
+            txtRecord.empty() ? nullptr : txtRecord.data(), mdns_callback, nullptr);
 
     if (error != kDNSServiceErr_NoError) {
-        LOG(ERROR) << "Could not register mDNS service (" << error << ").";
-        return;
+        LOG(ERROR) << "Could not register mDNS service " << kADBDNSServices[index] << ", error ("
+                   << error << ").";
+        mdns_registered[index] = false;
     }
 
-    mdns_registered = true;
+    mdns_registered[index] = true;
+
+    LOG(INFO) << "adbd mDNS service " << kADBDNSServices[index]
+              << " registered: " << mdns_registered[index];
 }
 
-static void teardown_mdns() {
+static void unregister_mdns_service(int index) {
     std::lock_guard<std::mutex> lock(mdns_lock);
 
-    if (mdns_registered) {
-        DNSServiceRefDeallocate(mdns_ref);
+    if (mdns_registered[index]) {
+        DNSServiceRefDeallocate(mdns_refs[index]);
     }
 }
 
+static void register_base_mdns_transport() {
+    std::string hostname = "adb-";
+    hostname += android::base::GetProperty("ro.serialno", "unidentified");
+    register_mdns_service(kADBTransportServiceRefIndex, port, hostname);
+}
+
+static void setup_mdns_thread() {
+    start_mdnsd();
+
+    // We will now only set up the normal transport mDNS service
+    // instead of registering all the adb secure mDNS services
+    // in the beginning. This is to provide more privacy/security.
+    register_base_mdns_transport();
+}
+
+// This also tears down any adb secure mDNS services, if they exist.
+static void teardown_mdns() {
+    for (int i = 0; i < kNumADBDNSServices; ++i) {
+        unregister_mdns_service(i);
+    }
+}
+
+static std::string RandomAlphaNumString(size_t len) {
+    std::string ret;
+    std::random_device rd;
+    std::mt19937 mt(rd());
+    // Generate values starting with zero and then up to enough to cover numeric
+    // digits, small letters and capital letters (26 each).
+    std::uniform_int_distribution<uint8_t> dist(0, 61);
+    for (size_t i = 0; i < len; ++i) {
+        uint8_t val = dist(mt);
+        if (val < 10) {
+            ret += '0' + val;
+        } else if (val < 36) {
+            ret += 'A' + (val - 10);
+        } else {
+            ret += 'a' + (val - 36);
+        }
+    }
+    return ret;
+}
+
+static std::string GenerateDeviceGuid() {
+    // The format is adb-<serial_no>-<six-random-alphanum>
+    std::string guid = "adb-";
+
+    std::string serial = android::base::GetProperty("ro.serialno", "");
+    if (serial.empty()) {
+        // Generate 16-bytes of random alphanum string
+        serial = RandomAlphaNumString(16);
+    }
+    guid += serial + '-';
+    // Random six-char suffix
+    guid += RandomAlphaNumString(6);
+    return guid;
+}
+
+static std::string ReadDeviceGuid() {
+    std::string guid = android::base::GetProperty("persist.adb.wifi.guid", "");
+    if (guid.empty()) {
+        guid = GenerateDeviceGuid();
+        CHECK(!guid.empty());
+        android::base::SetProperty("persist.adb.wifi.guid", guid);
+    }
+    return guid;
+}
+
+// Public interface/////////////////////////////////////////////////////////////
+
 void setup_mdns(int port_in) {
+    // Make sure the adb wifi guid is generated.
+    std::string guid = ReadDeviceGuid();
+    CHECK(!guid.empty());
     port = port_in;
     std::thread(setup_mdns_thread).detach();
 
     // TODO: Make this more robust against a hard kill.
     atexit(teardown_mdns);
 }
+
+void register_adb_secure_connect_service(int port) {
+    std::thread([port]() {
+        auto service_name = ReadDeviceGuid();
+        if (service_name.empty()) {
+            return;
+        }
+        LOG(INFO) << "Registering secure_connect service (" << service_name << ")";
+        register_mdns_service(kADBSecureConnectServiceRefIndex, port, service_name);
+    }).detach();
+}
+
+void unregister_adb_secure_connect_service() {
+    std::thread([]() { unregister_mdns_service(kADBSecureConnectServiceRefIndex); }).detach();
+}
+
+bool is_adb_secure_connect_service_registered() {
+    std::lock_guard<std::mutex> lock(mdns_lock);
+    return mdns_registered[kADBSecureConnectServiceRefIndex];
+}
diff --git a/adb/daemon/mdns.h b/adb/daemon/mdns.h
index 4c6b1ca..e7e7a62 100644
--- a/adb/daemon/mdns.h
+++ b/adb/daemon/mdns.h
@@ -19,4 +19,9 @@
 
 void setup_mdns(int port);
 
+void register_adb_secure_connect_service(int port);
+void unregister_adb_secure_connect_service();
+bool is_adb_secure_connect_service_registered();
+
+void start_mdnsd();
 #endif  // _DAEMON_MDNS_H_
diff --git a/adb/daemon/transport_qemu.cpp b/adb/daemon/transport_qemu.cpp
index 901efee..e458cea 100644
--- a/adb/daemon/transport_qemu.cpp
+++ b/adb/daemon/transport_qemu.cpp
@@ -105,8 +105,9 @@
                  * exchange. */
                 std::string serial = android::base::StringPrintf("host-%d", fd.get());
                 WriteFdExactly(fd.get(), _start_req, strlen(_start_req));
-                register_socket_transport(std::move(fd), std::move(serial), port, 1,
-                                          [](atransport*) { return ReconnectResult::Abort; });
+                register_socket_transport(
+                        std::move(fd), std::move(serial), port, 1,
+                        [](atransport*) { return ReconnectResult::Abort; }, false);
             }
 
             /* Prepare for accepting of the next ADB host connection. */
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index a9ad805..0928198 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -19,6 +19,7 @@
 #include "sysdeps.h"
 
 #include <errno.h>
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -260,6 +261,12 @@
         CHECK_EQ(static_cast<size_t>(rc), sizeof(notify));
     }
 
+    virtual bool DoTlsHandshake(RSA* key, std::string* auth_key) override final {
+        // TODO: support TLS for usb connections.
+        LOG(FATAL) << "Not supported yet.";
+        return false;
+    }
+
   private:
     void StartMonitor() {
         // This is a bit of a mess.
diff --git a/adb/fastdeploy/deploypatchgenerator/apk_archive.cpp b/adb/fastdeploy/deploypatchgenerator/apk_archive.cpp
index 932d579..9da256e 100644
--- a/adb/fastdeploy/deploypatchgenerator/apk_archive.cpp
+++ b/adb/fastdeploy/deploypatchgenerator/apk_archive.cpp
@@ -18,6 +18,8 @@
 
 #include "apk_archive.h"
 
+#include <inttypes.h>
+
 #include "adb_trace.h"
 #include "sysdeps.h"
 
diff --git a/adb/fdevent/fdevent_test.h b/adb/fdevent/fdevent_test.h
index 2139d0f..ecda4da 100644
--- a/adb/fdevent/fdevent_test.h
+++ b/adb/fdevent/fdevent_test.h
@@ -48,6 +48,12 @@
   protected:
     unique_fd dummy;
 
+    ~FdeventTest() {
+        if (thread_.joinable()) {
+            TerminateThread();
+        }
+    }
+
     static void SetUpTestCase() {
 #if !defined(_WIN32)
         ASSERT_NE(SIG_ERR, signal(SIGPIPE, SIG_IGN));
diff --git a/adb/libs/.clang-format b/adb/libs/.clang-format
new file mode 120000
index 0000000..e545823
--- /dev/null
+++ b/adb/libs/.clang-format
@@ -0,0 +1 @@
+../../.clang-format-2
\ No newline at end of file
diff --git a/adb/libs/adbconnection/.clang-format b/adb/libs/adbconnection/.clang-format
new file mode 120000
index 0000000..e545823
--- /dev/null
+++ b/adb/libs/adbconnection/.clang-format
@@ -0,0 +1 @@
+../../.clang-format-2
\ No newline at end of file
diff --git a/adb/libs/adbconnection/Android.bp b/adb/libs/adbconnection/Android.bp
new file mode 100644
index 0000000..f6b0a42
--- /dev/null
+++ b/adb/libs/adbconnection/Android.bp
@@ -0,0 +1,59 @@
+// libadbconnection
+// =========================================================
+// libadbconnection_client/server implement the socket handling for jdwp
+// forwarding and the track-jdwp service.
+cc_library {
+    name: "libadbconnection_server",
+    srcs: ["adbconnection_server.cpp"],
+
+    export_include_dirs: ["include"],
+
+    stl: "libc++_static",
+    shared_libs: ["liblog"],
+    static_libs: ["libbase"],
+
+    defaults: ["adbd_defaults", "host_adbd_supported"],
+
+    // Avoid getting duplicate symbol of android::build::GetBuildNumber().
+    use_version_lib: false,
+
+    recovery_available: true,
+    compile_multilib: "both",
+}
+
+cc_library {
+    name: "libadbconnection_client",
+    srcs: ["adbconnection_client.cpp"],
+
+    export_include_dirs: ["include"],
+
+    stl: "libc++_static",
+    shared_libs: ["liblog"],
+    static_libs: ["libbase"],
+
+    defaults: ["adbd_defaults"],
+    visibility: [
+        "//art:__subpackages__",
+        "//system/core/adb/apex:__subpackages__",
+    ],
+    apex_available: [
+        "com.android.adbd",
+        "test_com.android.adbd",
+    ],
+
+    // libadbconnection_client doesn't need an embedded build number.
+    use_version_lib: false,
+
+    target: {
+        linux: {
+            version_script: "libadbconnection_client.map.txt",
+        },
+    },
+    stubs: {
+        symbol_file: "libadbconnection_client.map.txt",
+        versions: ["1"],
+    },
+
+    host_supported: true,
+    compile_multilib: "both",
+}
diff --git a/adb/libs/adbconnection/adbconnection_client.cpp b/adb/libs/adbconnection/adbconnection_client.cpp
new file mode 100644
index 0000000..ee48abb
--- /dev/null
+++ b/adb/libs/adbconnection/adbconnection_client.cpp
@@ -0,0 +1,148 @@
+/*
+ * 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 "adbconnection/client.h"
+
+#include <pwd.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <memory>
+#include <optional>
+
+#include <android-base/cmsg.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+using android::base::unique_fd;
+
+static constexpr char kJdwpControlName[] = "\0jdwp-control";
+
+struct AdbConnectionClientContext {
+  unique_fd control_socket_;
+};
+
+bool SocketPeerIsTrusted(int fd) {
+  ucred cr;
+  socklen_t cr_length = sizeof(cr);
+  if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &cr_length) != 0) {
+    PLOG(ERROR) << "couldn't get socket credentials";
+    return false;
+  }
+
+  passwd* shell = getpwnam("shell");
+  if (cr.uid != 0 && cr.uid != shell->pw_uid) {
+    LOG(ERROR) << "untrusted uid " << cr.uid << " on other end of socket";
+    return false;
+  }
+
+  return true;
+}
+
+AdbConnectionClientContext* adbconnection_client_new(
+    const AdbConnectionClientInfo* const* info_elems, size_t info_count) {
+  auto ctx = std::make_unique<AdbConnectionClientContext>();
+
+  std::optional<uint64_t> pid;
+  std::optional<bool> debuggable;
+
+  for (size_t i = 0; i < info_count; ++i) {
+    auto info = info_elems[i];
+    switch (info->type) {
+      case AdbConnectionClientInfoType::pid:
+        if (pid) {
+          LOG(ERROR) << "multiple pid entries in AdbConnectionClientInfo, ignoring";
+          continue;
+        }
+        pid = info->data.pid;
+        break;
+
+      case AdbConnectionClientInfoType::debuggable:
+        if (debuggable) {
+          LOG(ERROR) << "multiple debuggable entries in AdbConnectionClientInfo, ignoring";
+          continue;
+        }
+        debuggable = info->data.pid;
+        break;
+    }
+  }
+
+  if (!pid) {
+    LOG(ERROR) << "AdbConnectionClientInfo missing required field pid";
+    return nullptr;
+  }
+
+  if (!debuggable) {
+    LOG(ERROR) << "AdbConnectionClientInfo missing required field debuggable";
+    return nullptr;
+  }
+
+  ctx->control_socket_.reset(socket(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0));
+  if (ctx->control_socket_ < 0) {
+    PLOG(ERROR) << "failed to create Unix domain socket";
+    return nullptr;
+  }
+
+  struct timeval timeout;
+  timeout.tv_sec = 1;
+  timeout.tv_usec = 0;
+  setsockopt(ctx->control_socket_.get(), SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
+
+  sockaddr_un addr = {};
+  addr.sun_family = AF_UNIX;
+  memcpy(addr.sun_path, kJdwpControlName, sizeof(kJdwpControlName));
+  size_t addr_len = offsetof(sockaddr_un, sun_path) + sizeof(kJdwpControlName) - 1;
+
+  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";
+    return nullptr;
+  }
+
+  bool trusted = SocketPeerIsTrusted(ctx->control_socket_.get());
+  if (!trusted) {
+    LOG(ERROR) << "adb socket is not trusted, aborting connection";
+    return nullptr;
+  }
+
+  uint32_t pid_u32 = static_cast<uint32_t>(*pid);
+  rc = TEMP_FAILURE_RETRY(write(ctx->control_socket_.get(), &pid_u32, sizeof(pid_u32)));
+  if (rc != sizeof(pid_u32)) {
+    PLOG(ERROR) << "failed to send JDWP process pid to adbd";
+  }
+
+  return ctx.release();
+}
+
+void adbconnection_client_destroy(AdbConnectionClientContext* ctx) {
+  delete ctx;
+}
+
+int adbconnection_client_pollfd(AdbConnectionClientContext* ctx) {
+  return ctx->control_socket_.get();
+}
+
+int adbconnection_client_receive_jdwp_fd(AdbConnectionClientContext* ctx) {
+  char dummy;
+  unique_fd jdwp_fd;
+  ssize_t rc = android::base::ReceiveFileDescriptors(ctx->control_socket_, &dummy, 1, &jdwp_fd);
+  if (rc != 1) {
+    return rc;
+  }
+  return jdwp_fd.release();
+}
diff --git a/adb/libs/adbconnection/adbconnection_server.cpp b/adb/libs/adbconnection/adbconnection_server.cpp
new file mode 100644
index 0000000..939da2f
--- /dev/null
+++ b/adb/libs/adbconnection/adbconnection_server.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "adbconnection/server.h"
+
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <array>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+using android::base::unique_fd;
+
+#define JDWP_CONTROL_NAME "\0jdwp-control"
+#define JDWP_CONTROL_NAME_LEN (sizeof(JDWP_CONTROL_NAME) - 1)
+
+static_assert(JDWP_CONTROL_NAME_LEN <= sizeof(reinterpret_cast<sockaddr_un*>(0)->sun_path));
+
+// Listen for incoming jdwp clients forever.
+void adbconnection_listen(void (*callback)(int fd, pid_t pid)) {
+  sockaddr_un addr = {};
+  socklen_t addrlen = JDWP_CONTROL_NAME_LEN + sizeof(addr.sun_family);
+
+  addr.sun_family = AF_UNIX;
+  memcpy(addr.sun_path, JDWP_CONTROL_NAME, JDWP_CONTROL_NAME_LEN);
+
+  unique_fd s(socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0));
+  if (s < 0) {
+    PLOG(ERROR) << "failed to create JDWP control socket";
+    return;
+  }
+
+  if (bind(s.get(), reinterpret_cast<sockaddr*>(&addr), addrlen) < 0) {
+    PLOG(ERROR) << "failed to bind JDWP control socket";
+    return;
+  }
+
+  if (listen(s.get(), 4) < 0) {
+    PLOG(ERROR) << "failed to listen on JDWP control socket";
+    return;
+  }
+
+  std::vector<unique_fd> pending_connections;
+
+  unique_fd epfd(epoll_create1(EPOLL_CLOEXEC));
+  std::array<epoll_event, 16> events;
+
+  events[0].events = EPOLLIN;
+  events[0].data.fd = -1;
+  if (epoll_ctl(epfd.get(), EPOLL_CTL_ADD, s.get(), &events[0]) != 0) {
+    LOG(FATAL) << "failed to register event with epoll fd";
+  }
+
+  while (true) {
+    int epoll_rc = TEMP_FAILURE_RETRY(epoll_wait(epfd.get(), events.data(), events.size(), -1));
+    if (epoll_rc == -1) {
+      PLOG(FATAL) << "epoll_wait failed";
+    }
+
+    for (int i = 0; i < epoll_rc; ++i) {
+      const epoll_event& event = events[i];
+      if (event.data.fd == -1) {
+        unique_fd client(
+            TEMP_FAILURE_RETRY(accept4(s.get(), nullptr, nullptr, SOCK_NONBLOCK | SOCK_CLOEXEC)));
+
+        if (client == -1) {
+          PLOG(WARNING) << "failed to accept client on JDWP control socket";
+          continue;
+        }
+
+        epoll_event register_event;
+        register_event.events = EPOLLIN;
+        register_event.data.fd = client.get();
+
+        if (epoll_ctl(epfd.get(), EPOLL_CTL_ADD, client.get(), &register_event) != 0) {
+          PLOG(FATAL) << "failed to register JDWP client with epoll";
+        }
+
+        pending_connections.emplace_back(std::move(client));
+      } else {
+        // n^2, but the backlog should be short.
+        auto it = std::find_if(pending_connections.begin(), pending_connections.end(),
+                               [&](const unique_fd& fd) { return fd.get() == event.data.fd; });
+
+        if (it == pending_connections.end()) {
+          LOG(FATAL) << "failed to find JDWP client (" << event.data.fd
+                     << ") in pending connections";
+        }
+
+        // Massively oversized buffer: we're expecting an int32_t from the other end.
+        char buf[32];
+        int rc = TEMP_FAILURE_RETRY(recv(it->get(), buf, sizeof(buf), MSG_DONTWAIT));
+        if (rc != 4) {
+          LOG(ERROR) << "received data of incorrect size from JDWP client: read " << rc
+                     << ", expected 4";
+        } else {
+          int32_t pid;
+          memcpy(&pid, buf, sizeof(pid));
+          callback(it->release(), static_cast<pid_t>(pid));
+        }
+
+        if (epoll_ctl(epfd.get(), EPOLL_CTL_DEL, event.data.fd, nullptr) != 0) {
+          LOG(FATAL) << "failed to delete fd from JDWP epoll fd";
+        }
+
+        pending_connections.erase(it);
+      }
+    }
+  }
+}
diff --git a/adb/libs/adbconnection/include/adbconnection/client.h b/adb/libs/adbconnection/include/adbconnection/client.h
new file mode 100644
index 0000000..692fea0
--- /dev/null
+++ b/adb/libs/adbconnection/include/adbconnection/client.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <android-base/unique_fd.h>
+
+extern "C" {
+
+struct AdbConnectionClientContext;
+
+enum AdbConnectionClientInfoType {
+  pid,
+  debuggable,
+};
+
+struct AdbConnectionClientInfo {
+  AdbConnectionClientInfoType type;
+  union {
+    uint64_t pid;
+    bool debuggable;
+  } data;
+};
+
+// Construct a context and connect to adbd.
+// Returns null if we fail to connect to adbd.
+AdbConnectionClientContext* adbconnection_client_new(
+    const AdbConnectionClientInfo* const* info_elems, size_t info_count);
+
+void adbconnection_client_destroy(AdbConnectionClientContext* ctx);
+
+// Get an fd which can be polled upon to detect when a jdwp socket is available.
+// You do not own this fd. Do not close it.
+int adbconnection_client_pollfd(AdbConnectionClientContext* ctx);
+
+// Receive a jdwp client fd.
+// Ownership is transferred to the caller of this function.
+int adbconnection_client_receive_jdwp_fd(AdbConnectionClientContext* ctx);
+}
diff --git a/adb/libs/adbconnection/include/adbconnection/server.h b/adb/libs/adbconnection/include/adbconnection/server.h
new file mode 100644
index 0000000..57ca6cd
--- /dev/null
+++ b/adb/libs/adbconnection/include/adbconnection/server.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+#include <android-base/unique_fd.h>
+
+extern "C" {
+
+void adbconnection_listen(void (*callback)(int fd, pid_t pid));
+}
diff --git a/adb/libs/adbconnection/libadbconnection_client.map.txt b/adb/libs/adbconnection/libadbconnection_client.map.txt
new file mode 100644
index 0000000..153a0e4
--- /dev/null
+++ b/adb/libs/adbconnection/libadbconnection_client.map.txt
@@ -0,0 +1,25 @@
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LIBADBCONNECTION_CLIENT_1 {
+  global:
+    adbconnection_client_new;
+    adbconnection_client_destroy;
+    adbconnection_client_pollfd;
+    adbconnection_client_receive_jdwp_fd;
+  local:
+    *;
+};
diff --git a/adb/libs/libadbd_fs/Android.bp b/adb/libs/libadbd_fs/Android.bp
new file mode 100644
index 0000000..d178148
--- /dev/null
+++ b/adb/libs/libadbd_fs/Android.bp
@@ -0,0 +1,30 @@
+// libadbd_fs
+// =========================================================
+cc_library {
+    name: "libadbd_fs",
+    defaults: ["adbd_defaults"],
+
+    srcs: ["adbd_fs.cpp"],
+    static_libs: [
+        "libbase",
+        "libcutils",
+        "liblog",
+    ],
+    export_include_dirs: ["include"],
+
+    version_script: "libadbd_fs.map.txt",
+    stubs: {
+        versions: ["1"],
+        symbol_file: "libadbd_fs.map.txt",
+    },
+
+    host_supported: true,
+    recovery_available: true,
+    compile_multilib: "both",
+
+    target: {
+        darwin: {
+            enabled: false,
+        }
+    },
+}
diff --git a/adb/libs/libadbd_fs/adbd_fs.cpp b/adb/libs/libadbd_fs/adbd_fs.cpp
new file mode 100644
index 0000000..8e62d40
--- /dev/null
+++ b/adb/libs/libadbd_fs/adbd_fs.cpp
@@ -0,0 +1,30 @@
+/*
+ * 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 <adbd_fs.h>
+
+#include <private/fs_config.h>
+
+void adbd_fs_config(const char* path, int dir, const char* target_out_path, uid_t* uid, gid_t* gid,
+                    mode_t* mode, uint64_t* capabilities) {
+  unsigned uid_hack;
+  unsigned gid_hack;
+  unsigned mode_hack;
+  fs_config(path, dir, target_out_path, &uid_hack, &gid_hack, &mode_hack, capabilities);
+  *uid = uid_hack;
+  *gid = gid_hack;
+  *mode = mode_hack;
+}
diff --git a/adb/libs/libadbd_fs/include/adbd_fs.h b/adb/libs/libadbd_fs/include/adbd_fs.h
new file mode 100644
index 0000000..6158d72
--- /dev/null
+++ b/adb/libs/libadbd_fs/include/adbd_fs.h
@@ -0,0 +1,26 @@
+/*
+ * 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 <stdint.h>
+#include <sys/types.h>
+
+extern "C" {
+// Thin wrapper around libcutils fs_config.
+void adbd_fs_config(const char* path, int dir, const char* target_out_path, uid_t* uid, gid_t* gid,
+                    mode_t* mode, uint64_t* capabilities);
+}
diff --git a/adb/libs/libadbd_fs/libadbd_fs.map.txt b/adb/libs/libadbd_fs/libadbd_fs.map.txt
new file mode 100644
index 0000000..1454e96
--- /dev/null
+++ b/adb/libs/libadbd_fs/libadbd_fs.map.txt
@@ -0,0 +1,6 @@
+LIBADBD_FS {
+  global:
+    adbd_fs_config; # apex
+  local:
+    *;
+};
diff --git a/adb/pairing_auth/Android.bp b/adb/pairing_auth/Android.bp
new file mode 100644
index 0000000..0850047
--- /dev/null
+++ b/adb/pairing_auth/Android.bp
@@ -0,0 +1,83 @@
+// 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.
+
+cc_defaults {
+    name: "libadb_pairing_auth_defaults",
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Wthread-safety",
+        "-Werror",
+    ],
+
+    compile_multilib: "both",
+
+    srcs: [
+        "aes_128_gcm.cpp",
+        "pairing_auth.cpp",
+    ],
+    target: {
+        android: {
+            version_script: "libadb_pairing_auth.map.txt",
+        },
+        windows: {
+            compile_multilib: "first",
+            enabled: true,
+        },
+    },
+    export_include_dirs: ["include"],
+
+    visibility: [
+        "//art:__subpackages__",
+        "//system/core/adb:__subpackages__",
+    ],
+
+    // libadb_pairing_auth doesn't need an embedded build number.
+    use_version_lib: false,
+
+    host_supported: true,
+    recovery_available: true,
+
+    stl: "libc++_static",
+
+    static_libs: ["libbase"],
+    shared_libs: [
+        "libcrypto",
+        "liblog",
+    ],
+}
+
+cc_library {
+    name: "libadb_pairing_auth",
+    defaults: ["libadb_pairing_auth_defaults"],
+
+    apex_available: [
+        "com.android.adbd",
+    ],
+
+    stubs: {
+        symbol_file: "libadb_pairing_auth.map.txt",
+        versions: ["30"],
+    },
+}
+
+// For running atest (b/147158681)
+cc_library_static {
+    name: "libadb_pairing_auth_static",
+    defaults: ["libadb_pairing_auth_defaults"],
+
+    apex_available: [
+        "//apex_available:platform",
+    ],
+}
diff --git a/adb/pairing_auth/aes_128_gcm.cpp b/adb/pairing_auth/aes_128_gcm.cpp
new file mode 100644
index 0000000..2978834
--- /dev/null
+++ b/adb/pairing_auth/aes_128_gcm.cpp
@@ -0,0 +1,183 @@
+/*
+ * 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 "adb/pairing/aes_128_gcm.h"
+
+#include <android-base/endian.h>
+#include <android-base/logging.h>
+
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+#include <openssl/hkdf.h>
+#include <openssl/rand.h>
+
+namespace adb {
+namespace pairing {
+
+namespace {
+static const size_t kHkdfKeyLength = 256;
+
+struct Header {
+    uint32_t payload;
+    uint8_t iv[AES_128_GCM_IV_SIZE];
+    uint8_t tag[AES_128_GCM_TAG_SIZE];
+} __attribute__((packed));
+
+}  // namespace
+
+// static
+const EVP_CIPHER* Aes128Gcm::cipher_ = EVP_aes_128_gcm();
+
+Aes128Gcm::Aes128Gcm(const uint8_t* key_material, size_t key_material_len) {
+    CHECK(key_material);
+    CHECK_NE(key_material_len, 0ul);
+    context_.reset(EVP_CIPHER_CTX_new());
+    CHECK(context_.get());
+
+    // Start with a random number for our counter
+    CHECK_EQ(RAND_bytes(counter_.data(), counter_.size()), 1);
+
+    uint8_t key[kHkdfKeyLength] = {};
+    uint8_t salt[64] = "this is the salt";
+    uint8_t info[64] = "this is the info";
+    CHECK_EQ(HKDF(key, sizeof(key), EVP_sha256(), key_material, key_material_len, salt,
+                  sizeof(salt), info, sizeof(info)),
+             1);
+    CHECK_EQ(AES_set_encrypt_key(key, sizeof(key), &aes_key_), 0);
+}
+
+int Aes128Gcm::Encrypt(const uint8_t* in, size_t in_len, uint8_t* out, size_t out_len) {
+    if (out_len < EncryptedSize(in_len)) {
+        LOG(ERROR) << "out buffer size (sz=" << out_len
+                   << ") not big enough (sz=" << EncryptedSize(in_len) << ")";
+        return -1;
+    }
+    auto& header = *reinterpret_cast<Header*>(out);
+    // Place the IV in the header
+    memcpy(header.iv, counter_.data(), counter_.size());
+    int status = EVP_EncryptInit_ex(context_.get(), cipher_, nullptr,
+                                    reinterpret_cast<const uint8_t*>(&aes_key_), counter_.data());
+    counter_.Increase();
+    if (status != 1) {
+        return -1;
+    }
+
+    int cipherLen = 0;
+    out += sizeof(header);
+    status = EVP_EncryptUpdate(context_.get(), out, &cipherLen, in, in_len);
+    if (status != 1 || cipherLen < 0) {
+        return -1;
+    }
+
+    // Padding is enabled by default, so EVP_EncryptFinal_ex will pad any
+    // remaining partial data up to the block size.
+    int padding = 0;
+    status = EVP_EncryptFinal_ex(context_.get(), out + cipherLen, &padding);
+    if (status != 1 || padding < 0) {
+        return -1;
+    }
+
+    // Place the tag in the header
+    status = EVP_CIPHER_CTX_ctrl(context_.get(), EVP_CTRL_GCM_GET_TAG, sizeof(header.tag),
+                                 header.tag);
+    if (status != 1) {
+        return -1;
+    }
+    // Place the payload size in the header
+    uint32_t totalLen = sizeof(header) + cipherLen + padding;
+    header.payload = htonl(static_cast<uint32_t>(cipherLen) + static_cast<uint32_t>(padding));
+    return totalLen;
+}
+
+int Aes128Gcm::Decrypt(const uint8_t* in, size_t in_len, uint8_t* out, size_t out_len) {
+    if (in_len < sizeof(Header)) {
+        return 0;
+    }
+    if (out_len < DecryptedSize(in, in_len)) {
+        return 0;
+    }
+    const auto& header = *reinterpret_cast<const Header*>(in);
+    uint32_t payload = ntohl(header.payload);
+    uint32_t expected_inlen = sizeof(Header) + payload;
+    if (in_len < expected_inlen) {
+        // Not enough data available
+        return 0;
+    }
+    // Initialized with expected IV from header
+    int status = EVP_DecryptInit_ex(context_.get(), cipher_, nullptr,
+                                    reinterpret_cast<const uint8_t*>(&aes_key_), header.iv);
+    if (status != 1) {
+        return -1;
+    }
+
+    int decrypted_len = 0;
+    status = EVP_DecryptUpdate(context_.get(), out, &decrypted_len, in + sizeof(header), payload);
+    if (status != 1 || decrypted_len < 0) {
+        return -1;
+    }
+
+    // Set expected tag from header
+    status = EVP_CIPHER_CTX_ctrl(context_.get(), EVP_CTRL_GCM_SET_TAG, sizeof(header.tag),
+                                 const_cast<uint8_t*>(header.tag));
+    if (status != 1) {
+        return -1;
+    }
+
+    // This is the padding. It can be ignored.
+    int len = 0;
+    status = EVP_DecryptFinal_ex(context_.get(), out + decrypted_len, &len);
+    if (status != 1) {
+        LOG(ERROR) << "EVP_DecryptFinal_ex failed. Tag mismatch";
+        return -1;
+    }
+
+    // Return the length without the padding.
+    return decrypted_len;
+}
+
+size_t Aes128Gcm::EncryptedSize(size_t size) {
+    // We need to account for block alignment of the encrypted data.
+    // According to openssl.org/docs/man1.0.2/man3/EVP_EncryptUpdate.html,
+    // "The amount of data written depends on the block alignment of the
+    // encrypted data ..."
+    // ".. the amount of data written may be anything from zero bytes to
+    // (inl + cipher_block_size - 1) ..."
+    const size_t cipher_block_size = EVP_CIPHER_block_size(cipher_);
+    size_t padding = cipher_block_size - (size % cipher_block_size);
+    if (padding != cipher_block_size) {
+        size += padding;
+    }
+    return size + sizeof(Header);
+}
+
+size_t Aes128Gcm::DecryptedSize(const uint8_t* encrypted_data, size_t encrypted_size) {
+    if (encrypted_size < sizeof(Header)) {
+        // Not enough data yet
+        return 0;
+    }
+    auto header = reinterpret_cast<const Header*>(encrypted_data);
+    uint32_t payload = ntohl(header->payload);
+    size_t total_size = payload + sizeof(Header);
+    if (encrypted_size < total_size) {
+        // There's enough data for the header but not enough data for the
+        // payload. Indicate that there's not enough data for now.
+        return 0;
+    }
+    return payload;
+}
+
+}  // namespace pairing
+}  // namespace adb
diff --git a/adb/pairing_auth/include/adb/pairing/aes_128_gcm.h b/adb/pairing_auth/include/adb/pairing/aes_128_gcm.h
new file mode 100644
index 0000000..490dd12
--- /dev/null
+++ b/adb/pairing_auth/include/adb/pairing/aes_128_gcm.h
@@ -0,0 +1,72 @@
+/*
+ * 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 <openssl/aes.h>
+#include <openssl/cipher.h>
+
+#include <stdint.h>
+
+#include "adb/pairing/counter.h"
+
+// This is the default size of the initialization vector (iv) for AES-128-GCM
+#define AES_128_GCM_IV_SIZE 12
+// This is the full tag size for AES-128-GCM
+#define AES_128_GCM_TAG_SIZE 16
+
+namespace adb {
+namespace pairing {
+
+class Aes128Gcm {
+  public:
+    explicit Aes128Gcm(const uint8_t* key_material, size_t key_material_len);
+
+    // Encrypt a block of data in |in| of length |in_len|, this consumes all data
+    // in |in| and places the encrypted data in |out| if |out_len| indicates that
+    // there is enough space. The data contains information needed for
+    // decryption that is specific to this implementation and is therefore only
+    // suitable for decryption with this class.
+    // The method returns the number of bytes placed in |out| on success and a
+    // negative value if an error occurs.
+    int Encrypt(const uint8_t* in, size_t in_len, uint8_t* out, size_t out_len);
+    // Decrypt a block of data in |in| of length |in_len|, this consumes all data
+    // in |in_len| bytes of data. The decrypted output is placed in the |out|
+    // buffer of length |out_len|. On successful decryption the number of bytes in
+    // |out| will be placed in |out_len|.
+    // The method returns the number of bytes consumed from the |in| buffer. If
+    // there is not enough data available in |in| the method returns zero. If
+    // an error occurs the method returns a negative value.
+    int Decrypt(const uint8_t* in, size_t in_len, uint8_t* out, size_t out_len);
+
+    // Return a safe amount of buffer storage needed to encrypt |size| bytes.
+    size_t EncryptedSize(size_t size);
+    // Return a safe amount of buffer storage needed to decrypt the encrypted
+    // data in |encrypted_data| which is of length |encrypted_size|. Returns 0 if
+    // there is not enough data available to determine the required size.
+    size_t DecryptedSize(const uint8_t* encrypted_data, size_t encrypted_size);
+
+    static const EVP_CIPHER* cipher_;
+
+  private:
+    bssl::UniquePtr<EVP_CIPHER_CTX> context_;
+    AES_KEY aes_key_;
+    // We're going to use this counter for our iv so that it never repeats
+    Counter<AES_128_GCM_IV_SIZE> counter_;
+};
+
+}  // namespace pairing
+}  // namespace adb
diff --git a/adb/pairing_auth/include/adb/pairing/counter.h b/adb/pairing_auth/include/adb/pairing/counter.h
new file mode 100644
index 0000000..263ceb7
--- /dev/null
+++ b/adb/pairing_auth/include/adb/pairing/counter.h
@@ -0,0 +1,49 @@
+/*
+ * 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 <stddef.h>
+#include <stdint.h>
+
+namespace adb {
+namespace pairing {
+
+template <size_t N>
+class Counter {
+  public:
+    void Increase() {
+        for (size_t i = sizeof(counter_) - 1; i < sizeof(counter_); --i) {
+            if (++counter_[i] != 0) {
+                break;
+            }
+        }
+    }
+
+    uint8_t* data() { return counter_; }
+    const uint8_t* data() const { return counter_; }
+
+    constexpr size_t size() const { return sizeof(counter_); }
+
+    uint8_t& operator[](size_t index) { return counter_[index]; }
+    const uint8_t& operator[](size_t index) const { return counter_[index]; }
+
+  private:
+    uint8_t counter_[N];
+};
+
+}  // namespace pairing
+}  // namespace adb
diff --git a/adb/pairing_auth/include/adb/pairing/pairing_auth.h b/adb/pairing_auth/include/adb/pairing/pairing_auth.h
new file mode 100644
index 0000000..9ef97e2
--- /dev/null
+++ b/adb/pairing_auth/include/adb/pairing/pairing_auth.h
@@ -0,0 +1,186 @@
+/*
+ * 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 <stddef.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+
+#if !defined(__INTRODUCED_IN)
+#define __INTRODUCED_IN(__api_level) /* nothing */
+#endif
+
+__BEGIN_DECLS
+#if !defined(__ANDROID__) || __ANDROID_API__ >= 30
+
+/**
+ * PairingAuthCtx is a wrapper around the SPAKE2 protocol + cipher initialization
+ * for encryption. On construction, the |password| will be used to generate a
+ * SPAKE2 message. Each peer will exchange the messages in |pairing_auth_get_msg|
+ * to initialize their ciphers in |pairing_auth_init_cipher|. If both peers used the
+ * same |password|, then both sides will be able to decrypt each other's messages.
+ *
+ * On creation of a PairingAuthCtx, |pairing_auth_init_cipher| prior to using
+ * the encrypt and decrypt APIs. Furthermore, you can only initialize the cipher
+ * once.
+ *
+ * See pairing_auth_test.cpp for example usage.
+ *
+ */
+struct PairingAuthCtx;
+typedef struct PairingAuthCtx PairingAuthCtx;
+
+/**
+ * Creates a new PairingAuthCtx instance as the server.
+ *
+ * @param pswd the shared secret the server and client use to authenticate each
+ *             other. Will abort if null.
+ * @param len the length of the pswd in bytes. Will abort if 0.
+ * @return a new PairingAuthCtx server instance. Caller is responsible for
+ *         destroying the context via #pairing_auth_destroy.
+ */
+PairingAuthCtx* pairing_auth_server_new(const uint8_t* pswd, size_t len) __INTRODUCED_IN(30);
+
+/**
+ * Creates a new PairingAuthCtx instance as the client.
+ *
+ * @param pswd the shared secret the server and client use to authenticate each
+ *             other. Will abort if null.
+ * @param len the length of the pswd in bytes. Will abort if 0.
+ * @return a new PairingAuthCtx client instance. Caller is responsible for
+ *         destroying the context via #pairing_auth_destroy.
+ */
+PairingAuthCtx* pairing_auth_client_new(const uint8_t* pswd, size_t len) __INTRODUCED_IN(30);
+
+/**
+ * Destroys the PairingAuthCtx.
+ *
+ * @param ctx the PairingAuthCtx instance to destroy. Will abort if null.
+ */
+void pairing_auth_destroy(PairingAuthCtx* ctx) __INTRODUCED_IN(30);
+
+/**
+ * Returns the exact size of the SPAKE2 msg.
+ *
+ * Use this size as the buffer size when retrieving the message via
+ * #pairing_auth_get_msg.
+ *
+ * @param ctx the PairingAuthCtx instance. Will abort if null.
+ * @return the size of the SPAKE2 message in bytes. This is guaranteed to be > 0.
+ */
+size_t pairing_auth_msg_size(PairingAuthCtx* ctx) __INTRODUCED_IN(30);
+
+/**
+ * Writes the SPAKE2 message to exchange with the other party to |out_buf|.
+ *
+ * This is guaranteed to write a valid message to |out_buf|. Use #pairing_auth_msg_size
+ * to get the size the |out_buf| should be. The SPAKE2 messages will be used to
+ * initialize the cipher for encryption/decryption (see #pairing_auth_init_cipher).
+ *
+ * @param ctx the PairingAuthCtx instance. Will abort if null.
+ * @param out_buf the buffer the message is written to. The buffer is assumed to
+ *                be have at least #pairing_auth_msg_size size. Will abort if
+ *                out_buf is null.
+ */
+void pairing_auth_get_spake2_msg(PairingAuthCtx* ctx, uint8_t* out_buf) __INTRODUCED_IN(30);
+
+/**
+ * Processes the peer's |their_msg| and attempts to initialize the cipher for
+ * encryption.
+ *
+ * You can only call this method ONCE with a non-empty |msg|, regardless of success
+ * or failure. On success, you can use the #pairing_auth_decrypt and #pairing_auth_encrypt
+ * methods to exchange any further information securely. On failure, this
+ * PairingAuthCtx instance has no more purpose and should be destroyed.
+ *
+ * @param ctx the PairingAuthCtx instance. Will abort if null.
+ * @param their_msg the peer's SPAKE2 msg. See #pairing_auth_get_msg. Will abort
+ *        if null.
+ * @param msg_len the length of their_msg in bytes. Will abort if 0.
+ * @return true iff the client and server used the same password when creating
+ *         the PairingAuthCtx. See
+ *         https: *commondatastorage.googleapis.com/chromium-boringssl-docs/curve25519.h.html#SPAKE2
+ *         for more details on the SPAKE2 protocol.
+ */
+bool pairing_auth_init_cipher(PairingAuthCtx* ctx, const uint8_t* their_msg, size_t msg_len)
+        __INTRODUCED_IN(30);
+
+/**
+ * Returns a safe buffer size for encrypting data of a certain size.
+ *
+ * IMPORTANT: This will abort if either #pairing_auth_init_cipher was not called
+ * or #pairing_auth_init_cipher failed.
+ *
+ * @param ctx the PairingAuthCtx instance. Will abort if null.
+ * @param len the size of the message wanting to encrypt in bytes.
+ * @return the minimum buffer size, in bytes, to hold an encrypted message of size len. See
+ * #pairing_auth_encrypt for usage.
+ */
+size_t pairing_auth_safe_encrypted_size(PairingAuthCtx* ctx, size_t len) __INTRODUCED_IN(30);
+
+/**
+ * Encrypts input data and writes the encrypted data into a user-provided buffer.
+ *
+ * IMPORTANT: This will abort if either #pairing_auth_init_cipher was not called
+ * or #pairing_auth_init_cipher failed.
+ *
+ * @param ctx the PairingAuthCtx instance. Will abort if null.
+ * @param inbuf the buffer containing the data to encrypt. Will abort if null.
+ * @param inlen the size of inbuf in bytes. Will abort if 0.
+ * @param outbuf the buffer to write the encrypted data to. Will abort if null
+ * @param outlen the size of outbuf in bytes. See #pairing_auth_safe_encrypted_size.
+ * @return true if all the data was encrypted and written to outbuf, false
+ *         otherwise.
+ */
+bool pairing_auth_encrypt(PairingAuthCtx* ctx, const uint8_t* inbuf, size_t inlen, uint8_t* outbuf,
+                          size_t* outlen) __INTRODUCED_IN(30);
+
+/**
+ * Returns a safe buffer size for decrypting data of a certain size.
+ *
+ * IMPORTANT: This will abort if either #pairing_auth_init_cipher was not called
+ * or #pairing_auth_init_cipher failed.
+ *
+ * @param ctx the PairingAuthCtx instance. Will abort if null.
+ * @param buf the buffer containing the encrypted data. Will abort if null.
+ * @param len the size of the buf in bytes. Will abort if 0.
+ * @return the minimum buffer size, in bytes, to hold a decrypted message of size len. See
+ *         #pairing_auth_decrypt for usage.
+ */
+size_t pairing_auth_safe_decrypted_size(PairingAuthCtx* ctx, const uint8_t* buf, size_t len)
+        __INTRODUCED_IN(30);
+
+/**
+ * Decrypts input data and writes the decrypted data into a user-provided buffer.
+ *
+ * IMPORTANT: This will abort if either #pairing_auth_init_cipher was not called
+ * or #pairing_auth_init_cipher failed.
+ *
+ * @param ctx the PairingAuthCtx instance. Will abort if null.
+ * @param inbuf the buffer containing the data to decrypt. Will abort if null.
+ * @param inlen the size of inbuf in bytes. WIll abort if 0.
+ * @param outbuf the buffer to write the decrypted data to. Will abort if null.
+ * @param outlen the size of outbuf in bytes. See #pairing_auth_safe_decrypted_size.
+ *        Will abort if 0.
+ * @return true if all the data was decrypted and written to outbuf, false
+ *         otherwise.
+ */
+bool pairing_auth_decrypt(PairingAuthCtx* ctx, const uint8_t* inbuf, size_t inlen, uint8_t* outbuf,
+                          size_t* outlen) __INTRODUCED_IN(30);
+
+#endif  //!__ANDROID__ || __ANDROID_API__ >= 30
+__END_DECLS
diff --git a/adb/pairing_auth/libadb_pairing_auth.map.txt b/adb/pairing_auth/libadb_pairing_auth.map.txt
new file mode 100644
index 0000000..fdc1557
--- /dev/null
+++ b/adb/pairing_auth/libadb_pairing_auth.map.txt
@@ -0,0 +1,15 @@
+LIBADB_PAIRING_AUTH {
+  global:
+    pairing_auth_msg_size; # apex introduced=30
+    pairing_auth_get_spake2_msg; # apex introduced=30
+    pairing_auth_init_cipher; # apex introduced=30
+    pairing_auth_safe_encrypted_size; # apex introduced=30
+    pairing_auth_encrypt; # apex introduced=30
+    pairing_auth_safe_decrypted_size; # apex introduced=30
+    pairing_auth_decrypt; # apex introduced=30
+    pairing_auth_server_new; # apex introduced=30
+    pairing_auth_client_new; # apex introduced=30
+    pairing_auth_destroy; # apex introduced=30
+  local:
+    *;
+};
diff --git a/adb/pairing_auth/pairing_auth.cpp b/adb/pairing_auth/pairing_auth.cpp
new file mode 100644
index 0000000..96bc110
--- /dev/null
+++ b/adb/pairing_auth/pairing_auth.cpp
@@ -0,0 +1,300 @@
+/*
+ * 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 "adb/pairing/pairing_auth.h"
+
+#include <android-base/logging.h>
+
+#include <openssl/curve25519.h>
+#include <openssl/mem.h>
+
+#include <iomanip>
+#include <sstream>
+#include <vector>
+
+#include "adb/pairing/aes_128_gcm.h"
+
+using namespace adb::pairing;
+
+static constexpr spake2_role_t kClientRole = spake2_role_alice;
+static constexpr spake2_role_t kServerRole = spake2_role_bob;
+
+static const uint8_t kClientName[] = "adb pair client";
+static const uint8_t kServerName[] = "adb pair server";
+
+// This class is basically a wrapper around the SPAKE2 protocol + initializing a
+// cipher with the generated key material for encryption.
+struct PairingAuthCtx {
+  public:
+    using Data = std::vector<uint8_t>;
+    enum class Role {
+        Client,
+        Server,
+    };
+
+    explicit PairingAuthCtx(Role role, const Data& pswd);
+
+    // Returns the message to exchange with the other party. This is guaranteed
+    // to have a non-empty message if creating this object with
+    // |PairingAuthCtx::Create|, so you won't need to check.
+    const Data& msg() const;
+
+    // Processes the peer's |msg| and attempts to initialize the cipher for
+    // encryption. You can only call this method ONCE with a non-empty |msg|,
+    // regardless of success or failure. Subsequent calls will always return
+    // false. On success, you can use the |decrypt|
+    // and |encrypt| methods to exchange any further information securely.
+    //
+    // Note: Once you call this with a non-empty key, the state is locked, which
+    // means that you cannot try and register another key, regardless of the
+    // return value. In order to register another key, you have to create a new
+    // instance of PairingAuthCtx.
+    bool InitCipher(const Data& their_msg);
+
+    // Encrypts |data| and returns the result. If encryption fails, the return
+    // will be an empty vector.
+    Data Encrypt(const Data& data);
+
+    // Decrypts |data| and returns the result. If decryption fails, the return
+    // will be an empty vector.
+    Data Decrypt(const Data& data);
+
+    // Returns a safe buffer size for encrypting a buffer of size |len|.
+    size_t SafeEncryptedSize(size_t len);
+
+    // Returns a safe buffer size for decrypting a buffer |buf|.
+    size_t SafeDecryptedSize(const Data& buf);
+
+  private:
+    Data our_msg_;
+    Role role_;
+    bssl::UniquePtr<SPAKE2_CTX> spake2_ctx_;
+    std::unique_ptr<Aes128Gcm> cipher_;
+};  // PairingAuthCtx
+
+PairingAuthCtx::PairingAuthCtx(Role role, const Data& pswd) : role_(role) {
+    CHECK(!pswd.empty());
+    // Try to create the spake2 context and generate the public key.
+    spake2_role_t spake_role;
+    const uint8_t* my_name = nullptr;
+    const uint8_t* their_name = nullptr;
+    size_t my_len = 0;
+    size_t their_len = 0;
+
+    // Create the SPAKE2 context
+    switch (role_) {
+        case Role::Client:
+            spake_role = kClientRole;
+            my_name = kClientName;
+            my_len = sizeof(kClientName);
+            their_name = kServerName;
+            their_len = sizeof(kServerName);
+            break;
+        case Role::Server:
+            spake_role = kServerRole;
+            my_name = kServerName;
+            my_len = sizeof(kServerName);
+            their_name = kClientName;
+            their_len = sizeof(kClientName);
+            break;
+    }
+    spake2_ctx_.reset(SPAKE2_CTX_new(spake_role, my_name, my_len, their_name, their_len));
+    if (spake2_ctx_ == nullptr) {
+        LOG(ERROR) << "Unable to create a SPAKE2 context.";
+        return;
+    }
+
+    // Generate the SPAKE2 public key
+    size_t key_size = 0;
+    uint8_t key[SPAKE2_MAX_MSG_SIZE];
+    int status = SPAKE2_generate_msg(spake2_ctx_.get(), key, &key_size, SPAKE2_MAX_MSG_SIZE,
+                                     pswd.data(), pswd.size());
+    if (status != 1 || key_size == 0) {
+        LOG(ERROR) << "Unable to generate the SPAKE2 public key.";
+        return;
+    }
+    our_msg_.assign(key, key + key_size);
+}
+
+const PairingAuthCtx::Data& PairingAuthCtx::msg() const {
+    return our_msg_;
+}
+
+bool PairingAuthCtx::InitCipher(const PairingAuthCtx::Data& their_msg) {
+    // You can only register a key once.
+    CHECK(!their_msg.empty());
+    CHECK(!cipher_);
+
+    // Don't even try to process a message over the SPAKE2_MAX_MSG_SIZE
+    if (their_msg.size() > SPAKE2_MAX_MSG_SIZE) {
+        LOG(ERROR) << "their_msg size [" << their_msg.size() << "] greater then max size ["
+                   << SPAKE2_MAX_MSG_SIZE << "].";
+        return false;
+    }
+
+    size_t key_material_len = 0;
+    uint8_t key_material[SPAKE2_MAX_KEY_SIZE];
+    int status = SPAKE2_process_msg(spake2_ctx_.get(), key_material, &key_material_len,
+                                    sizeof(key_material), their_msg.data(), their_msg.size());
+    if (status != 1) {
+        LOG(ERROR) << "Unable to process their public key";
+        return false;
+    }
+
+    // Once SPAKE2_process_msg returns successfully, you can't do anything else
+    // with the context, besides destroy it.
+    cipher_.reset(new Aes128Gcm(key_material, key_material_len));
+
+    return true;
+}
+
+PairingAuthCtx::Data PairingAuthCtx::Encrypt(const PairingAuthCtx::Data& data) {
+    CHECK(cipher_);
+    CHECK(!data.empty());
+
+    // Determine the size for the encrypted data based on the raw data.
+    Data encrypted(cipher_->EncryptedSize(data.size()));
+    int bytes = cipher_->Encrypt(data.data(), data.size(), encrypted.data(), encrypted.size());
+    if (bytes < 0) {
+        LOG(ERROR) << "Unable to encrypt data";
+        return Data();
+    }
+    encrypted.resize(bytes);
+
+    return encrypted;
+}
+
+PairingAuthCtx::Data PairingAuthCtx::Decrypt(const PairingAuthCtx::Data& data) {
+    CHECK(cipher_);
+    CHECK(!data.empty());
+
+    // Determine the size for the decrypted data based on the raw data.
+    Data decrypted(cipher_->DecryptedSize(data.data(), data.size()));
+    size_t decrypted_size = decrypted.size();
+    int bytes = cipher_->Decrypt(data.data(), data.size(), decrypted.data(), decrypted_size);
+    if (bytes <= 0) {
+        LOG(ERROR) << "Unable to decrypt data";
+        return Data();
+    }
+    decrypted.resize(bytes);
+
+    return decrypted;
+}
+
+size_t PairingAuthCtx::SafeEncryptedSize(size_t len) {
+    CHECK(cipher_);
+    return cipher_->EncryptedSize(len);
+}
+
+size_t PairingAuthCtx::SafeDecryptedSize(const PairingAuthCtx::Data& buf) {
+    CHECK(cipher_);
+    return cipher_->DecryptedSize(buf.data(), buf.size());
+}
+
+PairingAuthCtx* pairing_auth_server_new(const uint8_t* pswd, size_t len) {
+    CHECK(pswd);
+    CHECK_GT(len, 0U);
+    std::vector<uint8_t> p(pswd, pswd + len);
+    auto* ret = new PairingAuthCtx(PairingAuthCtx::Role::Server, std::move(p));
+    CHECK(!ret->msg().empty());
+    return ret;
+}
+
+PairingAuthCtx* pairing_auth_client_new(const uint8_t* pswd, size_t len) {
+    CHECK(pswd);
+    CHECK_GT(len, 0U);
+    std::vector<uint8_t> p(pswd, pswd + len);
+    auto* ret = new PairingAuthCtx(PairingAuthCtx::Role::Client, std::move(p));
+    CHECK(!ret->msg().empty());
+    return ret;
+}
+
+size_t pairing_auth_msg_size(PairingAuthCtx* ctx) {
+    CHECK(ctx);
+    return ctx->msg().size();
+}
+
+void pairing_auth_get_spake2_msg(PairingAuthCtx* ctx, uint8_t* out_buf) {
+    CHECK(ctx);
+    CHECK(out_buf);
+    auto& msg = ctx->msg();
+    memcpy(out_buf, msg.data(), msg.size());
+}
+
+bool pairing_auth_init_cipher(PairingAuthCtx* ctx, const uint8_t* their_msg, size_t msg_len) {
+    CHECK(ctx);
+    CHECK(their_msg);
+    CHECK_GT(msg_len, 0U);
+
+    std::vector<uint8_t> p(their_msg, their_msg + msg_len);
+    return ctx->InitCipher(p);
+}
+
+size_t pairing_auth_safe_encrypted_size(PairingAuthCtx* ctx, size_t len) {
+    CHECK(ctx);
+    return ctx->SafeEncryptedSize(len);
+}
+
+bool pairing_auth_encrypt(PairingAuthCtx* ctx, const uint8_t* inbuf, size_t inlen, uint8_t* outbuf,
+                          size_t* outlen) {
+    CHECK(ctx);
+    CHECK(inbuf);
+    CHECK(outbuf);
+    CHECK(outlen);
+    CHECK_GT(inlen, 0U);
+
+    std::vector<uint8_t> in(inbuf, inbuf + inlen);
+    auto out = ctx->Encrypt(in);
+    if (out.empty()) {
+        return false;
+    }
+
+    memcpy(outbuf, out.data(), out.size());
+    *outlen = out.size();
+    return true;
+}
+
+size_t pairing_auth_safe_decrypted_size(PairingAuthCtx* ctx, const uint8_t* buf, size_t len) {
+    CHECK(ctx);
+    CHECK(buf);
+    CHECK_GT(len, 0U);
+    std::vector<uint8_t> p(buf, buf + len);
+    return ctx->SafeDecryptedSize(p);
+}
+
+bool pairing_auth_decrypt(PairingAuthCtx* ctx, const uint8_t* inbuf, size_t inlen, uint8_t* outbuf,
+                          size_t* outlen) {
+    CHECK(ctx);
+    CHECK(inbuf);
+    CHECK(outbuf);
+    CHECK(outlen);
+    CHECK_GT(inlen, 0U);
+
+    std::vector<uint8_t> in(inbuf, inbuf + inlen);
+    auto out = ctx->Decrypt(in);
+    if (out.empty()) {
+        return false;
+    }
+
+    memcpy(outbuf, out.data(), out.size());
+    *outlen = out.size();
+    return true;
+}
+
+void pairing_auth_destroy(PairingAuthCtx* ctx) {
+    CHECK(ctx);
+    delete ctx;
+}
diff --git a/adb/pairing_auth/tests/Android.bp b/adb/pairing_auth/tests/Android.bp
new file mode 100644
index 0000000..292fff5
--- /dev/null
+++ b/adb/pairing_auth/tests/Android.bp
@@ -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.
+//
+
+cc_test {
+    name: "adb_pairing_auth_test",
+    srcs: [
+        "aes_128_gcm_test.cpp",
+        "counter_test.cpp",
+        "pairing_auth_test.cpp",
+    ],
+
+    compile_multilib: "first",
+
+    shared_libs: [
+        "libbase",
+        "libcrypto",
+    ],
+
+    // Let's statically link them so we don't have to install it onto the
+    // system image for testing.
+    static_libs: [
+        "libadb_pairing_auth_static",
+    ],
+
+    test_suites: ["device-tests"],
+}
diff --git a/adb/pairing_auth/tests/aes_128_gcm_test.cpp b/adb/pairing_auth/tests/aes_128_gcm_test.cpp
new file mode 100644
index 0000000..e1a20e8
--- /dev/null
+++ b/adb/pairing_auth/tests/aes_128_gcm_test.cpp
@@ -0,0 +1,128 @@
+/*
+ * 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 <memory>
+
+#include <adb/pairing/aes_128_gcm.h>
+#include <openssl/rand.h>
+
+namespace adb {
+namespace pairing {
+
+TEST(Aes128GcmTest, init_null_material) {
+    std::unique_ptr<Aes128Gcm> cipher;
+    ASSERT_DEATH({ cipher.reset(new Aes128Gcm(nullptr, 42)); }, "");
+}
+
+TEST(Aes128GcmTest, init_empty_material) {
+    uint8_t material[64];
+    std::unique_ptr<Aes128Gcm> cipher;
+    ASSERT_DEATH({ cipher.reset(new Aes128Gcm(material, 0)); }, "");
+}
+
+TEST(Aes128GcmTest, encrypt_decrypt) {
+    const uint8_t msg[] = "alice and bob, sitting in a binary tree";
+    uint8_t material[256];
+    uint8_t encrypted[1024];
+    uint8_t out_buf[1024];
+
+    RAND_bytes(material, sizeof(material));
+    Aes128Gcm alice(material, sizeof(material));
+    Aes128Gcm bob(material, sizeof(material));
+    ;
+
+    ASSERT_GE(alice.EncryptedSize(sizeof(msg)), sizeof(msg));
+    int encrypted_size = alice.Encrypt(msg, sizeof(msg), encrypted, sizeof(encrypted));
+    ASSERT_GT(encrypted_size, 0);
+    size_t out_size = sizeof(out_buf);
+    ASSERT_GE(bob.DecryptedSize(encrypted, sizeof(encrypted)), sizeof(msg));
+    int decrypted_size = bob.Decrypt(encrypted, sizeof(encrypted), out_buf, out_size);
+    ASSERT_EQ(sizeof(msg), decrypted_size);
+    memset(out_buf + decrypted_size, 0, sizeof(out_buf) - decrypted_size);
+    ASSERT_STREQ(reinterpret_cast<const char*>(msg), reinterpret_cast<const char*>(out_buf));
+}
+
+TEST(Aes128GcmTest, padding) {
+    // Test with block-align data as well as unaligned data.
+    const size_t cipher_block_size = EVP_CIPHER_block_size(Aes128Gcm::cipher_);
+    uint8_t material[256];
+    RAND_bytes(material, sizeof(material));
+    Aes128Gcm alice(material, sizeof(material));
+    Aes128Gcm bob(material, sizeof(material));
+    ;
+    std::vector<uint8_t> msg;
+    std::vector<uint8_t> encrypted;
+    std::vector<uint8_t> decrypted;
+
+    // Test with aligned data
+    {
+        msg.resize(cipher_block_size);
+        RAND_bytes(msg.data(), msg.size());
+
+        // encrypt
+        size_t safe_encrypted_sz = alice.EncryptedSize(msg.size());
+        ASSERT_GE(safe_encrypted_sz, msg.size());
+        encrypted.resize(safe_encrypted_sz);
+        int encrypted_size =
+                alice.Encrypt(msg.data(), msg.size(), encrypted.data(), encrypted.size());
+        ASSERT_GT(encrypted_size, 0);
+        ASSERT_LE(encrypted_size, safe_encrypted_sz);
+        encrypted.resize(encrypted_size);
+
+        // decrypt
+        size_t safe_decrypted_size = bob.DecryptedSize(encrypted.data(), encrypted.size());
+        ASSERT_GE(safe_decrypted_size, msg.size());
+        decrypted.resize(safe_decrypted_size);
+        int decrypted_size =
+                bob.Decrypt(encrypted.data(), encrypted.size(), decrypted.data(), decrypted.size());
+        ASSERT_GT(decrypted_size, 0);
+        ASSERT_LE(decrypted_size, safe_decrypted_size);
+        ASSERT_EQ(msg.size(), decrypted_size);
+        ASSERT_EQ(memcmp(msg.data(), decrypted.data(), decrypted.size()), 0);
+    }
+
+    // Test with unaligned data
+    {
+        msg.resize(cipher_block_size + 1);
+        RAND_bytes(msg.data(), msg.size());
+
+        // encrypt
+        size_t safe_encrypted_sz = alice.EncryptedSize(msg.size());
+        ASSERT_GE(safe_encrypted_sz, msg.size());
+        encrypted.resize(safe_encrypted_sz);
+        int encrypted_size =
+                alice.Encrypt(msg.data(), msg.size(), encrypted.data(), encrypted.size());
+        ASSERT_GT(encrypted_size, 0);
+        ASSERT_LE(encrypted_size, safe_encrypted_sz);
+        encrypted.resize(encrypted_size);
+
+        // decrypt
+        size_t safe_decrypted_size = bob.DecryptedSize(encrypted.data(), encrypted.size());
+        ASSERT_GE(safe_decrypted_size, msg.size());
+        decrypted.resize(safe_decrypted_size);
+        int decrypted_size =
+                bob.Decrypt(encrypted.data(), encrypted.size(), decrypted.data(), decrypted.size());
+        ASSERT_GT(decrypted_size, 0);
+        ASSERT_LE(decrypted_size, safe_decrypted_size);
+        ASSERT_EQ(msg.size(), decrypted_size);
+        ASSERT_EQ(memcmp(msg.data(), decrypted.data(), decrypted.size()), 0);
+    }
+}
+
+}  // namespace pairing
+}  // namespace adb
diff --git a/adb/pairing_auth/tests/counter_test.cpp b/adb/pairing_auth/tests/counter_test.cpp
new file mode 100644
index 0000000..b338551
--- /dev/null
+++ b/adb/pairing_auth/tests/counter_test.cpp
@@ -0,0 +1,70 @@
+/*
+ * 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/pairing/counter.h>
+
+namespace adb {
+namespace pairing {
+
+static constexpr size_t kTestCounterSize = 13;
+static const uint8_t kZeroes[64] = {0};
+
+TEST(AdbCounterTest, size_match) {
+    Counter<kTestCounterSize> counter;
+    ASSERT_EQ(kTestCounterSize, counter.size());
+}
+
+TEST(AdbCounterTest, Increase) {
+    Counter<kTestCounterSize> counter;
+    memset(counter.data(), 0, counter.size());
+    counter.Increase();
+    EXPECT_EQ(1, counter[counter.size() - 1]);
+    EXPECT_EQ(0, memcmp(counter.data(), kZeroes, counter.size() - 1));
+}
+
+TEST(AdbCounterTest, rollover_first_byte) {
+    Counter<kTestCounterSize> counter;
+    memset(counter.data(), 0, counter.size());
+    counter[counter.size() - 1] = 0xFF;
+    counter.Increase();
+    EXPECT_EQ(0, counter[counter.size() - 1]);
+    EXPECT_EQ(1, counter[counter.size() - 2]);
+    EXPECT_EQ(0, memcmp(counter.data(), kZeroes, counter.size() - 2));
+}
+
+TEST(AdbCounterTest, multiple_rollover) {
+    Counter<kTestCounterSize> counter;
+    memset(counter.data(), 0xFF, counter.size());
+    memset(counter.data(), 0, counter.size() - 3);
+    counter.Increase();
+    EXPECT_EQ(0, counter[counter.size() - 5]);
+    EXPECT_EQ(1, counter[counter.size() - 4]);
+    EXPECT_EQ(0, counter[counter.size() - 3]);
+    EXPECT_EQ(0, counter[counter.size() - 2]);
+    EXPECT_EQ(0, counter[counter.size() - 1]);
+}
+
+TEST(AdbCounterTest, full_rollover) {
+    Counter<kTestCounterSize> counter;
+    memset(counter.data(), 0xFF, counter.size());
+    counter.Increase();
+    EXPECT_EQ(0, memcmp(counter.data(), kZeroes, counter.size()));
+}
+
+}  // namespace pairing
+}  // namespace adb
diff --git a/adb/pairing_auth/tests/pairing_auth_test.cpp b/adb/pairing_auth/tests/pairing_auth_test.cpp
new file mode 100644
index 0000000..fdc07f1
--- /dev/null
+++ b/adb/pairing_auth/tests/pairing_auth_test.cpp
@@ -0,0 +1,330 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "AdbPairingAuthTest"
+
+#include <gtest/gtest.h>
+
+#include <adb/pairing/pairing_auth.h>
+#include <android-base/endian.h>
+
+namespace adb {
+namespace pairing {
+
+static void PairingAuthDeleter(PairingAuthCtx* p) {
+    pairing_auth_destroy(p);
+}
+
+class AdbPairingAuthTest : public testing::Test {
+  protected:
+    virtual void SetUp() override {}
+
+    virtual void TearDown() override {}
+
+    using PairingAuthUniquePtr = std::unique_ptr<PairingAuthCtx, decltype(&PairingAuthDeleter)>;
+
+    PairingAuthUniquePtr makeClient(std::vector<uint8_t> pswd) {
+        return PairingAuthUniquePtr(pairing_auth_client_new(pswd.data(), pswd.size()),
+                                    PairingAuthDeleter);
+    }
+
+    PairingAuthUniquePtr makeServer(std::vector<uint8_t> pswd) {
+        return PairingAuthUniquePtr(pairing_auth_server_new(pswd.data(), pswd.size()),
+                                    PairingAuthDeleter);
+    }
+};
+
+TEST_F(AdbPairingAuthTest, EmptyPassword) {
+    // Context creation should fail if password is empty
+    PairingAuthUniquePtr client(nullptr, PairingAuthDeleter);
+    ASSERT_DEATH(
+            {
+                client = PairingAuthUniquePtr(pairing_auth_client_new(nullptr, 0),
+                                              PairingAuthDeleter);
+            },
+            "");
+    ASSERT_DEATH(
+            {
+                client = PairingAuthUniquePtr(pairing_auth_client_new(nullptr, 2),
+                                              PairingAuthDeleter);
+            },
+            "");
+    ASSERT_DEATH(
+            {
+                uint8_t p;
+                client = PairingAuthUniquePtr(pairing_auth_client_new(&p, 0), PairingAuthDeleter);
+            },
+            "");
+}
+
+TEST_F(AdbPairingAuthTest, ValidPassword) {
+    const char* kPswd = "password";
+    std::vector<uint8_t> pswd(kPswd, kPswd + sizeof(kPswd));
+    auto client = makeClient(pswd);
+    auto server = makeServer(pswd);
+
+    ASSERT_NE(nullptr, client);
+    ASSERT_NE(nullptr, server);
+
+    // msg should not be empty.
+    {
+        size_t msg_size = pairing_auth_msg_size(client.get());
+        std::vector<uint8_t> buf(msg_size);
+        ASSERT_GT(msg_size, 0);
+        pairing_auth_get_spake2_msg(client.get(), buf.data());
+    }
+    {
+        size_t msg_size = pairing_auth_msg_size(server.get());
+        std::vector<uint8_t> buf(msg_size);
+        ASSERT_GT(msg_size, 0);
+        pairing_auth_get_spake2_msg(server.get(), buf.data());
+    }
+}
+
+TEST_F(AdbPairingAuthTest, NoInitCipher) {
+    // Register a non-empty password, but not the peer's msg.
+    // You should not be able to encrypt/decrypt messages.
+    const char* kPswd = "password";
+    std::vector<uint8_t> pswd(kPswd, kPswd + sizeof(kPswd));
+    std::vector<uint8_t> data{0x01, 0x02, 0x03};
+    uint8_t outbuf[256];
+    size_t outsize;
+
+    // All other functions should crash if cipher hasn't been initialized.
+    ASSERT_DEATH(
+            {
+                auto server = makeServer(pswd);
+                pairing_auth_init_cipher(server.get(), nullptr, 0);
+            },
+            "");
+    ASSERT_DEATH(
+            {
+                auto server = makeServer(pswd);
+                pairing_auth_encrypt(server.get(), data.data(), data.size(), outbuf, &outsize);
+            },
+            "");
+    ASSERT_DEATH(
+            {
+                auto server = makeServer(pswd);
+                pairing_auth_decrypt(server.get(), data.data(), data.size(), outbuf, &outsize);
+            },
+            "");
+    ASSERT_DEATH(
+            {
+                auto server = makeServer(pswd);
+                pairing_auth_safe_decrypted_size(server.get(), data.data(), data.size());
+            },
+            "");
+    ASSERT_DEATH(
+            {
+                auto server = makeServer(pswd);
+                pairing_auth_safe_encrypted_size(server.get(), data.size());
+            },
+            "");
+}
+
+TEST_F(AdbPairingAuthTest, DifferentPasswords) {
+    // Register different passwords and then exchange the msgs. The
+    // encryption should succeed, but the decryption should fail, since the
+    // ciphers have been initialized with different keys.
+    auto client = makeClient({0x01, 0x02, 0x03});
+    std::vector<uint8_t> client_msg(pairing_auth_msg_size(client.get()));
+    ASSERT_FALSE(client_msg.empty());
+    pairing_auth_get_spake2_msg(client.get(), client_msg.data());
+
+    auto server = makeServer({0x01, 0x02, 0x04});
+    std::vector<uint8_t> server_msg(pairing_auth_msg_size(server.get()));
+    ASSERT_FALSE(server_msg.empty());
+    pairing_auth_get_spake2_msg(server.get(), server_msg.data());
+
+    EXPECT_TRUE(pairing_auth_init_cipher(client.get(), server_msg.data(), server_msg.size()));
+    EXPECT_TRUE(pairing_auth_init_cipher(server.get(), client_msg.data(), client_msg.size()));
+
+    // We shouldn't be able to decrypt.
+    std::vector<uint8_t> msg{0x2a, 0x2b, 0x2c};
+    // Client encrypts, server can't decrypt
+    size_t out_size;
+    client_msg.resize(pairing_auth_safe_encrypted_size(client.get(), msg.size()));
+    ASSERT_GT(client_msg.size(), 0);
+    ASSERT_TRUE(pairing_auth_encrypt(client.get(), msg.data(), msg.size(), client_msg.data(),
+                                     &out_size));
+    ASSERT_GT(out_size, 0);
+    client_msg.resize(out_size);
+
+    server_msg.resize(
+            pairing_auth_safe_decrypted_size(server.get(), client_msg.data(), client_msg.size()));
+    ASSERT_GT(server_msg.size(), 0);
+    ASSERT_FALSE(pairing_auth_decrypt(server.get(), client_msg.data(), client_msg.size(),
+                                      server_msg.data(), &out_size));
+
+    // Server encrypts, client can't decrypt
+    server_msg.resize(pairing_auth_safe_encrypted_size(server.get(), msg.size()));
+    ASSERT_GT(server_msg.size(), 0);
+    ASSERT_TRUE(pairing_auth_encrypt(server.get(), msg.data(), msg.size(), server_msg.data(),
+                                     &out_size));
+    ASSERT_GT(out_size, 0);
+    server_msg.resize(out_size);
+
+    client_msg.resize(
+            pairing_auth_safe_decrypted_size(client.get(), server_msg.data(), server_msg.size()));
+    ASSERT_GT(client_msg.size(), 0);
+    ASSERT_FALSE(pairing_auth_decrypt(client.get(), server_msg.data(), server_msg.size(),
+                                      client_msg.data(), &out_size));
+}
+
+TEST_F(AdbPairingAuthTest, SamePasswords) {
+    // Register same password and then exchange the msgs. The
+    // encryption and decryption should succeed and have the same, unencrypted
+    // values.
+    std::vector<uint8_t> pswd{0x4f, 0x5a, 0x01, 0x46};
+    auto client = makeClient(pswd);
+    std::vector<uint8_t> client_msg(pairing_auth_msg_size(client.get()));
+    ASSERT_FALSE(client_msg.empty());
+    pairing_auth_get_spake2_msg(client.get(), client_msg.data());
+
+    auto server = makeServer(pswd);
+    std::vector<uint8_t> server_msg(pairing_auth_msg_size(server.get()));
+    ASSERT_FALSE(server_msg.empty());
+    pairing_auth_get_spake2_msg(server.get(), server_msg.data());
+
+    EXPECT_TRUE(pairing_auth_init_cipher(client.get(), server_msg.data(), server_msg.size()));
+    EXPECT_TRUE(pairing_auth_init_cipher(server.get(), client_msg.data(), client_msg.size()));
+
+    // We should be able to decrypt.
+    std::vector<uint8_t> msg{0x2a, 0x2b, 0x2c, 0xff, 0x45, 0x12, 0x33};
+    // Client encrypts, server decrypts
+    size_t out_size;
+    client_msg.resize(pairing_auth_safe_encrypted_size(client.get(), msg.size()));
+    ASSERT_GT(client_msg.size(), 0);
+    ASSERT_TRUE(pairing_auth_encrypt(client.get(), msg.data(), msg.size(), client_msg.data(),
+                                     &out_size));
+    ASSERT_GT(out_size, 0);
+    client_msg.resize(out_size);
+
+    server_msg.resize(
+            pairing_auth_safe_decrypted_size(server.get(), client_msg.data(), client_msg.size()));
+    ASSERT_GT(server_msg.size(), 0);
+    ASSERT_TRUE(pairing_auth_decrypt(server.get(), client_msg.data(), client_msg.size(),
+                                     server_msg.data(), &out_size));
+    ASSERT_EQ(out_size, msg.size());
+    EXPECT_EQ(memcmp(msg.data(), server_msg.data(), out_size), 0);
+
+    // Server encrypts, client decrypt
+    server_msg.resize(pairing_auth_safe_encrypted_size(server.get(), msg.size()));
+    ASSERT_GT(server_msg.size(), 0);
+    ASSERT_TRUE(pairing_auth_encrypt(server.get(), msg.data(), msg.size(), server_msg.data(),
+                                     &out_size));
+    ASSERT_GT(out_size, 0);
+    server_msg.resize(out_size);
+
+    client_msg.resize(
+            pairing_auth_safe_decrypted_size(client.get(), server_msg.data(), server_msg.size()));
+    ASSERT_GT(client_msg.size(), 0);
+    ASSERT_TRUE(pairing_auth_decrypt(client.get(), server_msg.data(), server_msg.size(),
+                                     client_msg.data(), &out_size));
+    ASSERT_EQ(out_size, msg.size());
+    EXPECT_EQ(memcmp(msg.data(), client_msg.data(), out_size), 0);
+}
+
+TEST_F(AdbPairingAuthTest, CorruptedPayload) {
+    // Do a matching password for both server/client, but let's fudge with the
+    // header payload field. The decryption should fail.
+    std::vector<uint8_t> pswd{0x4f, 0x5a, 0x01, 0x46};
+    auto client = makeClient(pswd);
+    std::vector<uint8_t> client_msg(pairing_auth_msg_size(client.get()));
+    ASSERT_FALSE(client_msg.empty());
+    pairing_auth_get_spake2_msg(client.get(), client_msg.data());
+
+    auto server = makeServer(pswd);
+    std::vector<uint8_t> server_msg(pairing_auth_msg_size(server.get()));
+    ASSERT_FALSE(server_msg.empty());
+    pairing_auth_get_spake2_msg(server.get(), server_msg.data());
+
+    EXPECT_TRUE(pairing_auth_init_cipher(client.get(), server_msg.data(), server_msg.size()));
+    EXPECT_TRUE(pairing_auth_init_cipher(server.get(), client_msg.data(), client_msg.size()));
+
+    std::vector<uint8_t> msg{0x2a, 0x2b, 0x2c, 0xff, 0x45, 0x12,
+                             0x33, 0x45, 0x12, 0xea, 0xf2, 0xdb};
+    {
+        // Client encrypts whole msg, server decrypts msg. Should be fine.
+        size_t out_size;
+        client_msg.resize(pairing_auth_safe_encrypted_size(client.get(), msg.size()));
+        ASSERT_GT(client_msg.size(), 0);
+        ASSERT_TRUE(pairing_auth_encrypt(client.get(), msg.data(), msg.size(), client_msg.data(),
+                                         &out_size));
+        ASSERT_GT(out_size, 0);
+        client_msg.resize(out_size);
+
+        server_msg.resize(pairing_auth_safe_decrypted_size(server.get(), client_msg.data(),
+                                                           client_msg.size()));
+        ASSERT_GT(server_msg.size(), 0);
+        ASSERT_TRUE(pairing_auth_decrypt(server.get(), client_msg.data(), client_msg.size(),
+                                         server_msg.data(), &out_size));
+        ASSERT_EQ(out_size, msg.size());
+        EXPECT_EQ(memcmp(msg.data(), server_msg.data(), out_size), 0);
+    }
+    {
+        // 1) Client encrypts msg
+        // 2) append some data to the encrypted msg
+        // 3) change the payload field
+        // 4) server tries to decrypt. It should fail.
+        size_t out_size;
+        client_msg.resize(pairing_auth_safe_encrypted_size(client.get(), msg.size()));
+        ASSERT_GT(client_msg.size(), 0);
+        ASSERT_TRUE(pairing_auth_encrypt(client.get(), msg.data(), msg.size(), client_msg.data(),
+                                         &out_size));
+        ASSERT_GT(out_size, 0);
+        client_msg.resize(out_size);
+        client_msg.push_back(0xaa);
+        // This requires knowledge of the layout of the data. payload is the
+        // first four bytes of the client_msg.
+        uint32_t* payload = reinterpret_cast<uint32_t*>(client_msg.data());
+        *payload = ntohl(*payload);
+        *payload = htonl(*payload + 1);
+
+        server_msg.resize(pairing_auth_safe_decrypted_size(server.get(), client_msg.data(),
+                                                           client_msg.size()));
+        ASSERT_GT(server_msg.size(), 0);
+        ASSERT_FALSE(pairing_auth_decrypt(server.get(), client_msg.data(), client_msg.size(),
+                                          server_msg.data(), &out_size));
+    }
+    {
+        // 1) Client encrypts msg
+        // 3) decrement the payload field
+        // 4) server tries to decrypt. It should fail.
+        size_t out_size;
+        client_msg.resize(pairing_auth_safe_encrypted_size(client.get(), msg.size()));
+        ASSERT_GT(client_msg.size(), 0);
+        ASSERT_TRUE(pairing_auth_encrypt(client.get(), msg.data(), msg.size(), client_msg.data(),
+                                         &out_size));
+        ASSERT_GT(out_size, 0);
+        client_msg.resize(out_size);
+        // This requires knowledge of the layout of the data. payload is the
+        // first four bytes of the client_msg.
+        uint32_t* payload = reinterpret_cast<uint32_t*>(client_msg.data());
+        *payload = ntohl(*payload);
+        *payload = htonl(*payload - 1);
+
+        server_msg.resize(pairing_auth_safe_decrypted_size(server.get(), client_msg.data(),
+                                                           client_msg.size()));
+        ASSERT_GT(server_msg.size(), 0);
+        ASSERT_FALSE(pairing_auth_decrypt(server.get(), client_msg.data(), client_msg.size(),
+                                          server_msg.data(), &out_size));
+    }
+}
+
+}  // namespace pairing
+}  // namespace adb
diff --git a/adb/pairing_connection/Android.bp b/adb/pairing_connection/Android.bp
new file mode 100644
index 0000000..c053854
--- /dev/null
+++ b/adb/pairing_connection/Android.bp
@@ -0,0 +1,185 @@
+// 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.
+
+cc_defaults {
+    name: "libadb_pairing_connection_defaults",
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Wthread-safety",
+        "-Werror",
+    ],
+
+    compile_multilib: "both",
+
+    srcs: [
+        "pairing_connection.cpp",
+    ],
+    target: {
+        android: {
+            version_script: "libadb_pairing_connection.map.txt",
+        },
+        windows: {
+            compile_multilib: "first",
+            enabled: true,
+        },
+    },
+    export_include_dirs: ["include"],
+
+    visibility: [
+        "//art:__subpackages__",
+        "//system/core/adb:__subpackages__",
+        "//frameworks/base/services:__subpackages__",
+    ],
+    apex_available: [
+        "com.android.adbd",
+    ],
+
+    // libadb_pairing_connection doesn't need an embedded build number.
+    use_version_lib: false,
+
+    stl: "libc++_static",
+
+    host_supported: true,
+    recovery_available: true,
+
+    static_libs: [
+        "libbase",
+        "libssl",
+    ],
+    shared_libs: [
+        "libcrypto",
+        "liblog",
+        "libadb_pairing_auth",
+    ],
+}
+
+cc_library {
+    name: "libadb_pairing_connection",
+    defaults: ["libadb_pairing_connection_defaults"],
+
+    apex_available: [
+        "com.android.adbd",
+    ],
+
+    stubs: {
+        symbol_file: "libadb_pairing_connection.map.txt",
+        versions: ["30"],
+    },
+
+    static_libs: [
+        "libadb_protos",
+        // Statically link libadb_tls_connection because it is not
+	// ABI-stable.
+        "libadb_tls_connection",
+        "libprotobuf-cpp-lite",
+    ],
+}
+
+// For running atest (b/147158681)
+cc_library_static {
+    name: "libadb_pairing_connection_static",
+    defaults: ["libadb_pairing_connection_defaults"],
+
+    apex_available: [
+        "//apex_available:platform",
+    ],
+
+    static_libs: [
+        "libadb_protos_static",
+        "libprotobuf-cpp-lite",
+        "libadb_tls_connection_static",
+    ],
+}
+
+cc_defaults {
+    name: "libadb_pairing_server_defaults",
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Wthread-safety",
+        "-Werror",
+    ],
+
+    compile_multilib: "both",
+
+    srcs: [
+        "pairing_server.cpp",
+    ],
+    target: {
+        android: {
+            version_script: "libadb_pairing_server.map.txt",
+        },
+    },
+    export_include_dirs: ["include"],
+
+    visibility: [
+        "//art:__subpackages__",
+        "//system/core/adb:__subpackages__",
+        "//frameworks/base/services:__subpackages__",
+    ],
+
+    host_supported: true,
+    recovery_available: true,
+
+    stl: "libc++_static",
+
+    static_libs: [
+        "libbase",
+    ],
+    shared_libs: [
+        "libcrypto",
+        "libcrypto_utils",
+        "libcutils",
+        "liblog",
+        "libadb_pairing_auth",
+        "libadb_pairing_connection",
+    ],
+}
+
+cc_library {
+    name: "libadb_pairing_server",
+    defaults: ["libadb_pairing_server_defaults"],
+
+    apex_available: [
+        "com.android.adbd",
+    ],
+
+    stubs: {
+        symbol_file: "libadb_pairing_server.map.txt",
+        versions: ["30"],
+    },
+
+    static_libs: [
+        // Statically link libadb_crypto because it is not
+	// ABI-stable.
+        "libadb_crypto",
+        "libadb_protos",
+    ],
+}
+
+// For running atest (b/147158681)
+cc_library_static {
+    name: "libadb_pairing_server_static",
+    defaults: ["libadb_pairing_server_defaults"],
+
+    apex_available: [
+        "//apex_available:platform",
+    ],
+
+    static_libs: [
+        "libadb_crypto_static",
+        "libadb_protos_static",
+    ],
+}
diff --git a/adb/pairing_connection/include/adb/pairing/pairing_connection.h b/adb/pairing_connection/include/adb/pairing/pairing_connection.h
new file mode 100644
index 0000000..3543b87
--- /dev/null
+++ b/adb/pairing_connection/include/adb/pairing/pairing_connection.h
@@ -0,0 +1,130 @@
+/*
+ * 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 <stddef.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+
+#if !defined(__INTRODUCED_IN)
+#define __INTRODUCED_IN(__api_level) /* nothing */
+#endif
+
+// These APIs are for the Adb pairing protocol. This protocol requires both
+// sides to possess a shared secret to authenticate each other. The connection
+// is over TLS, and requires that both the client and server have a valid
+// certificate.
+//
+// This protocol is one-to-one, i.e., one PairingConnectionCtx server instance
+// interacts with only one PairingConnectionCtx client instance. In other words,
+// every new client instance must be bound to a new server instance.
+//
+// If both sides have authenticated, they will exchange their peer information
+// (see #PeerInfo).
+__BEGIN_DECLS
+#if !defined(__ANDROID__) || __ANDROID_API__ >= 30
+
+const uint32_t kMaxPeerInfoSize = 8192;
+struct PeerInfo {
+    uint8_t type;
+    uint8_t data[kMaxPeerInfoSize - 1];
+} __attribute__((packed));
+typedef struct PeerInfo PeerInfo;
+static_assert(sizeof(PeerInfo) == kMaxPeerInfoSize, "PeerInfo has weird size");
+
+enum PeerInfoType : uint8_t {
+    ADB_RSA_PUB_KEY = 0,
+    ADB_DEVICE_GUID = 1,
+};
+
+struct PairingConnectionCtx;
+typedef struct PairingConnectionCtx PairingConnectionCtx;
+typedef void (*pairing_result_cb)(const PeerInfo*, int, void*);
+
+// Starts the pairing connection on a separate thread.
+//
+// Upon completion, if the pairing was successful,
+// |cb| will be called with the peer information and certificate.
+// Otherwise, |cb| will be called with empty data. |fd| should already
+// be opened. PairingConnectionCtx will take ownership of the |fd|.
+//
+// Pairing is successful if both server/client uses the same non-empty
+// |pswd|, and they are able to exchange the information. |pswd| and
+// |certificate| must be non-empty. start() can only be called once in the
+// lifetime of this object.
+//
+// @param ctx the PairingConnectionCtx instance. Will abort if null.
+// @param fd the fd connecting the peers. This will take ownership of fd.
+// @param cb the user-provided callback that is called with the result of the
+//        pairing. The callback will be called on a different thread from the
+//        caller.
+// @param opaque opaque userdata.
+// @return true if the thread was successfully started, false otherwise. To stop
+//         the connection process, destroy the instance (see
+//         #pairing_connection_destroy). If false is returned, cb will not be
+//         invoked. Otherwise, cb is guaranteed to be invoked, even if you
+//         destroy the ctx while in the pairing process.
+bool pairing_connection_start(PairingConnectionCtx* ctx, int fd, pairing_result_cb cb, void* opaque)
+        __INTRODUCED_IN(30);
+
+// Creates a new PairingConnectionCtx instance as the client.
+//
+// @param pswd the password to authenticate both peers. Will abort if null.
+// @param pswd_len the length of pswd. Will abort if 0.
+// @param peer_info the PeerInfo struct that is exchanged between peers if the
+//                  pairing was successful. Will abort if null.
+// @param x509_cert_pem the X.509 certificate in PEM format. Will abort if null.
+// @param x509_size the size of x509_cert_pem. Will abort if 0.
+// @param priv_key_pem the private key corresponding to the given X.509
+//                     certificate, in PEM format. Will abort if null.
+// @param priv_size the size of priv_key_pem. Will abort if 0.
+// @return a new PairingConnectionCtx client instance. The caller is responsible
+//         for destroying the context via #pairing_connection_destroy.
+PairingConnectionCtx* pairing_connection_client_new(const uint8_t* pswd, size_t pswd_len,
+                                                    const PeerInfo* peer_info,
+                                                    const uint8_t* x509_cert_pem, size_t x509_size,
+                                                    const uint8_t* priv_key_pem, size_t priv_size)
+        __INTRODUCED_IN(30);
+
+// Creates a new PairingConnectionCtx instance as the server.
+//
+// @param pswd the password to authenticate both peers. Will abort if null.
+// @param pswd_len the length of pswd. Will abort if 0.
+// @param peer_info the PeerInfo struct that is exchanged between peers if the
+//                  pairing was successful. Will abort if null.
+// @param x509_cert_pem the X.509 certificate in PEM format. Will abort if null.
+// @param x509_size the size of x509_cert_pem. Will abort if 0.
+// @param priv_key_pem the private key corresponding to the given X.509
+//                     certificate, in PEM format. Will abort if null.
+// @param priv_size the size of priv_key_pem. Will abort if 0.
+// @return a new PairingConnectionCtx server instance. The caller is responsible
+//         for destroying the context via #pairing_connection_destroy.
+PairingConnectionCtx* pairing_connection_server_new(const uint8_t* pswd, size_t pswd_len,
+                                                    const PeerInfo* peer_info,
+                                                    const uint8_t* x509_cert_pem, size_t x509_size,
+                                                    const uint8_t* priv_key_pem, size_t priv_size)
+        __INTRODUCED_IN(30);
+
+// Destroys the PairingConnectionCtx instance.
+//
+// It is safe to destroy the instance at any point in the pairing process.
+//
+// @param ctx the PairingConnectionCtx instance to destroy. Will abort if null.
+void pairing_connection_destroy(PairingConnectionCtx* ctx) __INTRODUCED_IN(30);
+
+#endif  //!__ANDROID__ || __ANDROID_API__ >= 30
+__END_DECLS
diff --git a/adb/pairing_connection/include/adb/pairing/pairing_server.h b/adb/pairing_connection/include/adb/pairing/pairing_server.h
new file mode 100644
index 0000000..178a174
--- /dev/null
+++ b/adb/pairing_connection/include/adb/pairing/pairing_server.h
@@ -0,0 +1,111 @@
+/*
+ * 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 <stddef.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+
+#include <functional>
+#include <memory>
+#include <string_view>
+#include <vector>
+
+#include "adb/pairing/pairing_connection.h"
+
+#if !defined(__INTRODUCED_IN)
+#define __INTRODUCED_IN(__api_level) /* nothing */
+#endif
+
+__BEGIN_DECLS
+#if !defined(__ANDROID__) || __ANDROID_API__ >= 30
+
+// PairingServerCtx is a wrapper around the #PairingConnectionCtx APIs,
+// which handles multiple client connections.
+//
+// See pairing_connection_test.cpp for example usage.
+//
+struct PairingServerCtx;
+typedef struct PairingServerCtx PairingServerCtx;
+
+// Callback containing the result of the pairing. If #PeerInfo is null,
+// then the pairing failed. Otherwise, pairing succeeded and #PeerInfo
+// contains information about the peer.
+typedef void (*pairing_server_result_cb)(const PeerInfo*, void*) __INTRODUCED_IN(30);
+
+// Starts the pairing server.
+//
+// This call is non-blocking. Upon completion, if the pairing was successful,
+// then |cb| will be called with the PeerInfo
+// containing the info of the trusted peer. Otherwise, |cb| will be
+// called with an empty value. Start can only be called once in the lifetime
+// of this object.
+//
+// @param ctx the PairingServerCtx instance.
+// @param cb the user-provided callback to notify the result of the pairing. See
+//           #pairing_server_result_cb.
+// @param opaque the opaque userdata.
+// @return the port number the server is listening on. Returns 0 on failure.
+uint16_t pairing_server_start(PairingServerCtx* ctx, pairing_server_result_cb cb, void* opaque)
+        __INTRODUCED_IN(30);
+
+// Creates a new PairingServerCtx instance.
+//
+// @param pswd the password used to authenticate the client and server.
+// @param pswd_len the length of pswd.
+// @param peer_info the #PeerInfo struct passed to the client on successful
+//                  pairing.
+// @param x509_cert_pem the X.509 certificate in PEM format. Cannot be empty.
+// @param x509_size the size of x509_cert_pem.
+// @param priv_key_pem the private key corresponding to the given X.509
+//                     certificate, in PEM format. Cannot be empty.
+// @param priv_size the size of priv_key_pem.
+// @param port the port number the server should listen on. Must be within the
+//             valid port range [0, 65535]. If port is 0, then the server will
+//             find an open port to listen on. See #pairing_server_start to
+//             obtain the port used.
+// @return a new PairingServerCtx instance The caller is responsible
+//         for destroying the context via #pairing_server_destroy.
+PairingServerCtx* pairing_server_new(const uint8_t* pswd, size_t pswd_len,
+                                     const PeerInfo* peer_info, const uint8_t* x509_cert_pem,
+                                     size_t x509_size, const uint8_t* priv_key_pem,
+                                     size_t priv_size, uint16_t port) __INTRODUCED_IN(30);
+
+// Same as #pairing_server_new, except that the x509 certificate and private key
+// is generated internally.
+//
+// @param pswd the password used to authenticate the client and server.
+// @param pswd_len the length of pswd.
+// @param peer_info the #PeerInfo struct passed to the client on successful
+//                  pairing.
+// @param port the port number the server should listen on. Must be within the
+//             valid port range [0, 65535]. If port is 0, then the server will
+//             find an open port to listen on. See #pairing_server_start to
+//             obtain the port used.
+// @return a new PairingServerCtx instance The caller is responsible
+//         for destroying the context via #pairing_server_destroy.
+PairingServerCtx* pairing_server_new_no_cert(const uint8_t* pswd, size_t pswd_len,
+                                             const PeerInfo* peer_info, uint16_t port)
+        __INTRODUCED_IN(30);
+
+// Destroys the PairingServerCtx instance.
+//
+// @param ctx the PairingServerCtx instance to destroy.
+void pairing_server_destroy(PairingServerCtx* ctx) __INTRODUCED_IN(30);
+
+#endif  //!__ANDROID__ || __ANDROID_API__ >= 30
+__END_DECLS
diff --git a/adb/pairing_connection/internal/constants.h b/adb/pairing_connection/internal/constants.h
new file mode 100644
index 0000000..9a04f17
--- /dev/null
+++ b/adb/pairing_connection/internal/constants.h
@@ -0,0 +1,34 @@
+/*
+ * 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
+
+// This file contains constants that can be used both in the pairing_connection
+// code and tested in the pairing_connection_test code.
+namespace adb {
+namespace pairing {
+namespace internal {
+
+// The maximum number of connections the PairingServer can handle at once.
+constexpr int kMaxConnections = 10;
+// The maximum number of attempts the PairingServer will take before quitting.
+// This is to prevent someone malicious from quickly brute-forcing every
+// combination.
+constexpr int kMaxPairingAttempts = 20;
+
+}  // namespace internal
+}  // namespace pairing
+}  // namespace adb
diff --git a/adb/pairing_connection/libadb_pairing_connection.map.txt b/adb/pairing_connection/libadb_pairing_connection.map.txt
new file mode 100644
index 0000000..abd5f16
--- /dev/null
+++ b/adb/pairing_connection/libadb_pairing_connection.map.txt
@@ -0,0 +1,10 @@
+LIBADB_PAIRING_CONNECTION {
+  global:
+    pairing_connection_client_new; # apex introduced=30
+    pairing_connection_server_new; # apex introduced=30
+    pairing_connection_start; # apex introduced=30
+    pairing_connection_destroy; # apex introduced=30
+
+  local:
+    *;
+};
diff --git a/adb/pairing_connection/libadb_pairing_server.map.txt b/adb/pairing_connection/libadb_pairing_server.map.txt
new file mode 100644
index 0000000..dc0dc89
--- /dev/null
+++ b/adb/pairing_connection/libadb_pairing_server.map.txt
@@ -0,0 +1,10 @@
+LIBADB_PAIRING_SERVER {
+  global:
+    pairing_server_start; # apex introduced=30
+    pairing_server_new; # apex introduced=30
+    pairing_server_new_no_cert; # apex introduced=30
+    pairing_server_destroy; # apex introduced=30
+
+  local:
+    *;
+};
diff --git a/adb/pairing_connection/pairing_connection.cpp b/adb/pairing_connection/pairing_connection.cpp
new file mode 100644
index 0000000..a26a6b4
--- /dev/null
+++ b/adb/pairing_connection/pairing_connection.cpp
@@ -0,0 +1,491 @@
+/*
+ * 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 "adb/pairing/pairing_connection.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <functional>
+#include <memory>
+#include <string_view>
+#include <thread>
+#include <vector>
+
+#include <adb/pairing/pairing_auth.h>
+#include <adb/tls/tls_connection.h>
+#include <android-base/endian.h>
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/unique_fd.h>
+
+#include "pairing.pb.h"
+
+using namespace adb;
+using android::base::unique_fd;
+using TlsError = tls::TlsConnection::TlsError;
+
+const uint8_t kCurrentKeyHeaderVersion = 1;
+const uint8_t kMinSupportedKeyHeaderVersion = 1;
+const uint8_t kMaxSupportedKeyHeaderVersion = 1;
+const uint32_t kMaxPayloadSize = kMaxPeerInfoSize * 2;
+
+struct PairingPacketHeader {
+    uint8_t version;   // PairingPacket version
+    uint8_t type;      // the type of packet (PairingPacket.Type)
+    uint32_t payload;  // Size of the payload in bytes
+} __attribute__((packed));
+
+struct PairingAuthDeleter {
+    void operator()(PairingAuthCtx* p) { pairing_auth_destroy(p); }
+};  // PairingAuthDeleter
+using PairingAuthPtr = std::unique_ptr<PairingAuthCtx, PairingAuthDeleter>;
+
+// PairingConnectionCtx encapsulates the protocol to authenticate two peers with
+// each other. This class will open the tcp sockets and handle the pairing
+// process. On completion, both sides will have each other's public key
+// (certificate) if successful, otherwise, the pairing failed. The tcp port
+// number is hardcoded (see pairing_connection.cpp).
+//
+// Each PairingConnectionCtx instance represents a different device trying to
+// pair. So for the device, we can have multiple PairingConnectionCtxs while the
+// host may have only one (unless host has a PairingServer).
+//
+// See pairing_connection_test.cpp for example usage.
+//
+struct PairingConnectionCtx {
+  public:
+    using Data = std::vector<uint8_t>;
+    using ResultCallback = pairing_result_cb;
+    enum class Role {
+        Client,
+        Server,
+    };
+
+    explicit PairingConnectionCtx(Role role, const Data& pswd, const PeerInfo& peer_info,
+                                  const Data& certificate, const Data& priv_key);
+    virtual ~PairingConnectionCtx();
+
+    // Starts the pairing connection on a separate thread.
+    // Upon completion, if the pairing was successful,
+    // |cb| will be called with the peer information and certificate.
+    // Otherwise, |cb| will be called with empty data. |fd| should already
+    // be opened. PairingConnectionCtx will take ownership of the |fd|.
+    //
+    // Pairing is successful if both server/client uses the same non-empty
+    // |pswd|, and they are able to exchange the information. |pswd| and
+    // |certificate| must be non-empty. Start() can only be called once in the
+    // lifetime of this object.
+    //
+    // Returns true if the thread was successfully started, false otherwise.
+    bool Start(int fd, ResultCallback cb, void* opaque);
+
+  private:
+    // Setup the tls connection.
+    bool SetupTlsConnection();
+
+    /************ PairingPacketHeader methods ****************/
+    // Tries to write out the header and payload.
+    bool WriteHeader(const PairingPacketHeader* header, std::string_view payload);
+    // Tries to parse incoming data into the |header|. Returns true if header
+    // is valid and header version is supported. |header| is filled on success.
+    // |header| may contain garbage if unsuccessful.
+    bool ReadHeader(PairingPacketHeader* header);
+    // Creates a PairingPacketHeader.
+    void CreateHeader(PairingPacketHeader* header, adb::proto::PairingPacket::Type type,
+                      uint32_t payload_size);
+    // Checks if actual matches expected.
+    bool CheckHeaderType(adb::proto::PairingPacket::Type expected, uint8_t actual);
+
+    /*********** State related methods **************/
+    // Handles the State::ExchangingMsgs state.
+    bool DoExchangeMsgs();
+    // Handles the State::ExchangingPeerInfo state.
+    bool DoExchangePeerInfo();
+
+    // The background task to do the pairing.
+    void StartWorker();
+
+    // Calls |cb_| and sets the state to Stopped.
+    void NotifyResult(const PeerInfo* p);
+
+    static PairingAuthPtr CreatePairingAuthPtr(Role role, const Data& pswd);
+
+    enum class State {
+        Ready,
+        ExchangingMsgs,
+        ExchangingPeerInfo,
+        Stopped,
+    };
+
+    std::atomic<State> state_{State::Ready};
+    Role role_;
+    Data pswd_;
+    PeerInfo peer_info_;
+    Data cert_;
+    Data priv_key_;
+
+    // Peer's info
+    PeerInfo their_info_;
+
+    ResultCallback cb_;
+    void* opaque_ = nullptr;
+    std::unique_ptr<tls::TlsConnection> tls_;
+    PairingAuthPtr auth_;
+    unique_fd fd_;
+    std::thread thread_;
+    static constexpr size_t kExportedKeySize = 64;
+};  // PairingConnectionCtx
+
+PairingConnectionCtx::PairingConnectionCtx(Role role, const Data& pswd, const PeerInfo& peer_info,
+                                           const Data& cert, const Data& priv_key)
+    : role_(role), pswd_(pswd), peer_info_(peer_info), cert_(cert), priv_key_(priv_key) {
+    CHECK(!pswd_.empty() && !cert_.empty() && !priv_key_.empty());
+}
+
+PairingConnectionCtx::~PairingConnectionCtx() {
+    // Force close the fd and wait for the worker thread to finish.
+    fd_.reset();
+    if (thread_.joinable()) {
+        thread_.join();
+    }
+}
+
+bool PairingConnectionCtx::SetupTlsConnection() {
+    tls_ = tls::TlsConnection::Create(
+            role_ == Role::Server ? tls::TlsConnection::Role::Server
+                                  : tls::TlsConnection::Role::Client,
+            std::string_view(reinterpret_cast<const char*>(cert_.data()), cert_.size()),
+            std::string_view(reinterpret_cast<const char*>(priv_key_.data()), priv_key_.size()),
+            fd_);
+
+    if (tls_ == nullptr) {
+        LOG(ERROR) << "Unable to start TlsConnection. Unable to pair fd=" << fd_.get();
+        return false;
+    }
+
+    // Allow any peer certificate
+    tls_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
+
+    // SSL doesn't seem to behave correctly with fdevents so just do a blocking
+    // read for the pairing data.
+    if (tls_->DoHandshake() != TlsError::Success) {
+        LOG(ERROR) << "Failed to handshake with the peer fd=" << fd_.get();
+        return false;
+    }
+
+    // To ensure the connection is not stolen while we do the PAKE, append the
+    // exported key material from the tls connection to the password.
+    std::vector<uint8_t> exportedKeyMaterial = tls_->ExportKeyingMaterial(kExportedKeySize);
+    if (exportedKeyMaterial.empty()) {
+        LOG(ERROR) << "Failed to export key material";
+        return false;
+    }
+    pswd_.insert(pswd_.end(), std::make_move_iterator(exportedKeyMaterial.begin()),
+                 std::make_move_iterator(exportedKeyMaterial.end()));
+    auth_ = CreatePairingAuthPtr(role_, pswd_);
+
+    return true;
+}
+
+bool PairingConnectionCtx::WriteHeader(const PairingPacketHeader* header,
+                                       std::string_view payload) {
+    PairingPacketHeader network_header = *header;
+    network_header.payload = htonl(network_header.payload);
+    if (!tls_->WriteFully(std::string_view(reinterpret_cast<const char*>(&network_header),
+                                           sizeof(PairingPacketHeader))) ||
+        !tls_->WriteFully(payload)) {
+        LOG(ERROR) << "Failed to write out PairingPacketHeader";
+        state_ = State::Stopped;
+        return false;
+    }
+    return true;
+}
+
+bool PairingConnectionCtx::ReadHeader(PairingPacketHeader* header) {
+    auto data = tls_->ReadFully(sizeof(PairingPacketHeader));
+    if (data.empty()) {
+        return false;
+    }
+
+    uint8_t* p = data.data();
+    // First byte is always PairingPacketHeader version
+    header->version = *p;
+    ++p;
+    if (header->version < kMinSupportedKeyHeaderVersion ||
+        header->version > kMaxSupportedKeyHeaderVersion) {
+        LOG(ERROR) << "PairingPacketHeader version mismatch (us=" << kCurrentKeyHeaderVersion
+                   << " them=" << header->version << ")";
+        return false;
+    }
+    // Next byte is the PairingPacket::Type
+    if (!adb::proto::PairingPacket::Type_IsValid(*p)) {
+        LOG(ERROR) << "Unknown PairingPacket type=" << static_cast<uint32_t>(*p);
+        return false;
+    }
+    header->type = *p;
+    ++p;
+    // Last, the payload size
+    header->payload = ntohl(*(reinterpret_cast<uint32_t*>(p)));
+    if (header->payload == 0 || header->payload > kMaxPayloadSize) {
+        LOG(ERROR) << "header payload not within a safe payload size (size=" << header->payload
+                   << ")";
+        return false;
+    }
+
+    return true;
+}
+
+void PairingConnectionCtx::CreateHeader(PairingPacketHeader* header,
+                                        adb::proto::PairingPacket::Type type,
+                                        uint32_t payload_size) {
+    header->version = kCurrentKeyHeaderVersion;
+    uint8_t type8 = static_cast<uint8_t>(static_cast<int>(type));
+    header->type = type8;
+    header->payload = payload_size;
+}
+
+bool PairingConnectionCtx::CheckHeaderType(adb::proto::PairingPacket::Type expected_type,
+                                           uint8_t actual) {
+    uint8_t expected = *reinterpret_cast<uint8_t*>(&expected_type);
+    if (actual != expected) {
+        LOG(ERROR) << "Unexpected header type (expected=" << static_cast<uint32_t>(expected)
+                   << " actual=" << static_cast<uint32_t>(actual) << ")";
+        return false;
+    }
+    return true;
+}
+
+void PairingConnectionCtx::NotifyResult(const PeerInfo* p) {
+    cb_(p, fd_.get(), opaque_);
+    state_ = State::Stopped;
+}
+
+bool PairingConnectionCtx::Start(int fd, ResultCallback cb, void* opaque) {
+    if (fd < 0) {
+        return false;
+    }
+
+    State expected = State::Ready;
+    if (!state_.compare_exchange_strong(expected, State::ExchangingMsgs)) {
+        return false;
+    }
+
+    fd_.reset(fd);
+    cb_ = cb;
+    opaque_ = opaque;
+
+    thread_ = std::thread([this] { StartWorker(); });
+    return true;
+}
+
+bool PairingConnectionCtx::DoExchangeMsgs() {
+    uint32_t payload = pairing_auth_msg_size(auth_.get());
+    std::vector<uint8_t> msg(payload);
+    pairing_auth_get_spake2_msg(auth_.get(), msg.data());
+
+    PairingPacketHeader header;
+    CreateHeader(&header, adb::proto::PairingPacket::SPAKE2_MSG, payload);
+
+    // Write our SPAKE2 msg
+    if (!WriteHeader(&header,
+                     std::string_view(reinterpret_cast<const char*>(msg.data()), msg.size()))) {
+        LOG(ERROR) << "Failed to write SPAKE2 msg.";
+        return false;
+    }
+
+    // Read the peer's SPAKE2 msg header
+    if (!ReadHeader(&header)) {
+        LOG(ERROR) << "Invalid PairingPacketHeader.";
+        return false;
+    }
+    if (!CheckHeaderType(adb::proto::PairingPacket::SPAKE2_MSG, header.type)) {
+        return false;
+    }
+
+    // Read the SPAKE2 msg payload and initialize the cipher for
+    // encrypting the PeerInfo and certificate.
+    auto their_msg = tls_->ReadFully(header.payload);
+    if (their_msg.empty() ||
+        !pairing_auth_init_cipher(auth_.get(), their_msg.data(), their_msg.size())) {
+        LOG(ERROR) << "Unable to initialize pairing cipher [their_msg.size=" << their_msg.size()
+                   << "]";
+        return false;
+    }
+
+    return true;
+}
+
+bool PairingConnectionCtx::DoExchangePeerInfo() {
+    // Encrypt PeerInfo
+    std::vector<uint8_t> buf;
+    uint8_t* p = reinterpret_cast<uint8_t*>(&peer_info_);
+    buf.assign(p, p + sizeof(peer_info_));
+    std::vector<uint8_t> outbuf(pairing_auth_safe_encrypted_size(auth_.get(), buf.size()));
+    CHECK(!outbuf.empty());
+    size_t outsize;
+    if (!pairing_auth_encrypt(auth_.get(), buf.data(), buf.size(), outbuf.data(), &outsize)) {
+        LOG(ERROR) << "Failed to encrypt peer info";
+        return false;
+    }
+    outbuf.resize(outsize);
+
+    // Write out the packet header
+    PairingPacketHeader out_header;
+    out_header.version = kCurrentKeyHeaderVersion;
+    out_header.type = static_cast<uint8_t>(static_cast<int>(adb::proto::PairingPacket::PEER_INFO));
+    out_header.payload = htonl(outbuf.size());
+    if (!tls_->WriteFully(
+                std::string_view(reinterpret_cast<const char*>(&out_header), sizeof(out_header)))) {
+        LOG(ERROR) << "Unable to write PairingPacketHeader";
+        return false;
+    }
+
+    // Write out the encrypted payload
+    if (!tls_->WriteFully(
+                std::string_view(reinterpret_cast<const char*>(outbuf.data()), outbuf.size()))) {
+        LOG(ERROR) << "Unable to write encrypted peer info";
+        return false;
+    }
+
+    // Read in the peer's packet header
+    PairingPacketHeader header;
+    if (!ReadHeader(&header)) {
+        LOG(ERROR) << "Invalid PairingPacketHeader.";
+        return false;
+    }
+
+    if (!CheckHeaderType(adb::proto::PairingPacket::PEER_INFO, header.type)) {
+        return false;
+    }
+
+    // Read in the encrypted peer certificate
+    buf = tls_->ReadFully(header.payload);
+    if (buf.empty()) {
+        return false;
+    }
+
+    // Try to decrypt the certificate
+    outbuf.resize(pairing_auth_safe_decrypted_size(auth_.get(), buf.data(), buf.size()));
+    if (outbuf.empty()) {
+        LOG(ERROR) << "Unsupported payload while decrypting peer info.";
+        return false;
+    }
+
+    if (!pairing_auth_decrypt(auth_.get(), buf.data(), buf.size(), outbuf.data(), &outsize)) {
+        LOG(ERROR) << "Failed to decrypt";
+        return false;
+    }
+    outbuf.resize(outsize);
+
+    // The decrypted message should contain the PeerInfo.
+    if (outbuf.size() != sizeof(PeerInfo)) {
+        LOG(ERROR) << "Got size=" << outbuf.size() << "PeerInfo.size=" << sizeof(PeerInfo);
+        return false;
+    }
+
+    p = outbuf.data();
+    ::memcpy(&their_info_, p, sizeof(PeerInfo));
+    p += sizeof(PeerInfo);
+
+    return true;
+}
+
+void PairingConnectionCtx::StartWorker() {
+    // Setup the secure transport
+    if (!SetupTlsConnection()) {
+        NotifyResult(nullptr);
+        return;
+    }
+
+    for (;;) {
+        switch (state_) {
+            case State::ExchangingMsgs:
+                if (!DoExchangeMsgs()) {
+                    NotifyResult(nullptr);
+                    return;
+                }
+                state_ = State::ExchangingPeerInfo;
+                break;
+            case State::ExchangingPeerInfo:
+                if (!DoExchangePeerInfo()) {
+                    NotifyResult(nullptr);
+                    return;
+                }
+                NotifyResult(&their_info_);
+                return;
+            case State::Ready:
+            case State::Stopped:
+                LOG(FATAL) << __func__ << ": Got invalid state";
+                return;
+        }
+    }
+}
+
+// static
+PairingAuthPtr PairingConnectionCtx::CreatePairingAuthPtr(Role role, const Data& pswd) {
+    switch (role) {
+        case Role::Client:
+            return PairingAuthPtr(pairing_auth_client_new(pswd.data(), pswd.size()));
+            break;
+        case Role::Server:
+            return PairingAuthPtr(pairing_auth_server_new(pswd.data(), pswd.size()));
+            break;
+    }
+}
+
+static PairingConnectionCtx* CreateConnection(PairingConnectionCtx::Role role, const uint8_t* pswd,
+                                              size_t pswd_len, const PeerInfo* peer_info,
+                                              const uint8_t* x509_cert_pem, size_t x509_size,
+                                              const uint8_t* priv_key_pem, size_t priv_size) {
+    CHECK(pswd);
+    CHECK_GT(pswd_len, 0U);
+    CHECK(x509_cert_pem);
+    CHECK_GT(x509_size, 0U);
+    CHECK(priv_key_pem);
+    CHECK_GT(priv_size, 0U);
+    CHECK(peer_info);
+    std::vector<uint8_t> vec_pswd(pswd, pswd + pswd_len);
+    std::vector<uint8_t> vec_x509_cert(x509_cert_pem, x509_cert_pem + x509_size);
+    std::vector<uint8_t> vec_priv_key(priv_key_pem, priv_key_pem + priv_size);
+    return new PairingConnectionCtx(role, vec_pswd, *peer_info, vec_x509_cert, vec_priv_key);
+}
+
+PairingConnectionCtx* pairing_connection_client_new(const uint8_t* pswd, size_t pswd_len,
+                                                    const PeerInfo* peer_info,
+                                                    const uint8_t* x509_cert_pem, size_t x509_size,
+                                                    const uint8_t* priv_key_pem, size_t priv_size) {
+    return CreateConnection(PairingConnectionCtx::Role::Client, pswd, pswd_len, peer_info,
+                            x509_cert_pem, x509_size, priv_key_pem, priv_size);
+}
+
+PairingConnectionCtx* pairing_connection_server_new(const uint8_t* pswd, size_t pswd_len,
+                                                    const PeerInfo* peer_info,
+                                                    const uint8_t* x509_cert_pem, size_t x509_size,
+                                                    const uint8_t* priv_key_pem, size_t priv_size) {
+    return CreateConnection(PairingConnectionCtx::Role::Server, pswd, pswd_len, peer_info,
+                            x509_cert_pem, x509_size, priv_key_pem, priv_size);
+}
+
+void pairing_connection_destroy(PairingConnectionCtx* ctx) {
+    CHECK(ctx);
+    delete ctx;
+}
+
+bool pairing_connection_start(PairingConnectionCtx* ctx, int fd, pairing_result_cb cb,
+                              void* opaque) {
+    return ctx->Start(fd, cb, opaque);
+}
diff --git a/adb/pairing_connection/pairing_server.cpp b/adb/pairing_connection/pairing_server.cpp
new file mode 100644
index 0000000..7218eac
--- /dev/null
+++ b/adb/pairing_connection/pairing_server.cpp
@@ -0,0 +1,466 @@
+/*
+ * 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 "adb/pairing/pairing_server.h"
+
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+
+#include <atomic>
+#include <deque>
+#include <iomanip>
+#include <mutex>
+#include <sstream>
+#include <thread>
+#include <tuple>
+#include <unordered_map>
+#include <variant>
+#include <vector>
+
+#include <adb/crypto/rsa_2048_key.h>
+#include <adb/crypto/x509_generator.h>
+#include <adb/pairing/pairing_connection.h>
+#include <android-base/logging.h>
+#include <android-base/parsenetaddress.h>
+#include <android-base/thread_annotations.h>
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+
+#include "internal/constants.h"
+
+using android::base::ScopedLockAssertion;
+using android::base::unique_fd;
+using namespace adb::crypto;
+using namespace adb::pairing;
+
+// The implementation has two background threads running: one to handle and
+// accept any new pairing connection requests (socket accept), and the other to
+// handle connection events (connection started, connection finished).
+struct PairingServerCtx {
+  public:
+    using Data = std::vector<uint8_t>;
+
+    virtual ~PairingServerCtx();
+
+    // All parameters must be non-empty.
+    explicit PairingServerCtx(const Data& pswd, const PeerInfo& peer_info, const Data& cert,
+                              const Data& priv_key, uint16_t port);
+
+    // Starts the pairing server. This call is non-blocking. Upon completion,
+    // if the pairing was successful, then |cb| will be called with the PublicKeyHeader
+    // containing the info of the trusted peer. Otherwise, |cb| will be
+    // called with an empty value. Start can only be called once in the lifetime
+    // of this object.
+    //
+    // Returns the port number if PairingServerCtx was successfully started. Otherwise,
+    // returns 0.
+    uint16_t Start(pairing_server_result_cb cb, void* opaque);
+
+  private:
+    // Setup the server socket to accept incoming connections. Returns the
+    // server port number (> 0 on success).
+    uint16_t SetupServer();
+    // Force stop the server thread.
+    void StopServer();
+
+    // handles a new pairing client connection
+    bool HandleNewClientConnection(int fd) EXCLUDES(conn_mutex_);
+
+    // ======== connection events thread =============
+    std::mutex conn_mutex_;
+    std::condition_variable conn_cv_;
+
+    using FdVal = int;
+    struct ConnectionDeleter {
+        void operator()(PairingConnectionCtx* p) { pairing_connection_destroy(p); }
+    };
+    using ConnectionPtr = std::unique_ptr<PairingConnectionCtx, ConnectionDeleter>;
+    static ConnectionPtr CreatePairingConnection(const Data& pswd, const PeerInfo& peer_info,
+                                                 const Data& cert, const Data& priv_key);
+    using NewConnectionEvent = std::tuple<unique_fd, ConnectionPtr>;
+    // <fd, PeerInfo.type, PeerInfo.data>
+    using ConnectionFinishedEvent = std::tuple<FdVal, uint8_t, std::optional<std::string>>;
+    using ConnectionEvent = std::variant<NewConnectionEvent, ConnectionFinishedEvent>;
+    // Queue for connections to write into. We have a separate queue to read
+    // from, in order to minimize the time the server thread is blocked.
+    std::deque<ConnectionEvent> conn_write_queue_ GUARDED_BY(conn_mutex_);
+    std::deque<ConnectionEvent> conn_read_queue_;
+    // Map of fds to their PairingConnections currently running.
+    std::unordered_map<FdVal, ConnectionPtr> connections_;
+
+    // Two threads launched when starting the pairing server:
+    // 1) A server thread that waits for incoming client connections, and
+    // 2) A connection events thread that synchonizes events from all of the
+    //    clients, since each PairingConnection is running in it's own thread.
+    void StartConnectionEventsThread();
+    void StartServerThread();
+
+    static void PairingConnectionCallback(const PeerInfo* peer_info, int fd, void* opaque);
+
+    std::thread conn_events_thread_;
+    void ConnectionEventsWorker();
+    std::thread server_thread_;
+    void ServerWorker();
+    bool is_terminate_ GUARDED_BY(conn_mutex_) = false;
+
+    enum class State {
+        Ready,
+        Running,
+        Stopped,
+    };
+    State state_ = State::Ready;
+    Data pswd_;
+    PeerInfo peer_info_;
+    Data cert_;
+    Data priv_key_;
+    uint16_t port_;
+
+    pairing_server_result_cb cb_;
+    void* opaque_ = nullptr;
+    bool got_valid_pairing_ = false;
+
+    static const int kEpollConstSocket = 0;
+    // Used to break the server thread from epoll_wait
+    static const int kEpollConstEventFd = 1;
+    unique_fd epoll_fd_;
+    unique_fd server_fd_;
+    unique_fd event_fd_;
+};  // PairingServerCtx
+
+// static
+PairingServerCtx::ConnectionPtr PairingServerCtx::CreatePairingConnection(const Data& pswd,
+                                                                          const PeerInfo& peer_info,
+                                                                          const Data& cert,
+                                                                          const Data& priv_key) {
+    return ConnectionPtr(pairing_connection_server_new(pswd.data(), pswd.size(), &peer_info,
+                                                       cert.data(), cert.size(), priv_key.data(),
+                                                       priv_key.size()));
+}
+
+PairingServerCtx::PairingServerCtx(const Data& pswd, const PeerInfo& peer_info, const Data& cert,
+                                   const Data& priv_key, uint16_t port)
+    : pswd_(pswd), peer_info_(peer_info), cert_(cert), priv_key_(priv_key), port_(port) {
+    CHECK(!pswd_.empty() && !cert_.empty() && !priv_key_.empty());
+}
+
+PairingServerCtx::~PairingServerCtx() {
+    // Since these connections have references to us, let's make sure they
+    // destruct before us.
+    if (server_thread_.joinable()) {
+        StopServer();
+        server_thread_.join();
+    }
+
+    {
+        std::lock_guard<std::mutex> lock(conn_mutex_);
+        is_terminate_ = true;
+    }
+    conn_cv_.notify_one();
+    if (conn_events_thread_.joinable()) {
+        conn_events_thread_.join();
+    }
+
+    // Notify the cb_ if it hasn't already.
+    if (!got_valid_pairing_ && cb_ != nullptr) {
+        cb_(nullptr, opaque_);
+    }
+}
+
+uint16_t PairingServerCtx::Start(pairing_server_result_cb cb, void* opaque) {
+    cb_ = cb;
+    opaque_ = opaque;
+
+    if (state_ != State::Ready) {
+        LOG(ERROR) << "PairingServerCtx already running or stopped";
+        return 0;
+    }
+
+    port_ = SetupServer();
+    if (port_ == 0) {
+        LOG(ERROR) << "Unable to start PairingServer";
+        state_ = State::Stopped;
+        return 0;
+    }
+    LOG(INFO) << "Pairing server started on port " << port_;
+
+    state_ = State::Running;
+    return port_;
+}
+
+void PairingServerCtx::StopServer() {
+    if (event_fd_.get() == -1) {
+        return;
+    }
+    uint64_t value = 1;
+    ssize_t rc = write(event_fd_.get(), &value, sizeof(value));
+    if (rc == -1) {
+        // This can happen if the server didn't start.
+        PLOG(ERROR) << "write to eventfd failed";
+    } else if (rc != sizeof(value)) {
+        LOG(FATAL) << "write to event returned short (" << rc << ")";
+    }
+}
+
+uint16_t PairingServerCtx::SetupServer() {
+    epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC));
+    if (epoll_fd_ == -1) {
+        PLOG(ERROR) << "failed to create epoll fd";
+        return 0;
+    }
+
+    event_fd_.reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
+    if (event_fd_ == -1) {
+        PLOG(ERROR) << "failed to create eventfd";
+        return 0;
+    }
+
+    server_fd_.reset(socket_inaddr_any_server(port_, SOCK_STREAM));
+    if (server_fd_.get() == -1) {
+        PLOG(ERROR) << "Failed to start pairing connection server";
+        return 0;
+    } else if (fcntl(server_fd_.get(), F_SETFD, FD_CLOEXEC) != 0) {
+        PLOG(ERROR) << "Failed to make server socket cloexec";
+        return 0;
+    } else if (fcntl(server_fd_.get(), F_SETFD, O_NONBLOCK) != 0) {
+        PLOG(ERROR) << "Failed to make server socket nonblocking";
+        return 0;
+    }
+
+    StartConnectionEventsThread();
+    StartServerThread();
+    int port = socket_get_local_port(server_fd_.get());
+    return (port <= 0 ? 0 : port);
+}
+
+void PairingServerCtx::StartServerThread() {
+    server_thread_ = std::thread([this]() { ServerWorker(); });
+}
+
+void PairingServerCtx::StartConnectionEventsThread() {
+    conn_events_thread_ = std::thread([this]() { ConnectionEventsWorker(); });
+}
+
+void PairingServerCtx::ServerWorker() {
+    {
+        struct epoll_event event;
+        event.events = EPOLLIN;
+        event.data.u64 = kEpollConstSocket;
+        CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, server_fd_.get(), &event));
+    }
+
+    {
+        struct epoll_event event;
+        event.events = EPOLLIN;
+        event.data.u64 = kEpollConstEventFd;
+        CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, event_fd_.get(), &event));
+    }
+
+    while (true) {
+        struct epoll_event events[2];
+        int rc = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd_.get(), events, 2, -1));
+        if (rc == -1) {
+            PLOG(ERROR) << "epoll_wait failed";
+            return;
+        } else if (rc == 0) {
+            LOG(ERROR) << "epoll_wait returned 0";
+            return;
+        }
+
+        for (int i = 0; i < rc; ++i) {
+            struct epoll_event& event = events[i];
+            switch (event.data.u64) {
+                case kEpollConstSocket:
+                    HandleNewClientConnection(server_fd_.get());
+                    break;
+                case kEpollConstEventFd:
+                    uint64_t dummy;
+                    int rc = TEMP_FAILURE_RETRY(read(event_fd_.get(), &dummy, sizeof(dummy)));
+                    if (rc != sizeof(dummy)) {
+                        PLOG(FATAL) << "failed to read from eventfd (rc=" << rc << ")";
+                    }
+                    return;
+            }
+        }
+    }
+}
+
+// static
+void PairingServerCtx::PairingConnectionCallback(const PeerInfo* peer_info, int fd, void* opaque) {
+    auto* p = reinterpret_cast<PairingServerCtx*>(opaque);
+
+    ConnectionFinishedEvent event;
+    if (peer_info != nullptr) {
+        if (peer_info->type == ADB_RSA_PUB_KEY) {
+            event = std::make_tuple(fd, peer_info->type,
+                                    std::string(reinterpret_cast<const char*>(peer_info->data)));
+        } else {
+            LOG(WARNING) << "Ignoring successful pairing because of unknown "
+                         << "PeerInfo type=" << peer_info->type;
+        }
+    } else {
+        event = std::make_tuple(fd, 0, std::nullopt);
+    }
+    {
+        std::lock_guard<std::mutex> lock(p->conn_mutex_);
+        p->conn_write_queue_.push_back(std::move(event));
+    }
+    p->conn_cv_.notify_one();
+}
+
+void PairingServerCtx::ConnectionEventsWorker() {
+    uint8_t num_tries = 0;
+    for (;;) {
+        // Transfer the write queue to the read queue.
+        {
+            std::unique_lock<std::mutex> lock(conn_mutex_);
+            ScopedLockAssertion assume_locked(conn_mutex_);
+
+            if (is_terminate_) {
+                // We check |is_terminate_| twice because condition_variable's
+                // notify() only wakes up a thread if it is in the wait state
+                // prior to notify(). Furthermore, we aren't holding the mutex
+                // when processing the events in |conn_read_queue_|.
+                return;
+            }
+            if (conn_write_queue_.empty()) {
+                // We need to wait for new events, or the termination signal.
+                conn_cv_.wait(lock, [this]() REQUIRES(conn_mutex_) {
+                    return (is_terminate_ || !conn_write_queue_.empty());
+                });
+            }
+            if (is_terminate_) {
+                // We're done.
+                return;
+            }
+            // Move all events into the read queue.
+            conn_read_queue_ = std::move(conn_write_queue_);
+            conn_write_queue_.clear();
+        }
+
+        // Process all events in the read queue.
+        while (conn_read_queue_.size() > 0) {
+            auto& event = conn_read_queue_.front();
+            if (auto* p = std::get_if<NewConnectionEvent>(&event)) {
+                // Ignore if we are already at the max number of connections
+                if (connections_.size() >= internal::kMaxConnections) {
+                    conn_read_queue_.pop_front();
+                    continue;
+                }
+                auto [ufd, connection] = std::move(*p);
+                int fd = ufd.release();
+                bool started = pairing_connection_start(connection.get(), fd,
+                                                        PairingConnectionCallback, this);
+                if (!started) {
+                    LOG(ERROR) << "PairingServer unable to start a PairingConnection fd=" << fd;
+                    ufd.reset(fd);
+                } else {
+                    connections_[fd] = std::move(connection);
+                }
+            } else if (auto* p = std::get_if<ConnectionFinishedEvent>(&event)) {
+                auto [fd, info_type, public_key] = std::move(*p);
+                if (public_key.has_value() && !public_key->empty()) {
+                    // Valid pairing. Let's shutdown the server and close any
+                    // pairing connections in progress.
+                    StopServer();
+                    connections_.clear();
+
+                    PeerInfo info = {};
+                    info.type = info_type;
+                    strncpy(reinterpret_cast<char*>(info.data), public_key->data(),
+                            public_key->size());
+
+                    cb_(&info, opaque_);
+
+                    got_valid_pairing_ = true;
+                    return;
+                }
+                // Invalid pairing. Close the invalid connection.
+                if (connections_.find(fd) != connections_.end()) {
+                    connections_.erase(fd);
+                }
+
+                if (++num_tries >= internal::kMaxPairingAttempts) {
+                    cb_(nullptr, opaque_);
+                    // To prevent the destructor from calling it again.
+                    cb_ = nullptr;
+                    return;
+                }
+            }
+            conn_read_queue_.pop_front();
+        }
+    }
+}
+
+bool PairingServerCtx::HandleNewClientConnection(int fd) {
+    unique_fd ufd(TEMP_FAILURE_RETRY(accept4(fd, nullptr, nullptr, SOCK_CLOEXEC)));
+    if (ufd == -1) {
+        PLOG(WARNING) << "adb_socket_accept failed fd=" << fd;
+        return false;
+    }
+    auto connection = CreatePairingConnection(pswd_, peer_info_, cert_, priv_key_);
+    if (connection == nullptr) {
+        LOG(ERROR) << "PairingServer unable to create a PairingConnection fd=" << fd;
+        return false;
+    }
+    // send the new connection to the connection thread for further processing
+    NewConnectionEvent event = std::make_tuple(std::move(ufd), std::move(connection));
+    {
+        std::lock_guard<std::mutex> lock(conn_mutex_);
+        conn_write_queue_.push_back(std::move(event));
+    }
+    conn_cv_.notify_one();
+
+    return true;
+}
+
+uint16_t pairing_server_start(PairingServerCtx* ctx, pairing_server_result_cb cb, void* opaque) {
+    return ctx->Start(cb, opaque);
+}
+
+PairingServerCtx* pairing_server_new(const uint8_t* pswd, size_t pswd_len,
+                                     const PeerInfo* peer_info, const uint8_t* x509_cert_pem,
+                                     size_t x509_size, const uint8_t* priv_key_pem,
+                                     size_t priv_size, uint16_t port) {
+    CHECK(pswd);
+    CHECK_GT(pswd_len, 0U);
+    CHECK(x509_cert_pem);
+    CHECK_GT(x509_size, 0U);
+    CHECK(priv_key_pem);
+    CHECK_GT(priv_size, 0U);
+    CHECK(peer_info);
+    std::vector<uint8_t> vec_pswd(pswd, pswd + pswd_len);
+    std::vector<uint8_t> vec_x509_cert(x509_cert_pem, x509_cert_pem + x509_size);
+    std::vector<uint8_t> vec_priv_key(priv_key_pem, priv_key_pem + priv_size);
+    return new PairingServerCtx(vec_pswd, *peer_info, vec_x509_cert, vec_priv_key, port);
+}
+
+PairingServerCtx* pairing_server_new_no_cert(const uint8_t* pswd, size_t pswd_len,
+                                             const PeerInfo* peer_info, uint16_t port) {
+    auto rsa_2048 = CreateRSA2048Key();
+    auto x509_cert = GenerateX509Certificate(rsa_2048->GetEvpPkey());
+    std::string pkey_pem = Key::ToPEMString(rsa_2048->GetEvpPkey());
+    std::string cert_pem = X509ToPEMString(x509_cert.get());
+
+    return pairing_server_new(pswd, pswd_len, peer_info,
+                              reinterpret_cast<const uint8_t*>(cert_pem.data()), cert_pem.size(),
+                              reinterpret_cast<const uint8_t*>(pkey_pem.data()), pkey_pem.size(),
+                              port);
+}
+
+void pairing_server_destroy(PairingServerCtx* ctx) {
+    CHECK(ctx);
+    delete ctx;
+}
diff --git a/adb/pairing_connection/tests/Android.bp b/adb/pairing_connection/tests/Android.bp
new file mode 100644
index 0000000..bf075bc
--- /dev/null
+++ b/adb/pairing_connection/tests/Android.bp
@@ -0,0 +1,47 @@
+//
+// 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.
+//
+
+cc_test {
+    name: "adb_pairing_connection_test",
+    srcs: [
+        "pairing_client.cpp",
+        "pairing_connection_test.cpp",
+    ],
+
+    compile_multilib: "first",
+
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "libcrypto",
+        "libcrypto_utils",
+        "libprotobuf-cpp-lite",
+        "libssl",
+    ],
+
+    // Let's statically link them so we don't have to install it onto the
+    // system image for testing.
+    static_libs: [
+        "libadb_pairing_auth_static",
+        "libadb_pairing_connection_static",
+        "libadb_pairing_server_static",
+        "libadb_crypto_static",
+        "libadb_protos_static",
+        "libadb_tls_connection_static",
+    ],
+
+    test_suites: ["device-tests"],
+}
diff --git a/adb/pairing_connection/tests/pairing_client.cpp b/adb/pairing_connection/tests/pairing_client.cpp
new file mode 100644
index 0000000..1f3ef5a
--- /dev/null
+++ b/adb/pairing_connection/tests/pairing_client.cpp
@@ -0,0 +1,201 @@
+/*
+ * 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 "pairing_client.h"
+
+#include <netdb.h>
+#include <netinet/tcp.h>
+
+#include <atomic>
+#include <iomanip>
+#include <mutex>
+#include <sstream>
+#include <thread>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/parsenetaddress.h>
+#include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+
+namespace adb {
+namespace pairing {
+
+using android::base::unique_fd;
+
+static void ConnectionDeleter(PairingConnectionCtx* p) {
+    pairing_connection_destroy(p);
+}
+using ConnectionPtr = std::unique_ptr<PairingConnectionCtx, decltype(&ConnectionDeleter)>;
+
+namespace {
+
+class PairingClientImpl : public PairingClient {
+  public:
+    explicit PairingClientImpl(const Data& pswd, const PeerInfo& peer_info, const Data& cert,
+                               const Data& priv_key);
+
+    // Starts the pairing client. This call is non-blocking. Upon pairing
+    // completion, |cb| will be called with the PeerInfo on success,
+    // or an empty value on failure.
+    //
+    // Returns true if PairingClient was successfully started. Otherwise,
+    // return false.
+    virtual bool Start(std::string_view ip_addr, pairing_client_result_cb cb,
+                       void* opaque) override;
+
+  private:
+    static ConnectionPtr CreatePairingConnection(const Data& pswd, const PeerInfo& peer_info,
+                                                 const Data& cert, const Data& priv_key);
+
+    static void PairingResultCallback(const PeerInfo* peer_info, int fd, void* opaque);
+    // Setup and start the PairingConnection
+    bool StartConnection();
+
+    enum class State {
+        Ready,
+        Running,
+        Stopped,
+    };
+
+    State state_ = State::Ready;
+    Data pswd_;
+    PeerInfo peer_info_;
+    Data cert_;
+    Data priv_key_;
+    std::string host_;
+    int port_;
+
+    ConnectionPtr connection_;
+    pairing_client_result_cb cb_;
+    void* opaque_ = nullptr;
+};  // PairingClientImpl
+
+// static
+ConnectionPtr PairingClientImpl::CreatePairingConnection(const Data& pswd,
+                                                         const PeerInfo& peer_info,
+                                                         const Data& cert, const Data& priv_key) {
+    return ConnectionPtr(
+            pairing_connection_client_new(pswd.data(), pswd.size(), &peer_info, cert.data(),
+                                          cert.size(), priv_key.data(), priv_key.size()),
+            ConnectionDeleter);
+}
+
+PairingClientImpl::PairingClientImpl(const Data& pswd, const PeerInfo& peer_info, const Data& cert,
+                                     const Data& priv_key)
+    : pswd_(pswd),
+      peer_info_(peer_info),
+      cert_(cert),
+      priv_key_(priv_key),
+      connection_(nullptr, ConnectionDeleter) {
+    CHECK(!pswd_.empty() && !cert_.empty() && !priv_key_.empty());
+
+    state_ = State::Ready;
+}
+
+bool PairingClientImpl::Start(std::string_view ip_addr, pairing_client_result_cb cb, void* opaque) {
+    CHECK(!ip_addr.empty());
+    cb_ = cb;
+    opaque_ = opaque;
+
+    if (state_ != State::Ready) {
+        LOG(ERROR) << "PairingClient already running or finished";
+        return false;
+    }
+
+    // Try to parse the host address
+    std::string err;
+    CHECK(android::base::ParseNetAddress(std::string(ip_addr), &host_, &port_, nullptr, &err));
+    CHECK(port_ > 0 && port_ <= 65535);
+
+    if (!StartConnection()) {
+        LOG(ERROR) << "Unable to start PairingClient connection";
+        state_ = State::Stopped;
+        return false;
+    }
+
+    state_ = State::Running;
+    return true;
+}
+
+static int network_connect(const std::string& host, int port, int type, int timeout,
+                           std::string* error) {
+    int getaddrinfo_error = 0;
+    int fd = socket_network_client_timeout(host.c_str(), port, type, timeout, &getaddrinfo_error);
+    if (fd != -1) {
+        return fd;
+    }
+    if (getaddrinfo_error != 0) {
+        *error = android::base::StringPrintf("failed to resolve host: '%s': %s", host.c_str(),
+                                             gai_strerror(getaddrinfo_error));
+        LOG(WARNING) << *error;
+    } else {
+        *error = android::base::StringPrintf("failed to connect to '%s:%d': %s", host.c_str(), port,
+                                             strerror(errno));
+        LOG(WARNING) << *error;
+    }
+    return -1;
+}
+
+// static
+void PairingClientImpl::PairingResultCallback(const PeerInfo* peer_info, int /* fd */,
+                                              void* opaque) {
+    auto* p = reinterpret_cast<PairingClientImpl*>(opaque);
+    p->cb_(peer_info, p->opaque_);
+}
+
+bool PairingClientImpl::StartConnection() {
+    std::string err;
+    const int timeout = 10;  // seconds
+    unique_fd fd(network_connect(host_, port_, SOCK_STREAM, timeout, &err));
+    if (fd.get() == -1) {
+        LOG(ERROR) << "Failed to start pairing connection client [" << err << "]";
+        return false;
+    }
+    int off = 1;
+    setsockopt(fd.get(), IPPROTO_TCP, TCP_NODELAY, &off, sizeof(off));
+
+    connection_ = CreatePairingConnection(pswd_, peer_info_, cert_, priv_key_);
+    if (connection_ == nullptr) {
+        LOG(ERROR) << "PairingClient unable to create a PairingConnection";
+        return false;
+    }
+
+    if (!pairing_connection_start(connection_.get(), fd.release(), PairingResultCallback, this)) {
+        LOG(ERROR) << "PairingClient failed to start the PairingConnection";
+        state_ = State::Stopped;
+        return false;
+    }
+
+    return true;
+}
+
+}  // namespace
+
+// static
+std::unique_ptr<PairingClient> PairingClient::Create(const Data& pswd, const PeerInfo& peer_info,
+                                                     const Data& cert, const Data& priv_key) {
+    CHECK(!pswd.empty());
+    CHECK(!cert.empty());
+    CHECK(!priv_key.empty());
+
+    return std::unique_ptr<PairingClient>(new PairingClientImpl(pswd, peer_info, cert, priv_key));
+}
+
+}  // namespace pairing
+}  // namespace adb
diff --git a/adb/pairing_connection/tests/pairing_client.h b/adb/pairing_connection/tests/pairing_client.h
new file mode 100644
index 0000000..be0db5c
--- /dev/null
+++ b/adb/pairing_connection/tests/pairing_client.h
@@ -0,0 +1,68 @@
+/*
+ * 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 <stddef.h>
+#include <stdint.h>
+
+#include <functional>
+#include <memory>
+#include <string_view>
+#include <vector>
+
+#include "adb/pairing/pairing_connection.h"
+
+typedef void (*pairing_client_result_cb)(const PeerInfo*, void*);
+
+namespace adb {
+namespace pairing {
+
+// PairingClient is the client side of the PairingConnection protocol. It will
+// attempt to connect to a PairingServer specified at |host| and |port|, and
+// allocate a new PairingConnection for processing.
+//
+// See pairing_connection_test.cpp for example usage.
+//
+class PairingClient {
+  public:
+    using Data = std::vector<uint8_t>;
+
+    virtual ~PairingClient() = default;
+
+    // Starts the pairing client. This call is non-blocking. Upon completion,
+    // if the pairing was successful, then |cb| will be called with the PeerInfo
+    // containing the info of the trusted peer. Otherwise, |cb| will be
+    // called with an empty value. Start can only be called once in the lifetime
+    // of this object. |ip_addr| requires a port to be specified.
+    //
+    // Returns true if PairingClient was successfully started. Otherwise,
+    // returns false.
+    virtual bool Start(std::string_view ip_addr, pairing_client_result_cb cb, void* opaque) = 0;
+
+    // Creates a new PairingClient instance. May return null if unable
+    // to create an instance. |pswd|, |certificate|, |priv_key| and
+    // |ip_addr| cannot be empty. |peer_info| must contain non-empty strings for
+    // the guid and name fields.
+    static std::unique_ptr<PairingClient> Create(const Data& pswd, const PeerInfo& peer_info,
+                                                 const Data& certificate, const Data& priv_key);
+
+  protected:
+    PairingClient() = default;
+};  // class PairingClient
+
+}  // namespace pairing
+}  // namespace adb
diff --git a/adb/pairing_connection/tests/pairing_connection_test.cpp b/adb/pairing_connection/tests/pairing_connection_test.cpp
new file mode 100644
index 0000000..b6e09f1
--- /dev/null
+++ b/adb/pairing_connection/tests/pairing_connection_test.cpp
@@ -0,0 +1,500 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "AdbPairingConnectionTest"
+
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+#include <thread>
+
+#include <adb/pairing/pairing_server.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+
+#include "../internal/constants.h"
+#include "pairing_client.h"
+
+using namespace std::chrono_literals;
+
+namespace adb {
+namespace pairing {
+
+// Test X.509 certificates (RSA 2048)
+static const std::string kTestRsa2048ServerCert =
+        "-----BEGIN CERTIFICATE-----\n"
+        "MIIDFzCCAf+gAwIBAgIBATANBgkqhkiG9w0BAQsFADAtMQswCQYDVQQGEwJVUzEQ\n"
+        "MA4GA1UECgwHQW5kcm9pZDEMMAoGA1UEAwwDQWRiMB4XDTIwMDEyMTIyMjU1NVoX\n"
+        "DTMwMDExODIyMjU1NVowLTELMAkGA1UEBhMCVVMxEDAOBgNVBAoMB0FuZHJvaWQx\n"
+        "DDAKBgNVBAMMA0FkYjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK8E\n"
+        "2Ck9TfuKlz7wqWdMfknjZ1luFDp2IHxAUZzh/F6jeI2dOFGAjpeloSnGOE86FIaT\n"
+        "d1EvpyTh7nBwbrLZAA6XFZTo7Bl6BdNOQdqb2d2+cLEN0inFxqUIycevRtohUE1Y\n"
+        "FHM9fg442X1jOTWXjDZWeiqFWo95paAPhzm6pWqfJK1+YKfT1LsWZpYqJGGQE5pi\n"
+        "C3qOBYYgFpoXMxTYJNoZo3uOYEdM6upc8/vh15nMgIxX/ymJxEY5BHPpZPPWjXLg\n"
+        "BfzVaV9fUfv0JT4HQ4t2WvxC3cD/UsjWp2a6p454uUp2ENrANa+jRdRJepepg9D2\n"
+        "DKsx9L8zjc5Obqexrt0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B\n"
+        "Af8EBAMCAYYwHQYDVR0OBBYEFDFW+8GTErwoZN5Uu9KyY4QdGYKpMA0GCSqGSIb3\n"
+        "DQEBCwUAA4IBAQBCDEn6SHXGlq5TU7J8cg1kRPd9bsJW+0hDuKSq0REXDkl0PcBf\n"
+        "fy282Agg9enKPPKmnpeQjM1dmnxdM8tT8LIUbMl779i3fn6v9HJVB+yG4gmRFThW\n"
+        "c+AGlBnrIT820cX/gU3h3R3FTahfsq+1rrSJkEgHyuC0HYeRyveSckBdaEOLvx0S\n"
+        "toun+32JJl5hWydpUUZhE9Mbb3KHBRM2YYZZU9JeJ08Apjl+3lRUeMAUwI5fkAAu\n"
+        "z/1SqnuGL96bd8P5ixdkA1+rF8FPhodGcq9mQOuUGP9g5HOXjaNoJYvwVRUdLeGh\n"
+        "cP/ReOTwQIzM1K5a83p8cX8AGGYmM7dQp7ec\n"
+        "-----END CERTIFICATE-----\n";
+
+static const std::string kTestRsa2048ServerPrivKey =
+        "-----BEGIN PRIVATE KEY-----\n"
+        "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCvBNgpPU37ipc+\n"
+        "8KlnTH5J42dZbhQ6diB8QFGc4fxeo3iNnThRgI6XpaEpxjhPOhSGk3dRL6ck4e5w\n"
+        "cG6y2QAOlxWU6OwZegXTTkHam9ndvnCxDdIpxcalCMnHr0baIVBNWBRzPX4OONl9\n"
+        "Yzk1l4w2VnoqhVqPeaWgD4c5uqVqnyStfmCn09S7FmaWKiRhkBOaYgt6jgWGIBaa\n"
+        "FzMU2CTaGaN7jmBHTOrqXPP74deZzICMV/8picRGOQRz6WTz1o1y4AX81WlfX1H7\n"
+        "9CU+B0OLdlr8Qt3A/1LI1qdmuqeOeLlKdhDawDWvo0XUSXqXqYPQ9gyrMfS/M43O\n"
+        "Tm6nsa7dAgMBAAECggEAFCS2bPdUKIgjbzLgtHW+hT+J2hD20rcHdyAp+dNH/2vI\n"
+        "yLfDJHJA4chGMRondKA704oDw2bSJxxlG9t83326lB35yxPhye7cM8fqgWrK8PVl\n"
+        "tU22FhO1ZgeJvb9OeXWNxKZyDW9oOOJ8eazNXVMuEo+dFj7B6l3MXQyHJPL2mJDm\n"
+        "u9ofFLdypX+gJncVO0oW0FNJnEUn2MMwHDNlo7gc4WdQuidPkuZItKRGcB8TTGF3\n"
+        "Ka1/2taYdTQ4Aq//Z84LlFvE0zD3T4c8LwYYzOzD4gGGTXvft7vSHzIun1S8YLRS\n"
+        "dEKXdVjtaFhgH3uUe4j+1b/vMvSHeoGBNX/G88GD+wKBgQDWUYVlMVqc9HD2IeYi\n"
+        "EfBcNwAJFJkh51yAl5QbUBgFYgFJVkkS/EDxEGFPvEmI3/pAeQFHFY13BI466EPs\n"
+        "o8Z8UUwWDp+Z1MFHHKQKnFakbsZbZlbqjJ9VJsqpezbpWhMHTOmcG0dmE7rf0lyM\n"
+        "eQv9slBB8qp2NEUs5Of7f2C2bwKBgQDRDq4nUuMQF1hbjM05tGKSIwkobmGsLspv\n"
+        "TMhkM7fq4RpbFHmbNgsFqMhcqYZ8gY6/scv5KCuAZ4yHUkbqwf5h+QCwrJ4uJeUJ\n"
+        "ZgJfHus2mmcNSo8FwSkNoojIQtzcbJav7bs2K9VTuertk/i7IJLApU4FOZZ5pghN\n"
+        "EXu0CZF1cwKBgDWFGhjRIF29tU/h20R60llU6s9Zs3wB+NmsALJpZ/ZAKS4VPB5f\n"
+        "nCAXBRYSYRKrTCU5kpYbzb4BBzuysPOxWmnFK4j+keCqfrGxd02nCQP7HdHJVr8v\n"
+        "6sIq88UrHeVcNxBFprjzHvtgxfQK5k22FMZ/9wbhAKyQFQ5HA5+MiaxFAoGAIcZZ\n"
+        "ZIkDninnYIMS9OursShv5lRO+15j3i9tgKLKZ+wOMgDQ1L6acUOfezj4PU1BHr8+\n"
+        "0PYocQpJreMhCfRlgLaV4fVBaPs+UZJld7CrF5tCYudUy/01ALrtlk0XGZWBktK5\n"
+        "mDrksC4tQkzRtonAq9cJD9cJ9IVaefkFH0UcdvkCgYBpZj50VLeGhnHHBnkJRlV1\n"
+        "fV+/P6PAq6RtqjA6O9Qdaoj5V3w2d63aQcQXQLJjH2BBmtCIy47r04rFvZpbCxP7\n"
+        "NH/OnK9NHpk2ucRTe8TAnVbvF/TZzPJoIxAO/D3OWaW6df4R8en8u6GYzWFglAyT\n"
+        "sydGT8yfWD1FYUWgfrVRbg==\n"
+        "-----END PRIVATE KEY-----\n";
+
+static const std::string kTestRsa2048ClientCert =
+        "-----BEGIN CERTIFICATE-----\n"
+        "MIIDFzCCAf+gAwIBAgIBATANBgkqhkiG9w0BAQsFADAtMQswCQYDVQQGEwJVUzEQ\n"
+        "MA4GA1UECgwHQW5kcm9pZDEMMAoGA1UEAwwDQWRiMB4XDTIwMDEyMTIyMjU1NloX\n"
+        "DTMwMDExODIyMjU1NlowLTELMAkGA1UEBhMCVVMxEDAOBgNVBAoMB0FuZHJvaWQx\n"
+        "DDAKBgNVBAMMA0FkYjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAI3a\n"
+        "EXh1S5FTbet7JVONswffRPaekdIK53cb8SnAbSO9X5OLA4zGwdkrBvDTsd96SKrp\n"
+        "JxmoNOE1DhbZh05KPlWAPkGKacjGWaz+S7biDOL0I6aaLbTlU/il1Ub9olPSBVUx\n"
+        "0nhdtEFgIOzddnP6/1KmyIIeRxS5lTKeg4avqUkZNXkz/wL1dHBFL7FNFf0SCcbo\n"
+        "tsub/deFbjZ27LTDN+SIBgFttTNqC5NTvoBAoMdyCOAgNYwaHO+fKiK3edfJieaw\n"
+        "7HD8qqmQxcpCtRlA8CUPj7GfR+WHiCJmlevhnkFXCo56R1BS0F4wuD4KPdSWt8gc\n"
+        "27ejH/9/z2cKo/6SLJMCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B\n"
+        "Af8EBAMCAYYwHQYDVR0OBBYEFO/Mr5ygqqpyU/EHM9v7RDvcqaOkMA0GCSqGSIb3\n"
+        "DQEBCwUAA4IBAQAH33KMouzF2DYbjg90KDrDQr4rq3WfNb6P743knxdUFuvb+40U\n"
+        "QjC2OJZHkSexH7wfG/y6ic7vfCfF4clNs3QvU1lEjOZC57St8Fk7mdNdsWLwxEMD\n"
+        "uePFz0dvclSxNUHyCVMqNxddzQYzxiDWQRmXWrUBliMduQqEQelcxW2yDtg8bj+s\n"
+        "aMpR1ra9scaD4jzIZIIxLoOS9zBMuNRbgP217sZrniyGMhzoI1pZ/izN4oXpyH7O\n"
+        "THuaCzzRT3ph2f8EgmHSodz3ttgSf2DHzi/Ez1xUkk7NOlgNtmsxEdrM47+cC5ae\n"
+        "fIf2V+1o1JW8J7D11RmRbNPh3vfisueB4f88\n"
+        "-----END CERTIFICATE-----\n";
+
+static const std::string kTestRsa2048ClientPrivKey =
+        "-----BEGIN PRIVATE KEY-----\n"
+        "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCN2hF4dUuRU23r\n"
+        "eyVTjbMH30T2npHSCud3G/EpwG0jvV+TiwOMxsHZKwbw07Hfekiq6ScZqDThNQ4W\n"
+        "2YdOSj5VgD5BimnIxlms/ku24gzi9COmmi205VP4pdVG/aJT0gVVMdJ4XbRBYCDs\n"
+        "3XZz+v9SpsiCHkcUuZUynoOGr6lJGTV5M/8C9XRwRS+xTRX9EgnG6LbLm/3XhW42\n"
+        "duy0wzfkiAYBbbUzaguTU76AQKDHcgjgIDWMGhzvnyoit3nXyYnmsOxw/KqpkMXK\n"
+        "QrUZQPAlD4+xn0flh4giZpXr4Z5BVwqOekdQUtBeMLg+Cj3UlrfIHNu3ox//f89n\n"
+        "CqP+kiyTAgMBAAECggEAAa64eP6ggCob1P3c73oayYPIbvRqiQdAFOrr7Vwu7zbr\n"
+        "z0rde+n6RU0mrpc+4NuzyPMtrOGQiatLbidJB5Cx3z8U00ovqbCl7PtcgorOhFKe\n"
+        "VEzihebCcYyQqbWQcKtpDMhOgBxRwFoXieJb6VGXfa96FAZalCWvXgOrTl7/BF2X\n"
+        "qMqIm9nJi+yS5tIO8VdOsOmrMWRH/b/ENUcef4WpLoxTXr0EEgyKWraeZ/hhXo1e\n"
+        "z29dZKqdr9wMsq11NPsRddwS94jnDkXTo+EQyWVTfB7gb6yyp07s8jysaDb21tVv\n"
+        "UXB9MRhDV1mOv0ncXfXZ4/+4A2UahmZaLDAVLaat4QKBgQDAVRredhGRGl2Nkic3\n"
+        "KvZCAfyxug788CgasBdEiouz19iCCwcgMIDwnq0s3/WM7h/laCamT2x38riYDnpq\n"
+        "rkYMfuVtU9CjEL9pTrdfwbIRhTwYNqADaPz2mXwQUhRXutE5TIdgxxC/a+ZTh0qN\n"
+        "S+vhTj/4hf0IZhMh5Nqj7IPExQKBgQC8zxEzhmSGjys0GuE6Wl6Doo2TpiR6vwvi\n"
+        "xPLU9lmIz5eca/Rd/eERioFQqeoIWDLzx52DXuz6rUoQhbJWz9hP3yqCwXD+pbNP\n"
+        "oDJqDDbCC4IMYEb0IK/PEPH+gIpnTjoFcW+ecKDFG7W5Lt05J8WsJsfOaJvMrOU+\n"
+        "dLXq3IgxdwKBgQC5RAFq0v6e8G+3hFaEHL0z3igkpt3zJf7rnj37hx2FMmDa+3Z0\n"
+        "umQp5B9af61PgL12xLmeMBmC/Wp1BlVDV/Yf6Uhk5Hyv5t0KuomHEtTNbbLyfAPs\n"
+        "5P/vJu/L5NS1oT4S3LX3MineyjgGs+bLbpub3z1dzutrYLADUSiPCK/xJQKBgBQt\n"
+        "nQ0Ao+Wtj1R2OvPdjJRM3wyUiPmFSWPm4HzaBx+T8AQLlYYmB9O0FbXlMtnJc0iS\n"
+        "YMcVcgYoVu4FG9YjSF7g3s4yljzgwJUV7c1fmMqMKE3iTDLy+1cJ3JLycdgwiArk\n"
+        "4KTyLHxkRbuQwpvFIF8RlfD9RQlOwQE3v+llwDhpAoGBAL6XG6Rp6mBoD2Ds5c9R\n"
+        "943yYgSUes3ji1SI9zFqeJtj8Ml/enuK1xu+8E/BxB0//+vgZsH6i3i8GFwygKey\n"
+        "CGJF8CbiHc3EJc3NQIIRXcni/CGacf0HwC6m+PGFDBIpA4H2iDpVvCSofxttQiq0\n"
+        "/Z7HXmXUvZHVyYi/QzX2Gahj\n"
+        "-----END PRIVATE KEY-----\n";
+
+struct ServerDeleter {
+    void operator()(PairingServerCtx* p) { pairing_server_destroy(p); }
+};
+using ServerPtr = std::unique_ptr<PairingServerCtx, ServerDeleter>;
+
+struct ResultWaiter {
+    std::mutex mutex_;
+    std::condition_variable cv_;
+    std::optional<bool> is_valid_;
+    PeerInfo peer_info_;
+
+    static void ResultCallback(const PeerInfo* peer_info, void* opaque) {
+        auto* p = reinterpret_cast<ResultWaiter*>(opaque);
+        {
+            std::unique_lock<std::mutex> lock(p->mutex_);
+            if (peer_info) {
+                memcpy(&(p->peer_info_), peer_info, sizeof(PeerInfo));
+            }
+            p->is_valid_ = (peer_info != nullptr);
+        }
+        p->cv_.notify_one();
+    }
+};
+
+class AdbPairingConnectionTest : public testing::Test {
+  protected:
+    virtual void SetUp() override {}
+
+    virtual void TearDown() override {}
+
+    void InitPairing(const std::vector<uint8_t>& server_pswd,
+                     const std::vector<uint8_t>& client_pswd) {
+        server_ = CreateServer(server_pswd);
+        client_ = CreateClient(client_pswd);
+    }
+
+    ServerPtr CreateServer(const std::vector<uint8_t>& pswd) {
+        return CreateServer(pswd, &server_info_, kTestRsa2048ServerCert, kTestRsa2048ServerPrivKey,
+                            0);
+    }
+
+    std::unique_ptr<PairingClient> CreateClient(const std::vector<uint8_t> pswd) {
+        std::vector<uint8_t> cert;
+        std::vector<uint8_t> key;
+        // Include the null-byte as well.
+        cert.assign(reinterpret_cast<const uint8_t*>(kTestRsa2048ClientCert.data()),
+                    reinterpret_cast<const uint8_t*>(kTestRsa2048ClientCert.data()) +
+                            kTestRsa2048ClientCert.size() + 1);
+        key.assign(reinterpret_cast<const uint8_t*>(kTestRsa2048ClientPrivKey.data()),
+                   reinterpret_cast<const uint8_t*>(kTestRsa2048ClientPrivKey.data()) +
+                           kTestRsa2048ClientPrivKey.size() + 1);
+        return PairingClient::Create(pswd, client_info_, cert, key);
+    }
+
+    static ServerPtr CreateServer(const std::vector<uint8_t>& pswd, const PeerInfo* peer_info,
+                                  const std::string_view cert, const std::string_view priv_key,
+                                  int port) {
+        return ServerPtr(pairing_server_new(
+                pswd.data(), pswd.size(), peer_info, reinterpret_cast<const uint8_t*>(cert.data()),
+                cert.size(), reinterpret_cast<const uint8_t*>(priv_key.data()), priv_key.size(),
+                port));
+    }
+
+    ServerPtr server_;
+    const PeerInfo server_info_ = {
+            .type = ADB_DEVICE_GUID,
+            .data = "my_server_info",
+    };
+    std::unique_ptr<PairingClient> client_;
+    const PeerInfo client_info_ = {
+            .type = ADB_RSA_PUB_KEY,
+            .data = "my_client_info",
+    };
+    std::string ip_addr_ = "127.0.0.1:";
+};
+
+TEST_F(AdbPairingConnectionTest, ServerCreation) {
+    // All parameters bad
+    ASSERT_DEATH({ auto server = CreateServer({}, nullptr, "", "", 0); }, "");
+    // Bad password
+    ASSERT_DEATH(
+            {
+                auto server = CreateServer({}, &server_info_, kTestRsa2048ServerCert,
+                                           kTestRsa2048ServerPrivKey, 0);
+            },
+            "");
+    // Bad peer_info
+    ASSERT_DEATH(
+            {
+                auto server = CreateServer({0x01}, nullptr, kTestRsa2048ServerCert,
+                                           kTestRsa2048ServerPrivKey, 0);
+            },
+            "");
+    // Bad certificate
+    ASSERT_DEATH(
+            {
+                auto server = CreateServer({0x01}, &server_info_, "", kTestRsa2048ServerPrivKey, 0);
+            },
+            "");
+    // Bad private key
+    ASSERT_DEATH(
+            { auto server = CreateServer({0x01}, &server_info_, kTestRsa2048ServerCert, "", 0); },
+            "");
+    // Valid params
+    auto server = CreateServer({0x01}, &server_info_, kTestRsa2048ServerCert,
+                               kTestRsa2048ServerPrivKey, 0);
+    EXPECT_NE(nullptr, server);
+}
+
+TEST_F(AdbPairingConnectionTest, ClientCreation) {
+    std::vector<uint8_t> pswd{0x01, 0x03, 0x05, 0x07};
+    // Bad password
+    ASSERT_DEATH(
+            {
+                pairing_connection_client_new(
+                        nullptr, pswd.size(), &client_info_,
+                        reinterpret_cast<const uint8_t*>(kTestRsa2048ClientCert.data()),
+                        kTestRsa2048ClientCert.size(),
+                        reinterpret_cast<const uint8_t*>(kTestRsa2048ClientPrivKey.data()),
+                        kTestRsa2048ClientPrivKey.size());
+            },
+            "");
+    ASSERT_DEATH(
+            {
+                pairing_connection_client_new(
+                        pswd.data(), 0, &client_info_,
+                        reinterpret_cast<const uint8_t*>(kTestRsa2048ClientCert.data()),
+                        kTestRsa2048ClientCert.size(),
+                        reinterpret_cast<const uint8_t*>(kTestRsa2048ClientPrivKey.data()),
+                        kTestRsa2048ClientPrivKey.size());
+            },
+            "");
+
+    // Bad peer_info
+    ASSERT_DEATH(
+            {
+                pairing_connection_client_new(
+                        pswd.data(), pswd.size(), nullptr,
+                        reinterpret_cast<const uint8_t*>(kTestRsa2048ClientCert.data()),
+                        kTestRsa2048ClientCert.size(),
+                        reinterpret_cast<const uint8_t*>(kTestRsa2048ClientPrivKey.data()),
+                        kTestRsa2048ClientPrivKey.size());
+            },
+            "");
+
+    // Bad certificate
+    ASSERT_DEATH(
+            {
+                pairing_connection_client_new(
+                        pswd.data(), pswd.size(), &client_info_, nullptr,
+                        kTestRsa2048ClientCert.size(),
+                        reinterpret_cast<const uint8_t*>(kTestRsa2048ClientPrivKey.data()),
+                        kTestRsa2048ClientPrivKey.size());
+            },
+            "");
+    ASSERT_DEATH(
+            {
+                pairing_connection_client_new(
+                        pswd.data(), pswd.size(), &client_info_,
+                        reinterpret_cast<const uint8_t*>(kTestRsa2048ClientCert.data()), 0,
+                        reinterpret_cast<const uint8_t*>(kTestRsa2048ClientPrivKey.data()),
+                        kTestRsa2048ClientPrivKey.size());
+            },
+            "");
+
+    // Bad private key
+    ASSERT_DEATH(
+            {
+                pairing_connection_client_new(
+                        pswd.data(), pswd.size(), &client_info_,
+                        reinterpret_cast<const uint8_t*>(kTestRsa2048ClientCert.data()),
+                        kTestRsa2048ClientCert.size(), nullptr, kTestRsa2048ClientPrivKey.size());
+            },
+            "");
+    ASSERT_DEATH(
+            {
+                pairing_connection_client_new(
+                        pswd.data(), pswd.size(), &client_info_,
+                        reinterpret_cast<const uint8_t*>(kTestRsa2048ClientCert.data()),
+                        kTestRsa2048ClientCert.size(),
+                        reinterpret_cast<const uint8_t*>(kTestRsa2048ClientPrivKey.data()), 0);
+            },
+            "");
+
+    // Valid params
+    auto client = pairing_connection_client_new(
+            pswd.data(), pswd.size(), &client_info_,
+            reinterpret_cast<const uint8_t*>(kTestRsa2048ClientCert.data()),
+            kTestRsa2048ClientCert.size(),
+            reinterpret_cast<const uint8_t*>(kTestRsa2048ClientPrivKey.data()),
+            kTestRsa2048ClientPrivKey.size());
+    EXPECT_NE(nullptr, client);
+}
+
+TEST_F(AdbPairingConnectionTest, SmokeValidPairing) {
+    std::vector<uint8_t> pswd{0x01, 0x03, 0x05, 0x07};
+    InitPairing(pswd, pswd);
+
+    // Start the server
+    ResultWaiter server_waiter;
+    std::unique_lock<std::mutex> server_lock(server_waiter.mutex_);
+    auto port = pairing_server_start(server_.get(), server_waiter.ResultCallback, &server_waiter);
+    ASSERT_GT(port, 0);
+    ip_addr_ += std::to_string(port);
+
+    // Start the client
+    ResultWaiter client_waiter;
+    std::unique_lock<std::mutex> client_lock(client_waiter.mutex_);
+    ASSERT_TRUE(client_->Start(ip_addr_, client_waiter.ResultCallback, &client_waiter));
+    client_waiter.cv_.wait(client_lock, [&]() { return client_waiter.is_valid_.has_value(); });
+    ASSERT_TRUE(*(client_waiter.is_valid_));
+    ASSERT_EQ(strlen(reinterpret_cast<const char*>(client_waiter.peer_info_.data)),
+              strlen(reinterpret_cast<const char*>(server_info_.data)));
+    EXPECT_EQ(memcmp(client_waiter.peer_info_.data, server_info_.data, sizeof(server_info_.data)),
+              0);
+
+    // Kill server if the pairing failed, since server only shuts down when
+    // it gets a valid pairing.
+    if (!client_waiter.is_valid_) {
+        server_lock.unlock();
+        server_.reset();
+    } else {
+        server_waiter.cv_.wait(server_lock, [&]() { return server_waiter.is_valid_.has_value(); });
+        ASSERT_TRUE(*(server_waiter.is_valid_));
+        ASSERT_EQ(strlen(reinterpret_cast<const char*>(server_waiter.peer_info_.data)),
+                  strlen(reinterpret_cast<const char*>(client_info_.data)));
+        EXPECT_EQ(
+                memcmp(server_waiter.peer_info_.data, client_info_.data, sizeof(client_info_.data)),
+                0);
+    }
+}
+
+TEST_F(AdbPairingConnectionTest, CancelPairing) {
+    std::vector<uint8_t> pswd{0x01, 0x03, 0x05, 0x07};
+    std::vector<uint8_t> pswd2{0x01, 0x03, 0x05, 0x06};
+    InitPairing(pswd, pswd2);
+
+    // Start the server
+    ResultWaiter server_waiter;
+    std::unique_lock<std::mutex> server_lock(server_waiter.mutex_);
+    auto port = pairing_server_start(server_.get(), server_waiter.ResultCallback, &server_waiter);
+    ASSERT_GT(port, 0);
+    ip_addr_ += std::to_string(port);
+
+    // Start the client. Client should fail to pair
+    ResultWaiter client_waiter;
+    std::unique_lock<std::mutex> client_lock(client_waiter.mutex_);
+    ASSERT_TRUE(client_->Start(ip_addr_, client_waiter.ResultCallback, &client_waiter));
+    client_waiter.cv_.wait(client_lock, [&]() { return client_waiter.is_valid_.has_value(); });
+    ASSERT_FALSE(*(client_waiter.is_valid_));
+
+    // Kill the server. We should still receive the callback with no valid
+    // pairing.
+    server_lock.unlock();
+    server_.reset();
+    server_lock.lock();
+    ASSERT_TRUE(server_waiter.is_valid_.has_value());
+    EXPECT_FALSE(*(server_waiter.is_valid_));
+}
+
+TEST_F(AdbPairingConnectionTest, MultipleClientsAllFail) {
+    std::vector<uint8_t> pswd{0x01, 0x03, 0x05, 0x07};
+    std::vector<uint8_t> pswd2{0x01, 0x03, 0x05, 0x06};
+
+    // Start the server
+    auto server = CreateServer(pswd);
+    ResultWaiter server_waiter;
+    std::unique_lock<std::mutex> server_lock(server_waiter.mutex_);
+    auto port = pairing_server_start(server.get(), server_waiter.ResultCallback, &server_waiter);
+    ASSERT_GT(port, 0);
+    ip_addr_ += std::to_string(port);
+
+    // Start multiple clients, all with bad passwords
+    int test_num_clients = 5;
+    int num_clients_done = 0;
+    std::mutex global_clients_mutex;
+    std::unique_lock<std::mutex> global_clients_lock(global_clients_mutex);
+    std::condition_variable global_cv_;
+    for (int i = 0; i < test_num_clients; ++i) {
+        std::thread([&]() {
+            auto client = CreateClient(pswd2);
+            ResultWaiter client_waiter;
+            std::unique_lock<std::mutex> client_lock(client_waiter.mutex_);
+            ASSERT_TRUE(client->Start(ip_addr_, client_waiter.ResultCallback, &client_waiter));
+            client_waiter.cv_.wait(client_lock,
+                                   [&]() { return client_waiter.is_valid_.has_value(); });
+            ASSERT_FALSE(*(client_waiter.is_valid_));
+            {
+                std::lock_guard<std::mutex> global_lock(global_clients_mutex);
+                ++num_clients_done;
+            }
+            global_cv_.notify_one();
+        }).detach();
+    }
+
+    global_cv_.wait(global_clients_lock, [&]() { return num_clients_done == test_num_clients; });
+    server_lock.unlock();
+    server.reset();
+    server_lock.lock();
+    ASSERT_TRUE(server_waiter.is_valid_.has_value());
+    EXPECT_FALSE(*(server_waiter.is_valid_));
+}
+
+TEST_F(AdbPairingConnectionTest, MultipleClientsOnePass) {
+    // Send multiple clients with bad passwords, but send the last one with the
+    // correct password.
+    std::vector<uint8_t> pswd{0x01, 0x03, 0x05, 0x07};
+    std::vector<uint8_t> pswd2{0x01, 0x03, 0x05, 0x06};
+
+    // Start the server
+    auto server = CreateServer(pswd);
+    ResultWaiter server_waiter;
+    std::unique_lock<std::mutex> server_lock(server_waiter.mutex_);
+    auto port = pairing_server_start(server.get(), server_waiter.ResultCallback, &server_waiter);
+    ASSERT_GT(port, 0);
+    ip_addr_ += std::to_string(port);
+
+    // Start multiple clients, all with bad passwords
+    int test_num_clients = 5;
+    int num_clients_done = 0;
+    std::mutex global_clients_mutex;
+    std::unique_lock<std::mutex> global_clients_lock(global_clients_mutex);
+    std::condition_variable global_cv_;
+    for (int i = 0; i < test_num_clients; ++i) {
+        std::thread([&, i]() {
+            bool good_client = (i == (test_num_clients - 1));
+            auto client = CreateClient((good_client ? pswd : pswd2));
+            ResultWaiter client_waiter;
+            std::unique_lock<std::mutex> client_lock(client_waiter.mutex_);
+            ASSERT_TRUE(client->Start(ip_addr_, client_waiter.ResultCallback, &client_waiter));
+            client_waiter.cv_.wait(client_lock,
+                                   [&]() { return client_waiter.is_valid_.has_value(); });
+            if (good_client) {
+                ASSERT_TRUE(*(client_waiter.is_valid_));
+                ASSERT_EQ(strlen(reinterpret_cast<const char*>(client_waiter.peer_info_.data)),
+                          strlen(reinterpret_cast<const char*>(server_info_.data)));
+                EXPECT_EQ(memcmp(client_waiter.peer_info_.data, server_info_.data,
+                                 sizeof(server_info_.data)),
+                          0);
+            } else {
+                ASSERT_FALSE(*(client_waiter.is_valid_));
+            }
+            {
+                std::lock_guard<std::mutex> global_lock(global_clients_mutex);
+                ++num_clients_done;
+            }
+            global_cv_.notify_one();
+        }).detach();
+    }
+
+    global_cv_.wait(global_clients_lock, [&]() { return num_clients_done == test_num_clients; });
+    server_waiter.cv_.wait(server_lock, [&]() { return server_waiter.is_valid_.has_value(); });
+    ASSERT_TRUE(*(server_waiter.is_valid_));
+    ASSERT_EQ(strlen(reinterpret_cast<const char*>(server_waiter.peer_info_.data)),
+              strlen(reinterpret_cast<const char*>(client_info_.data)));
+    EXPECT_EQ(memcmp(server_waiter.peer_info_.data, client_info_.data, sizeof(client_info_.data)),
+              0);
+}
+
+}  // namespace pairing
+}  // namespace adb
diff --git a/adb/proto/Android.bp b/adb/proto/Android.bp
new file mode 100644
index 0000000..a7e5d9c
--- /dev/null
+++ b/adb/proto/Android.bp
@@ -0,0 +1,70 @@
+// 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.
+
+cc_defaults {
+    name: "libadb_protos_defaults",
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Wthread-safety",
+        "-Werror",
+    ],
+
+    compile_multilib: "both",
+
+    proto: {
+        export_proto_headers: true,
+        type: "lite",
+    },
+    srcs: [
+        "adb_known_hosts.proto",
+        "key_type.proto",
+        "pairing.proto",
+    ],
+    target: {
+        windows: {
+            compile_multilib: "first",
+            enabled: true,
+        },
+    },
+
+    visibility: [
+        "//system/core/adb:__subpackages__",
+    ],
+
+    stl: "libc++_static",
+
+    host_supported: true,
+    recovery_available: true,
+}
+
+cc_library {
+    name: "libadb_protos",
+    defaults: ["libadb_protos_defaults"],
+
+    apex_available: [
+        "com.android.adbd",
+        "test_com.android.adbd",
+    ],
+}
+
+// For running atest (b/147158681)
+cc_library_static {
+    name: "libadb_protos_static",
+    defaults: ["libadb_protos_defaults"],
+
+    apex_available: [
+        "//apex_available:platform",
+    ],
+}
diff --git a/adb/proto/adb_known_hosts.proto b/adb/proto/adb_known_hosts.proto
new file mode 100644
index 0000000..85d1489
--- /dev/null
+++ b/adb/proto/adb_known_hosts.proto
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+syntax = "proto3";
+
+option java_package = "com.android.server.adb.protos";
+option java_outer_classname = "AdbKnownHostsProto";
+
+package adb.proto;
+
+// Each known host
+message HostInfo {
+    string guid = 1;
+}
+
+// Protobuf definition for the adb_known_hosts.
+message AdbKnownHosts {
+    repeated HostInfo host_infos = 1;
+}
diff --git a/adb/proto/jarjar-rules.txt b/adb/proto/jarjar-rules.txt
new file mode 100644
index 0000000..4e40637
--- /dev/null
+++ b/adb/proto/jarjar-rules.txt
@@ -0,0 +1 @@
+rule com.google.protobuf.** com.android.framework.protobuf.@1
diff --git a/adb/proto/key_type.proto b/adb/proto/key_type.proto
new file mode 100644
index 0000000..ed451c5
--- /dev/null
+++ b/adb/proto/key_type.proto
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+syntax = "proto3";
+
+option java_package = "com.android.server.adb.protos";
+option java_outer_classname = "KeyTypeProto";
+
+package adb.proto;
+
+enum KeyType {
+    RSA_2048 = 0;
+}
diff --git a/adb/proto/pairing.proto b/adb/proto/pairing.proto
new file mode 100644
index 0000000..b0be20e
--- /dev/null
+++ b/adb/proto/pairing.proto
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+syntax = "proto3";
+
+option java_package = "com.android.server.adb.protos";
+option java_outer_classname = "PairingProto";
+
+package adb.proto;
+
+// The type of packets used in the pairing protocol
+message PairingPacket {
+    enum Type {
+        SPAKE2_MSG = 0;
+        PEER_INFO = 1;
+    }
+}
diff --git a/adb/protocol.txt b/adb/protocol.txt
index f4523c4..75700a4 100644
--- a/adb/protocol.txt
+++ b/adb/protocol.txt
@@ -79,6 +79,14 @@
 kind of unique ID (or empty), and banner is a human-readable version
 or identifier string.  The banner is used to transmit useful properties.
 
+--- STLS(type, version, "") --------------------------------------------
+
+Command constant: A_STLS
+
+The TLS message informs the recipient that the connection will be encrypted
+and will need to perform a TLS handshake. version is the current version of
+the protocol.
+
 
 --- AUTH(type, 0, "data") ----------------------------------------------
 
@@ -207,6 +215,7 @@
 #define A_OKAY 0x59414b4f
 #define A_CLSE 0x45534c43
 #define A_WRTE 0x45545257
+#define A_STLS 0x534C5453
 
 
 
diff --git a/adb/services.cpp b/adb/services.cpp
index 6185aa6..853d658 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -24,6 +24,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <cstring>
 #include <thread>
 
 #include <android-base/stringprintf.h>
@@ -34,6 +35,7 @@
 #include "adb_io.h"
 #include "adb_unique_fd.h"
 #include "adb_utils.h"
+#include "adb_wifi.h"
 #include "services.h"
 #include "socket_spec.h"
 #include "sysdeps.h"
@@ -193,6 +195,12 @@
     // Send response for emulator and device
     SendProtocolString(fd.get(), response);
 }
+
+static void pair_service(unique_fd fd, std::string host, std::string password) {
+    std::string response;
+    adb_wifi_pair_device(host, password, response);
+    SendProtocolString(fd.get(), response);
+}
 #endif
 
 #if ADB_HOST
@@ -248,6 +256,16 @@
         unique_fd fd = create_service_thread(
                 "connect", std::bind(connect_service, std::placeholders::_1, host));
         return create_local_socket(std::move(fd));
+    } else if (android::base::ConsumePrefix(&name, "pair:")) {
+        const char* divider = strchr(name.data(), ':');
+        if (!divider) {
+            return nullptr;
+        }
+        std::string password(name.data(), divider);
+        std::string host(divider + 1);
+        unique_fd fd = create_service_thread(
+                "pair", std::bind(pair_service, std::placeholders::_1, host, password));
+        return create_local_socket(std::move(fd));
     }
     return nullptr;
 }
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 0c5a6b4..4efbc02 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -88,6 +88,8 @@
 #undef mkdir
 #define mkdir ___xxx_mkdir
 
+extern int adb_rename(const char* oldpath, const char* newpath);
+
 // See the comments for the !defined(_WIN32) versions of adb_*().
 extern int adb_open(const char* path, int options);
 extern int adb_creat(const char* path, int mode);
@@ -101,6 +103,9 @@
 extern int adb_register_socket(SOCKET s);
 extern HANDLE adb_get_os_handle(borrowed_fd fd);
 
+extern int adb_gethostname(char* name, size_t len);
+extern int adb_getlogin_r(char* buf, size_t bufsize);
+
 // See the comments for the !defined(_WIN32) version of unix_close().
 static __inline__ int unix_close(int fd) {
     return close(fd);
@@ -267,6 +272,39 @@
 
 #define getcwd adb_getcwd
 
+// A very simple wrapper over a launched child process
+class Process {
+  public:
+    constexpr explicit Process(HANDLE h = nullptr) : h_(h) {}
+    ~Process() { close(); }
+    constexpr explicit operator bool() const { return h_ != nullptr; }
+
+    void wait() {
+        if (*this) {
+            ::WaitForSingleObject(h_, INFINITE);
+            close();
+        }
+    }
+    void kill() {
+        if (*this) {
+            ::TerminateProcess(h_, -1);
+        }
+    }
+
+  private:
+    void close() {
+        if (*this) {
+            ::CloseHandle(h_);
+            h_ = nullptr;
+        }
+    }
+
+    HANDLE h_;
+};
+
+Process adb_launch_process(std::string_view executable, std::vector<std::string> args,
+                           std::initializer_list<int> fds_to_inherit = {});
+
 // Helper class to convert UTF-16 argv from wmain() to UTF-8 args that can be
 // passed to main().
 class NarrowArgs {
@@ -428,15 +466,23 @@
     return s;
 }
 
+static __inline__ int adb_gethostname(char* name, size_t len) {
+    return gethostname(name, len);
+}
+
+static __inline__ int adb_getlogin_r(char* buf, size_t bufsize) {
+    return getlogin_r(buf, bufsize);
+}
+
 static __inline__ int adb_read(borrowed_fd fd, void* buf, size_t len) {
     return TEMP_FAILURE_RETRY(read(fd.get(), buf, len));
 }
 
-static __inline__ int adb_pread(int fd, void* buf, size_t len, off64_t offset) {
+static __inline__ int adb_pread(borrowed_fd fd, void* buf, size_t len, off64_t offset) {
 #if defined(__APPLE__)
-    return TEMP_FAILURE_RETRY(pread(fd, buf, len, offset));
+    return TEMP_FAILURE_RETRY(pread(fd.get(), buf, len, offset));
 #else
-    return TEMP_FAILURE_RETRY(pread64(fd, buf, len, offset));
+    return TEMP_FAILURE_RETRY(pread64(fd.get(), buf, len, offset));
 #endif
 }
 
@@ -604,6 +650,10 @@
 #undef mkdir
 #define mkdir ___xxx_mkdir
 
+static __inline__ int adb_rename(const char* oldpath, const char* newpath) {
+    return rename(oldpath, newpath);
+}
+
 static __inline__ int adb_is_absolute_host_path(const char* path) {
     return path[0] == '/';
 }
@@ -612,6 +662,32 @@
     return fd.get();
 }
 
+// A very simple wrapper over a launched child process
+class Process {
+  public:
+    constexpr explicit Process(pid_t pid) : pid_(pid) {}
+    constexpr explicit operator bool() const { return pid_ >= 0; }
+
+    void wait() {
+        if (*this) {
+            int status;
+            ::waitpid(pid_, &status, 0);
+            pid_ = -1;
+        }
+    }
+    void kill() {
+        if (*this) {
+            ::kill(pid_, SIGTERM);
+        }
+    }
+
+  private:
+    pid_t pid_;
+};
+
+Process adb_launch_process(std::string_view executable, std::vector<std::string> args,
+                           std::initializer_list<int> fds_to_inherit = {});
+
 #endif /* !_WIN32 */
 
 static inline void disable_tcp_nagle(borrowed_fd fd) {
diff --git a/adb/sysdeps_unix.cpp b/adb/sysdeps_unix.cpp
index 3fdc917..e565706 100644
--- a/adb/sysdeps_unix.cpp
+++ b/adb/sysdeps_unix.cpp
@@ -56,3 +56,37 @@
 
     return true;
 }
+
+static __inline__ void disable_close_on_exec(borrowed_fd fd) {
+    const auto oldFlags = fcntl(fd.get(), F_GETFD);
+    const auto newFlags = (oldFlags & ~FD_CLOEXEC);
+    if (newFlags != oldFlags) {
+        fcntl(fd.get(), F_SETFD, newFlags);
+    }
+}
+
+Process adb_launch_process(std::string_view executable, std::vector<std::string> args,
+                           std::initializer_list<int> fds_to_inherit) {
+    const auto pid = fork();
+    if (pid != 0) {
+        // parent, includes the case when failed to fork()
+        return Process(pid);
+    }
+    // child
+    std::vector<std::string> copies;
+    copies.reserve(args.size() + 1);
+    copies.emplace_back(executable);
+    copies.insert(copies.end(), std::make_move_iterator(args.begin()),
+                  std::make_move_iterator(args.end()));
+
+    std::vector<char*> rawArgs;
+    rawArgs.reserve(copies.size() + 1);
+    for (auto&& str : copies) {
+        rawArgs.push_back(str.data());
+    }
+    rawArgs.push_back(nullptr);
+    for (auto fd : fds_to_inherit) {
+        disable_close_on_exec(fd);
+    }
+    exit(execv(copies.front().data(), rawArgs.data()));
+}
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index d9cc36f..be82bc0 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -18,8 +18,9 @@
 
 #include "sysdeps.h"
 
-#include <winsock2.h> /* winsock.h *must* be included before windows.h. */
+#include <lmcons.h>
 #include <windows.h>
+#include <winsock2.h> /* winsock.h *must* be included before windows.h. */
 
 #include <errno.h>
 #include <stdio.h>
@@ -1009,6 +1010,55 @@
     return _fh_to_int(f);
 }
 
+static bool isBlankStr(const char* str) {
+    for (; *str != '\0'; ++str) {
+        if (!isblank(*str)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+int adb_gethostname(char* name, size_t len) {
+    const char* computerName = adb_getenv("COMPUTERNAME");
+    if (computerName && !isBlankStr(computerName)) {
+        strncpy(name, computerName, len);
+        name[len - 1] = '\0';
+        return 0;
+    }
+
+    wchar_t buffer[MAX_COMPUTERNAME_LENGTH + 1];
+    DWORD size = sizeof(buffer);
+    if (!GetComputerNameW(buffer, &size)) {
+        return -1;
+    }
+    std::string name_utf8;
+    if (!android::base::WideToUTF8(buffer, &name_utf8)) {
+        return -1;
+    }
+
+    strncpy(name, name_utf8.c_str(), len);
+    name[len - 1] = '\0';
+    return 0;
+}
+
+int adb_getlogin_r(char* buf, size_t bufsize) {
+    wchar_t buffer[UNLEN + 1];
+    DWORD len = sizeof(buffer);
+    if (!GetUserNameW(buffer, &len)) {
+        return -1;
+    }
+
+    std::string login;
+    if (!android::base::WideToUTF8(buffer, &login)) {
+        return -1;
+    }
+
+    strncpy(buf, login.c_str(), bufsize);
+    buf[bufsize - 1] = '\0';
+    return 0;
+}
+
 #undef accept
 int adb_socket_accept(borrowed_fd serverfd, struct sockaddr* addr, socklen_t* addrlen) {
     FH serverfh = _fh_from_int(serverfd, __func__);
@@ -2342,6 +2392,20 @@
     return _wmkdir(path_wide.c_str());
 }
 
+int adb_rename(const char* oldpath, const char* newpath) {
+    std::wstring oldpath_wide, newpath_wide;
+    if (!android::base::UTF8ToWide(oldpath, &oldpath_wide)) {
+        return -1;
+    }
+    if (!android::base::UTF8ToWide(newpath, &newpath_wide)) {
+        return -1;
+    }
+
+    // MSDN just says the return value is non-zero on failure, make sure it
+    // returns -1 on failure so that it behaves the same as other systems.
+    return _wrename(oldpath_wide.c_str(), newpath_wide.c_str()) ? -1 : 0;
+}
+
 // Version of utime() that takes a UTF-8 path.
 int adb_utime(const char* path, struct utimbuf* u) {
     std::wstring path_wide;
@@ -2771,6 +2835,66 @@
     return buf;
 }
 
+void enable_inherit(borrowed_fd fd) {
+    auto osh = adb_get_os_handle(fd);
+    const auto h = reinterpret_cast<HANDLE>(osh);
+    ::SetHandleInformation(h, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
+}
+
+void disable_inherit(borrowed_fd fd) {
+    auto osh = adb_get_os_handle(fd);
+    const auto h = reinterpret_cast<HANDLE>(osh);
+    ::SetHandleInformation(h, HANDLE_FLAG_INHERIT, 0);
+}
+
+Process adb_launch_process(std::string_view executable, std::vector<std::string> args,
+                           std::initializer_list<int> fds_to_inherit) {
+    std::wstring wexe;
+    if (!android::base::UTF8ToWide(executable.data(), executable.size(), &wexe)) {
+        return Process();
+    }
+
+    std::wstring wargs = L"\"" + wexe + L"\"";
+    std::wstring warg;
+    for (auto arg : args) {
+        warg.clear();
+        if (!android::base::UTF8ToWide(arg.data(), arg.size(), &warg)) {
+            return Process();
+        }
+        wargs += L" \"";
+        wargs += warg;
+        wargs += L'\"';
+    }
+
+    STARTUPINFOW sinfo = {sizeof(sinfo)};
+    PROCESS_INFORMATION pinfo = {};
+
+    // TODO: use the Vista+ API to pass the list of inherited handles explicitly;
+    // see http://blogs.msdn.com/b/oldnewthing/archive/2011/12/16/10248328.aspx
+    for (auto fd : fds_to_inherit) {
+        enable_inherit(fd);
+    }
+    const auto created = CreateProcessW(wexe.c_str(), wargs.data(),
+                                        nullptr,                    // process attributes
+                                        nullptr,                    // thread attributes
+                                        fds_to_inherit.size() > 0,  // inherit any handles?
+                                        0,                          // flags
+                                        nullptr,                    // environment
+                                        nullptr,                    // current directory
+                                        &sinfo,                     // startup info
+                                        &pinfo);
+    for (auto fd : fds_to_inherit) {
+        disable_inherit(fd);
+    }
+
+    if (!created) {
+        return Process();
+    }
+
+    ::CloseHandle(pinfo.hThread);
+    return Process(pinfo.hProcess);
+}
+
 // The SetThreadDescription API was brought in version 1607 of Windows 10.
 typedef HRESULT(WINAPI* SetThreadDescription)(HANDLE hThread, PCWSTR lpThreadDescription);
 
diff --git a/adb/test_device.py b/adb/test_device.py
index 083adce..6a9ff89 100755
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
 #
 # Copyright (C) 2015 The Android Open Source Project
@@ -81,6 +81,13 @@
         self.device = adb.get_device()
 
 
+class AbbTest(DeviceTest):
+    def test_smoke(self):
+        result = subprocess.run(['adb', 'abb'], capture_output=True)
+        self.assertEqual(1, result.returncode)
+        expected_output = b"cmd: No service specified; use -l to list all services\n"
+        self.assertEqual(expected_output, result.stderr)
+
 class ForwardReverseTest(DeviceTest):
     def _test_no_rebind(self, description, direction_list, direction,
                        direction_no_rebind, direction_remove_all):
@@ -246,7 +253,7 @@
                     # Accept the client connection.
                     accepted_connection, addr = listener.accept()
                     with contextlib.closing(accepted_connection) as server:
-                        data = 'hello'
+                        data = b'hello'
 
                         # Send data into the port setup by adb forward.
                         client.sendall(data)
@@ -254,7 +261,7 @@
                         client.close()
 
                         # Verify that the data came back via adb reverse.
-                        self.assertEqual(data, server.makefile().read())
+                        self.assertEqual(data, server.makefile().read().encode("utf8"))
         finally:
             if reverse_setup:
                 self.device.reverse_remove(forward_spec)
@@ -268,7 +275,7 @@
 
         Args:
           shell_args: List of string arguments to `adb shell`.
-          input: String input to send to the interactive shell.
+          input: bytes input to send to the interactive shell.
 
         Returns:
           The remote exit code.
@@ -286,7 +293,7 @@
         # Closing host-side stdin doesn't trigger a PTY shell to exit so we need
         # to explicitly add an exit command to close the session from the device
         # side, plus the necessary newline to complete the interactive command.
-        proc.communicate(input + '; exit\n')
+        proc.communicate(input + b'; exit\n')
         return proc.returncode
 
     def test_cat(self):
@@ -419,7 +426,7 @@
         self.assertEqual('foo' + self.device.linesep, result[1])
         self.assertEqual('bar' + self.device.linesep, result[2])
 
-        self.assertEqual(17, self._interactive_shell([], 'exit 17'))
+        self.assertEqual(17, self._interactive_shell([], b'exit 17'))
 
         # -x flag should disable shell protocol.
         result = self.device.shell_nocheck(
@@ -428,7 +435,7 @@
         self.assertEqual('foo{0}bar{0}'.format(self.device.linesep), result[1])
         self.assertEqual('', result[2])
 
-        self.assertEqual(0, self._interactive_shell(['-x'], 'exit 17'))
+        self.assertEqual(0, self._interactive_shell(['-x'], b'exit 17'))
 
     def test_non_interactive_sigint(self):
         """Tests that SIGINT in a non-interactive shell kills the process.
@@ -447,7 +454,7 @@
                 self.device.adb_cmd + shlex.split('shell echo $$; sleep 60'),
                 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
                 stderr=subprocess.STDOUT)
-        remote_pid = sleep_proc.stdout.readline().strip()
+        remote_pid = sleep_proc.stdout.readline().strip().decode("utf8")
         self.assertIsNone(sleep_proc.returncode, 'subprocess terminated early')
         proc_query = shlex.split('ps {0} | grep {0}'.format(remote_pid))
 
@@ -469,9 +476,10 @@
                                     'on this device')
 
         # Test both small and large inputs.
-        small_input = 'foo'
-        large_input = '\n'.join(c * 100 for c in (string.ascii_letters +
-                                                  string.digits))
+        small_input = b'foo'
+        characters = [c.encode("utf8") for c in string.ascii_letters + string.digits]
+        large_input = b'\n'.join(characters)
+
 
         for input in (small_input, large_input):
             proc = subprocess.Popen(self.device.adb_cmd + ['shell', 'cat'],
@@ -480,7 +488,7 @@
                                     stderr=subprocess.PIPE)
             stdout, stderr = proc.communicate(input)
             self.assertEqual(input.splitlines(), stdout.splitlines())
-            self.assertEqual('', stderr)
+            self.assertEqual(b'', stderr)
 
     def test_sighup(self):
         """Ensure that SIGHUP gets sent upon non-interactive ctrl-c"""
@@ -502,7 +510,7 @@
                                           stdin=subprocess.PIPE,
                                           stdout=subprocess.PIPE)
 
-        self.assertEqual("Waiting\n", process.stdout.readline())
+        self.assertEqual(b"Waiting\n", process.stdout.readline())
         process.send_signal(signal.SIGINT)
         process.wait()
 
@@ -533,7 +541,7 @@
             threads.append(thread)
         for thread in threads:
             thread.join()
-        for i, success in result.iteritems():
+        for i, success in result.items():
             self.assertTrue(success)
 
     def disabled_test_parallel(self):
@@ -546,24 +554,24 @@
 
         n_procs = 2048
         procs = dict()
-        for i in xrange(0, n_procs):
+        for i in range(0, n_procs):
             procs[i] = subprocess.Popen(
                 ['adb', 'shell', 'read foo; echo $foo; read rc; exit $rc'],
                 stdin=subprocess.PIPE,
                 stdout=subprocess.PIPE
             )
 
-        for i in xrange(0, n_procs):
+        for i in range(0, n_procs):
             procs[i].stdin.write("%d\n" % i)
 
-        for i in xrange(0, n_procs):
+        for i in range(0, n_procs):
             response = procs[i].stdout.readline()
             assert(response == "%d\n" % i)
 
-        for i in xrange(0, n_procs):
+        for i in range(0, n_procs):
             procs[i].stdin.write("%d\n" % (i % 256))
 
-        for i in xrange(0, n_procs):
+        for i in range(0, n_procs):
             assert(procs[i].wait() == i % 256)
 
 
@@ -602,14 +610,14 @@
     def test_install_argument_escaping(self):
         """Make sure that install argument escaping works."""
         # http://b/20323053, http://b/3090932.
-        for file_suffix in ('-text;ls;1.apk', "-Live Hold'em.apk"):
+        for file_suffix in (b'-text;ls;1.apk', b"-Live Hold'em.apk"):
             tf = tempfile.NamedTemporaryFile('wb', suffix=file_suffix,
                                              delete=False)
             tf.close()
 
             # Installing bogus .apks fails if the device supports exit codes.
             try:
-                output = self.device.install(tf.name)
+                output = self.device.install(tf.name.decode("utf8"))
             except subprocess.CalledProcessError as e:
                 output = e.output
 
@@ -712,7 +720,7 @@
     max_size = 16 * (1 << 10)
 
     files = []
-    for _ in xrange(num_files):
+    for _ in range(num_files):
         file_handle = tempfile.NamedTemporaryFile(dir=in_dir, delete=False)
 
         size = random.randrange(min_size, max_size, 1024)
@@ -731,7 +739,7 @@
     max_size = 16 * (1 << 10)
 
     files = []
-    for file_num in xrange(num_files):
+    for file_num in range(num_files):
         size = random.randrange(min_size, max_size, 1024)
 
         base_name = prefix + str(file_num)
@@ -878,7 +886,7 @@
             subdir_temp_files = make_random_host_files(in_dir=subdir,
                                                        num_files=4)
 
-            paths = map(lambda temp_file: temp_file.full_path, temp_files)
+            paths = [x.full_path for x in temp_files]
             paths.append(subdir)
             self.device._simple_call(['push'] + paths + [self.DEVICE_TEMP_DIR])
 
@@ -907,7 +915,7 @@
         Bug: http://b/26816782
         """
         with tempfile.NamedTemporaryFile() as tmp_file:
-            tmp_file.write('\0' * 1024 * 1024)
+            tmp_file.write(b'\0' * 1024 * 1024)
             tmp_file.flush()
             try:
                 self.device.push(local=tmp_file.name, remote='/system/')
@@ -915,8 +923,8 @@
             except subprocess.CalledProcessError as e:
                 output = e.output
 
-            self.assertTrue('Permission denied' in output or
-                            'Read-only file system' in output)
+            self.assertTrue(b'Permission denied' in output or
+                            b'Read-only file system' in output)
 
     @requires_non_root
     def test_push_directory_creation(self):
@@ -925,7 +933,7 @@
         Bug: http://b/110953234
         """
         with tempfile.NamedTemporaryFile() as tmp_file:
-            tmp_file.write('\0' * 1024 * 1024)
+            tmp_file.write(b'\0' * 1024 * 1024)
             tmp_file.flush()
             remote_path = self.DEVICE_TEMP_DIR + '/test_push_directory_creation'
             self.device.shell(['rm', '-rf', remote_path])
@@ -967,7 +975,7 @@
         except subprocess.CalledProcessError as e:
             output = e.output
 
-        self.assertIn('Permission denied', output)
+        self.assertIn(b'Permission denied', output)
 
         self.device.shell(['rm', '-f', self.DEVICE_TEMP_FILE])
 
@@ -1166,7 +1174,7 @@
             subdir_temp_files = make_random_device_files(
                 self.device, in_dir=subdir, num_files=4, prefix='subdir_')
 
-            paths = map(lambda temp_file: temp_file.full_path, temp_files)
+            paths = [x.full_path for x in temp_files]
             paths.append(subdir)
             self.device._simple_call(['pull'] + paths + [host_dir])
 
@@ -1355,7 +1363,7 @@
         """
         # The values that trigger things are 507 (512 - 5 bytes from shell protocol) + 1024*n
         # Probe some surrounding values as well, for the hell of it.
-        for base in [512] + range(1024, 1024 * 16, 1024):
+        for base in [512] + list(range(1024, 1024 * 16, 1024)):
             for offset in [-6, -5, -4]:
                 length = base + offset
                 cmd = ['dd', 'if=/dev/zero', 'bs={}'.format(length), 'count=1', '2>/dev/null;'
@@ -1390,10 +1398,10 @@
 
                 sock = socket.create_connection(("localhost", local_port))
                 with contextlib.closing(sock):
-                    bytesWritten = sock.send("a" * size)
+                    bytesWritten = sock.send(b"a" * size)
                     self.assertEqual(size, bytesWritten)
                     readBytes = sock.recv(4096)
-                    self.assertEqual("foo\n", readBytes)
+                    self.assertEqual(b"foo\n", readBytes)
 
                 thread.join()
         finally:
@@ -1424,13 +1432,13 @@
 
         s.sendall(adb_length_prefixed(transport_string))
         response = s.recv(4)
-        self.assertEquals(b"OKAY", response)
+        self.assertEqual(b"OKAY", response)
 
         shell_string = "shell:sleep 0.5; dd if=/dev/zero bs=1m count=1 status=none; echo foo"
         s.sendall(adb_length_prefixed(shell_string))
 
         response = s.recv(4)
-        self.assertEquals(b"OKAY", response)
+        self.assertEqual(b"OKAY", response)
 
         # Spawn a thread that dumps garbage into the socket until failure.
         def spam():
@@ -1453,7 +1461,7 @@
                 break
             received += read
 
-        self.assertEquals(1024 * 1024 + len("foo\n"), len(received))
+        self.assertEqual(1024 * 1024 + len("foo\n"), len(received))
         thread.join()
 
 
diff --git a/adb/tls/Android.bp b/adb/tls/Android.bp
new file mode 100644
index 0000000..49833ff
--- /dev/null
+++ b/adb/tls/Android.bp
@@ -0,0 +1,75 @@
+// 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.
+
+cc_defaults {
+    name: "libadb_tls_connection_defaults",
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Wthread-safety",
+        "-Werror",
+    ],
+
+    compile_multilib: "both",
+
+    srcs: [
+        "adb_ca_list.cpp",
+        "tls_connection.cpp",
+    ],
+    target: {
+        windows: {
+            compile_multilib: "first",
+            enabled: true,
+        },
+    },
+    export_include_dirs: ["include"],
+
+    host_supported: true,
+    recovery_available: true,
+
+    visibility: [
+        "//system/core/adb:__subpackages__",
+    ],
+
+    stl: "libc++_static",
+
+    static_libs: [
+        "libbase",
+    ],
+    shared_libs: [
+        "libcrypto",
+        "liblog",
+        "libssl",
+    ],
+}
+
+cc_library {
+    name: "libadb_tls_connection",
+    defaults: ["libadb_tls_connection_defaults"],
+
+    apex_available: [
+        "com.android.adbd",
+        "test_com.android.adbd",
+    ],
+}
+
+// For running atest (b/147158681)
+cc_library_static {
+    name: "libadb_tls_connection_static",
+    defaults: ["libadb_tls_connection_defaults"],
+
+    apex_available: [
+        "//apex_available:platform",
+    ],
+}
diff --git a/adb/tls/adb_ca_list.cpp b/adb/tls/adb_ca_list.cpp
new file mode 100644
index 0000000..36afe42
--- /dev/null
+++ b/adb/tls/adb_ca_list.cpp
@@ -0,0 +1,136 @@
+/*
+ * 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 "adb/tls/adb_ca_list.h"
+
+#include <iomanip>
+#include <sstream>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <openssl/ssl.h>
+
+namespace adb {
+namespace tls {
+
+namespace {
+
+// CA issuer identifier to distinguished embedded keys. Also has version
+// information appended to the end of the string (e.g. "AdbKey-0").
+static constexpr int kAdbKeyIdentifierNid = NID_organizationName;
+static constexpr char kAdbKeyIdentifierV0[] = "AdbKey-0";
+
+// Where we store the actual data
+static constexpr int kAdbKeyValueNid = NID_commonName;
+
+// TODO: Remove this once X509_NAME_add_entry_by_NID is fixed to use const unsigned char*
+// https://boringssl-review.googlesource.com/c/boringssl/+/39764
+int X509_NAME_add_entry_by_NID_const(X509_NAME* name, int nid, int type, const unsigned char* bytes,
+                                     int len, int loc, int set) {
+    return X509_NAME_add_entry_by_NID(name, nid, type, const_cast<unsigned char*>(bytes), len, loc,
+                                      set);
+}
+
+bool IsHexDigit(char c) {
+    return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
+}
+
+// Wrapper around X509_NAME_get_text_by_NID that first calculates the size
+// of the string. Returns empty string on failure.
+std::optional<std::string> GetX509NameTextByNid(X509_NAME* name, int nid) {
+    // |len| is the len of the text excluding the final null
+    int len = X509_NAME_get_text_by_NID(name, nid, nullptr, -1);
+    if (len <= 0) {
+        return std::nullopt;
+    }
+
+    // Include the space for the final null byte
+    std::vector<char> buf(len + 1, '\0');
+    CHECK(X509_NAME_get_text_by_NID(name, nid, buf.data(), buf.size()));
+    return std::make_optional(std::string(buf.data()));
+}
+
+}  // namespace
+
+// Takes an encoded public key and generates a X509_NAME that can be used in
+// TlsConnection::SetClientCAList(), to allow the client to figure out which of
+// its keys it should try to use in the TLS handshake.
+bssl::UniquePtr<X509_NAME> CreateCAIssuerFromEncodedKey(std::string_view key) {
+    // "O=AdbKey-0;CN=<key>;"
+    CHECK(!key.empty());
+
+    std::string identifier = kAdbKeyIdentifierV0;
+    bssl::UniquePtr<X509_NAME> name(X509_NAME_new());
+    CHECK(X509_NAME_add_entry_by_NID_const(name.get(), kAdbKeyIdentifierNid, MBSTRING_ASC,
+                                           reinterpret_cast<const uint8_t*>(identifier.data()),
+                                           identifier.size(), -1, 0));
+
+    CHECK(X509_NAME_add_entry_by_NID_const(name.get(), kAdbKeyValueNid, MBSTRING_ASC,
+                                           reinterpret_cast<const uint8_t*>(key.data()), key.size(),
+                                           -1, 0));
+    return name;
+}
+
+// Parses a CA issuer and returns the encoded key, if any.
+std::optional<std::string> ParseEncodedKeyFromCAIssuer(X509_NAME* issuer) {
+    CHECK(issuer);
+
+    auto buf = GetX509NameTextByNid(issuer, kAdbKeyIdentifierNid);
+    if (!buf) {
+        return std::nullopt;
+    }
+
+    // Check for supported versions
+    if (*buf == kAdbKeyIdentifierV0) {
+        return GetX509NameTextByNid(issuer, kAdbKeyValueNid);
+    }
+    return std::nullopt;
+}
+
+std::string SHA256BitsToHexString(std::string_view sha256) {
+    CHECK_EQ(sha256.size(), static_cast<size_t>(SHA256_DIGEST_LENGTH));
+    std::stringstream ss;
+    auto* u8 = reinterpret_cast<const uint8_t*>(sha256.data());
+    ss << std::uppercase << std::setfill('0') << std::hex;
+    // Convert to hex-string representation
+    for (size_t i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
+        // Need to cast to something bigger than one byte, or
+        // stringstream will interpret it as a char value.
+        ss << std::setw(2) << static_cast<uint16_t>(u8[i]);
+    }
+    return ss.str();
+}
+
+std::optional<std::string> SHA256HexStringToBits(std::string_view sha256_str) {
+    if (sha256_str.size() != SHA256_DIGEST_LENGTH * 2) {
+        return std::nullopt;
+    }
+
+    std::string result;
+    for (size_t i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
+        auto bytestr = std::string(sha256_str.substr(i * 2, 2));
+        if (!IsHexDigit(bytestr[0]) || !IsHexDigit(bytestr[1])) {
+            LOG(ERROR) << "SHA256 string has invalid non-hex chars";
+            return std::nullopt;
+        }
+        result += static_cast<char>(std::stol(bytestr, nullptr, 16));
+    }
+    return result;
+}
+
+}  // namespace tls
+}  // namespace adb
diff --git a/adb/tls/include/adb/tls/adb_ca_list.h b/adb/tls/include/adb/tls/adb_ca_list.h
new file mode 100644
index 0000000..a1ab9a7
--- /dev/null
+++ b/adb/tls/include/adb/tls/adb_ca_list.h
@@ -0,0 +1,47 @@
+/*
+ * 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 <openssl/base.h>
+#include <optional>
+#include <string>
+
+// These APIs is used to embed adbd's known public keys into client-allowed CA
+// issuer list that can indicate to the client which key to use.
+namespace adb {
+namespace tls {
+
+// Takes an encoded public key and generates a X509_NAME that can be used in
+// TlsConnection::SetClientCAList(), to allow the client to figure out which of
+// its keys it should try to use in the TLS handshake. This is guaranteed to
+// return a valid X509_NAME, given a non-empty key.
+bssl::UniquePtr<X509_NAME> CreateCAIssuerFromEncodedKey(std::string_view key);
+
+// Parses a CA issuer and returns the encoded key, if any. On failure, returns
+// nullopt.
+std::optional<std::string> ParseEncodedKeyFromCAIssuer(X509_NAME* issuer);
+
+// Converts SHA256 bits to a hex string representation. |sha256| must be exactly
+// |SHA256_DIGEST_LENGTH| in size.
+std::string SHA256BitsToHexString(std::string_view sha256);
+
+// Converts a valid SHA256 hex string to the actual bits. Returns nullopt on
+// failure.
+std::optional<std::string> SHA256HexStringToBits(std::string_view sha256_str);
+
+}  // namespace tls
+}  // namespace adb
diff --git a/adb/tls/include/adb/tls/tls_connection.h b/adb/tls/include/adb/tls/tls_connection.h
new file mode 100644
index 0000000..bc5b98a
--- /dev/null
+++ b/adb/tls/include/adb/tls/tls_connection.h
@@ -0,0 +1,126 @@
+/*
+ * 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 <stddef.h>
+#include <stdint.h>
+
+#include <string_view>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+
+namespace adb {
+namespace tls {
+
+class TlsConnection {
+  public:
+    // This class will require both client and server to exchange valid
+    // certificates.
+    enum class Role {
+        Server,
+        Client,
+    };
+
+    enum class TlsError : uint8_t {
+        Success = 0,
+        // An error indicating that we rejected the peer's certificate.
+        CertificateRejected,
+        // An error indicating that the peer rejected our certificate.
+        PeerRejectedCertificate,
+        // Add more if needed
+        UnknownFailure,
+    };
+
+    using CertVerifyCb = std::function<int(X509_STORE_CTX*)>;
+    using SetCertCb = std::function<int(SSL*)>;
+
+    virtual ~TlsConnection() = default;
+
+    // Adds a trusted certificate to the list for the SSL connection.
+    // During the handshake phase, it will check the list of trusted certificates.
+    // The connection will fail if the peer's certificate is not in the list. If
+    // you would like to accept any certificate, use #SetCertVerifyCallback and
+    // set your callback to always return 1.
+    //
+    // Returns true if |cert| was successfully added, false otherwise.
+    virtual bool AddTrustedCertificate(std::string_view cert) = 0;
+
+    // Sets a custom certificate verify callback. |cb| must return 1 if the
+    // certificate is trusted. Otherwise, return 0 if not.
+    virtual void SetCertVerifyCallback(CertVerifyCb cb) = 0;
+
+    // Configures a client |ca_list| that the server sends to the client in the
+    // CertificateRequest message.
+    virtual void SetClientCAList(STACK_OF(X509_NAME) * ca_list) = 0;
+
+    // Sets a callback that will be called to select a certificate. See
+    // https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_set_cert_cb
+    // for more details.
+    virtual void SetCertificateCallback(SetCertCb cb) = 0;
+
+    // Exports a value derived from the master secret used in the TLS
+    // connection. This value should be used alongside any PAKE to ensure the
+    // peer is the intended peer. |length| is the requested length for the
+    // keying material. This is only valid after |DoHandshake| succeeds.
+    virtual std::vector<uint8_t> ExportKeyingMaterial(size_t length) = 0;
+
+    // Enable client-side check on whether server accepted the handshake. In TLS
+    // 1.3, client will not know the server rejected the handshake until after
+    // performing a read operation. Basically, this will perform an
+    // SSL_peek right after the handshake and see whether that succeeds.
+    //
+    // IMPORTANT: this will only work if the protocol is a server-speaks-first
+    // type. Enabling this for the server is a no-op. This is disabled by
+    // default.
+    virtual void EnableClientPostHandshakeCheck(bool enable) = 0;
+
+    // Starts the handshake process. Returns TlsError::Success if handshake
+    // succeeded.
+    virtual TlsError DoHandshake() = 0;
+
+    // Reads |size| bytes and returns the data. The returned data has either
+    // size |size| or zero, in which case the read failed.
+    virtual std::vector<uint8_t> ReadFully(size_t size) = 0;
+
+    // Overloaded ReadFully method, which accepts a buffer for writing in.
+    // Returns true iff exactly |size| amount of data was written into |buf|,
+    // false otherwise.
+    virtual bool ReadFully(void* buf, size_t size) = 0;
+
+    // Writes |size| bytes. Returns true if all |size| bytes were read.
+    // Returns false otherwise.
+    virtual bool WriteFully(std::string_view data) = 0;
+
+    // Create a new TlsConnection instance. |cert| and |priv_key| cannot be
+    // empty.
+    static std::unique_ptr<TlsConnection> Create(Role role, std::string_view cert,
+                                                 std::string_view priv_key,
+                                                 android::base::borrowed_fd fd);
+
+    // Helper to set the certificate and key strings to a SSL client/server.
+    // Useful when in the set-certificate callback.
+    static bool SetCertAndKey(SSL* ssl, std::string_view cert_chain, std::string_view priv_key);
+
+  protected:
+    TlsConnection() = default;
+};  // TlsConnection
+
+}  // namespace tls
+}  // namespace adb
diff --git a/adb/tls/tests/Android.bp b/adb/tls/tests/Android.bp
new file mode 100644
index 0000000..198de58
--- /dev/null
+++ b/adb/tls/tests/Android.bp
@@ -0,0 +1,42 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+    name: "adb_tls_connection_test",
+    srcs: [
+        "adb_ca_list_test.cpp",
+        "tls_connection_test.cpp",
+    ],
+
+    compile_multilib: "first",
+
+    shared_libs: [
+        "libbase",
+        "libcrypto",
+        "libcrypto_utils",
+        "libssl",
+    ],
+
+    // Let's statically link them so we don't have to install it onto the
+    // system image for testing.
+    static_libs: [
+        "libadb_crypto_static",
+        "libadb_protos_static",
+        "libadb_tls_connection_static",
+    ],
+
+    test_suites: ["device-tests"],
+}
diff --git a/adb/tls/tests/adb_ca_list_test.cpp b/adb/tls/tests/adb_ca_list_test.cpp
new file mode 100644
index 0000000..c727e5f
--- /dev/null
+++ b/adb/tls/tests/adb_ca_list_test.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "AdbCAListTest"
+
+#include <gtest/gtest.h>
+
+#include <adb/tls/adb_ca_list.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <openssl/ssl.h>
+
+namespace adb {
+namespace tls {
+
+class AdbCAListTest : public testing::Test {
+  protected:
+    virtual void SetUp() override {}
+
+    virtual void TearDown() override {}
+};
+
+TEST_F(AdbCAListTest, SHA256BitsToHexString_BadParam) {
+    // Should crash if not exactly SHA256_DIGEST_LENGTH size
+    ASSERT_DEATH(
+            {
+                // empty
+                std::string sha;
+                SHA256BitsToHexString(sha);
+            },
+            "");
+    ASSERT_DEATH(
+            {
+                std::string sha(1, 0x80);
+                SHA256BitsToHexString(sha);
+            },
+            "");
+    ASSERT_DEATH(
+            {
+                std::string sha(SHA256_DIGEST_LENGTH - 1, 0x80);
+                SHA256BitsToHexString(sha);
+            },
+            "");
+    ASSERT_DEATH(
+            {
+                std::string sha(SHA256_DIGEST_LENGTH + 1, 0x80);
+                SHA256BitsToHexString(sha);
+            },
+            "");
+}
+
+TEST_F(AdbCAListTest, SHA256HexStringToBits_BadParam) {
+    {
+        // empty
+        std::string sha_str;
+        auto res = SHA256HexStringToBits(sha_str);
+        EXPECT_FALSE(res.has_value());
+    }
+    {
+        std::string sha_str(1, 'a');
+        auto res = SHA256HexStringToBits(sha_str);
+        EXPECT_FALSE(res.has_value());
+    }
+    {
+        std::string sha_str(SHA256_DIGEST_LENGTH * 2 - 1, 'a');
+        auto res = SHA256HexStringToBits(sha_str);
+        EXPECT_FALSE(res.has_value());
+    }
+    {
+        std::string sha_str(SHA256_DIGEST_LENGTH * 2 + 1, 'a');
+        auto res = SHA256HexStringToBits(sha_str);
+        EXPECT_FALSE(res.has_value());
+    }
+    {
+        // Non-hex chars
+        std::string sha_str(SHA256_DIGEST_LENGTH * 2, 'a');
+        sha_str[32] = 'x';
+        auto res = SHA256HexStringToBits(sha_str);
+        EXPECT_FALSE(res.has_value());
+    }
+}
+
+TEST_F(AdbCAListTest, SHA256BitsToHexString_ValidParam) {
+    uint8_t ct = 0;
+    // Test every possible byte
+    std::vector<std::string> expectedStr = {
+            "000102030405060708090A0B0C0D0E0F"
+            "101112131415161718191A1B1C1D1E1F",
+
+            "202122232425262728292A2B2C2D2E2F"
+            "303132333435363738393A3B3C3D3E3F",
+
+            "404142434445464748494A4B4C4D4E4F"
+            "505152535455565758595A5B5C5D5E5F",
+
+            "606162636465666768696A6B6C6D6E6F"
+            "707172737475767778797A7B7C7D7E7F",
+
+            "808182838485868788898A8B8C8D8E8F"
+            "909192939495969798999A9B9C9D9E9F",
+
+            "A0A1A2A3A4A5A6A7A8A9AAABACADAEAF"
+            "B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF",
+
+            "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"
+            "D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF",
+
+            "E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF"
+            "F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF",
+    };
+
+    for (auto& expected : expectedStr) {
+        std::string sha;
+        while (sha.size() < SHA256_DIGEST_LENGTH) {
+            sha += ct++;
+        }
+
+        auto sha_str = SHA256BitsToHexString(sha);
+        EXPECT_EQ(expected, sha_str);
+
+        // try to convert back to bits
+        auto out_sha = SHA256HexStringToBits(sha_str);
+        ASSERT_TRUE(out_sha.has_value());
+        EXPECT_EQ(*out_sha, sha);
+    }
+}
+
+TEST_F(AdbCAListTest, CreateCAIssuerFromEncodedKey_EmptyKey) {
+    ASSERT_DEATH({ auto issuer = CreateCAIssuerFromEncodedKey(""); }, "");
+}
+
+TEST_F(AdbCAListTest, Smoke) {
+    {
+        std::string key =
+                "A45BC1FF6C89BF0E"
+                "65F9BA153FBC9876"
+                "4969B4113F1CF878"
+                "EEF9BF1C3F9C9227";
+        auto issuer = CreateCAIssuerFromEncodedKey(key);
+        ASSERT_NE(issuer, nullptr);
+
+        // Try to parse the encoded key out of the X509_NAME
+        auto out_key = ParseEncodedKeyFromCAIssuer(issuer.get());
+        ASSERT_TRUE(out_key.has_value());
+        EXPECT_EQ(key, *out_key);
+    }
+}
+
+}  // namespace tls
+}  // namespace adb
diff --git a/adb/tls/tests/tls_connection_test.cpp b/adb/tls/tests/tls_connection_test.cpp
new file mode 100644
index 0000000..27bc1c9
--- /dev/null
+++ b/adb/tls/tests/tls_connection_test.cpp
@@ -0,0 +1,608 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "AdbWifiTlsConnectionTest"
+
+#include <thread>
+
+#include <gtest/gtest.h>
+
+#include <adb/crypto/rsa_2048_key.h>
+#include <adb/crypto/x509_generator.h>
+#include <adb/tls/adb_ca_list.h>
+#include <adb/tls/tls_connection.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <openssl/ssl.h>
+
+using namespace adb::crypto;
+
+namespace adb {
+namespace tls {
+
+using android::base::unique_fd;
+using TlsError = TlsConnection::TlsError;
+
+// Test X.509 certificates (RSA 2048)
+static const std::string kTestRsa2048ServerCert =
+        "-----BEGIN CERTIFICATE-----\n"
+        "MIIDFzCCAf+gAwIBAgIBATANBgkqhkiG9w0BAQsFADAtMQswCQYDVQQGEwJVUzEQ\n"
+        "MA4GA1UECgwHQW5kcm9pZDEMMAoGA1UEAwwDQWRiMB4XDTIwMDEyMTIyMjU1NVoX\n"
+        "DTMwMDExODIyMjU1NVowLTELMAkGA1UEBhMCVVMxEDAOBgNVBAoMB0FuZHJvaWQx\n"
+        "DDAKBgNVBAMMA0FkYjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK8E\n"
+        "2Ck9TfuKlz7wqWdMfknjZ1luFDp2IHxAUZzh/F6jeI2dOFGAjpeloSnGOE86FIaT\n"
+        "d1EvpyTh7nBwbrLZAA6XFZTo7Bl6BdNOQdqb2d2+cLEN0inFxqUIycevRtohUE1Y\n"
+        "FHM9fg442X1jOTWXjDZWeiqFWo95paAPhzm6pWqfJK1+YKfT1LsWZpYqJGGQE5pi\n"
+        "C3qOBYYgFpoXMxTYJNoZo3uOYEdM6upc8/vh15nMgIxX/ymJxEY5BHPpZPPWjXLg\n"
+        "BfzVaV9fUfv0JT4HQ4t2WvxC3cD/UsjWp2a6p454uUp2ENrANa+jRdRJepepg9D2\n"
+        "DKsx9L8zjc5Obqexrt0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B\n"
+        "Af8EBAMCAYYwHQYDVR0OBBYEFDFW+8GTErwoZN5Uu9KyY4QdGYKpMA0GCSqGSIb3\n"
+        "DQEBCwUAA4IBAQBCDEn6SHXGlq5TU7J8cg1kRPd9bsJW+0hDuKSq0REXDkl0PcBf\n"
+        "fy282Agg9enKPPKmnpeQjM1dmnxdM8tT8LIUbMl779i3fn6v9HJVB+yG4gmRFThW\n"
+        "c+AGlBnrIT820cX/gU3h3R3FTahfsq+1rrSJkEgHyuC0HYeRyveSckBdaEOLvx0S\n"
+        "toun+32JJl5hWydpUUZhE9Mbb3KHBRM2YYZZU9JeJ08Apjl+3lRUeMAUwI5fkAAu\n"
+        "z/1SqnuGL96bd8P5ixdkA1+rF8FPhodGcq9mQOuUGP9g5HOXjaNoJYvwVRUdLeGh\n"
+        "cP/ReOTwQIzM1K5a83p8cX8AGGYmM7dQp7ec\n"
+        "-----END CERTIFICATE-----\n";
+
+static const std::string kTestRsa2048ServerPrivKey =
+        "-----BEGIN PRIVATE KEY-----\n"
+        "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCvBNgpPU37ipc+\n"
+        "8KlnTH5J42dZbhQ6diB8QFGc4fxeo3iNnThRgI6XpaEpxjhPOhSGk3dRL6ck4e5w\n"
+        "cG6y2QAOlxWU6OwZegXTTkHam9ndvnCxDdIpxcalCMnHr0baIVBNWBRzPX4OONl9\n"
+        "Yzk1l4w2VnoqhVqPeaWgD4c5uqVqnyStfmCn09S7FmaWKiRhkBOaYgt6jgWGIBaa\n"
+        "FzMU2CTaGaN7jmBHTOrqXPP74deZzICMV/8picRGOQRz6WTz1o1y4AX81WlfX1H7\n"
+        "9CU+B0OLdlr8Qt3A/1LI1qdmuqeOeLlKdhDawDWvo0XUSXqXqYPQ9gyrMfS/M43O\n"
+        "Tm6nsa7dAgMBAAECggEAFCS2bPdUKIgjbzLgtHW+hT+J2hD20rcHdyAp+dNH/2vI\n"
+        "yLfDJHJA4chGMRondKA704oDw2bSJxxlG9t83326lB35yxPhye7cM8fqgWrK8PVl\n"
+        "tU22FhO1ZgeJvb9OeXWNxKZyDW9oOOJ8eazNXVMuEo+dFj7B6l3MXQyHJPL2mJDm\n"
+        "u9ofFLdypX+gJncVO0oW0FNJnEUn2MMwHDNlo7gc4WdQuidPkuZItKRGcB8TTGF3\n"
+        "Ka1/2taYdTQ4Aq//Z84LlFvE0zD3T4c8LwYYzOzD4gGGTXvft7vSHzIun1S8YLRS\n"
+        "dEKXdVjtaFhgH3uUe4j+1b/vMvSHeoGBNX/G88GD+wKBgQDWUYVlMVqc9HD2IeYi\n"
+        "EfBcNwAJFJkh51yAl5QbUBgFYgFJVkkS/EDxEGFPvEmI3/pAeQFHFY13BI466EPs\n"
+        "o8Z8UUwWDp+Z1MFHHKQKnFakbsZbZlbqjJ9VJsqpezbpWhMHTOmcG0dmE7rf0lyM\n"
+        "eQv9slBB8qp2NEUs5Of7f2C2bwKBgQDRDq4nUuMQF1hbjM05tGKSIwkobmGsLspv\n"
+        "TMhkM7fq4RpbFHmbNgsFqMhcqYZ8gY6/scv5KCuAZ4yHUkbqwf5h+QCwrJ4uJeUJ\n"
+        "ZgJfHus2mmcNSo8FwSkNoojIQtzcbJav7bs2K9VTuertk/i7IJLApU4FOZZ5pghN\n"
+        "EXu0CZF1cwKBgDWFGhjRIF29tU/h20R60llU6s9Zs3wB+NmsALJpZ/ZAKS4VPB5f\n"
+        "nCAXBRYSYRKrTCU5kpYbzb4BBzuysPOxWmnFK4j+keCqfrGxd02nCQP7HdHJVr8v\n"
+        "6sIq88UrHeVcNxBFprjzHvtgxfQK5k22FMZ/9wbhAKyQFQ5HA5+MiaxFAoGAIcZZ\n"
+        "ZIkDninnYIMS9OursShv5lRO+15j3i9tgKLKZ+wOMgDQ1L6acUOfezj4PU1BHr8+\n"
+        "0PYocQpJreMhCfRlgLaV4fVBaPs+UZJld7CrF5tCYudUy/01ALrtlk0XGZWBktK5\n"
+        "mDrksC4tQkzRtonAq9cJD9cJ9IVaefkFH0UcdvkCgYBpZj50VLeGhnHHBnkJRlV1\n"
+        "fV+/P6PAq6RtqjA6O9Qdaoj5V3w2d63aQcQXQLJjH2BBmtCIy47r04rFvZpbCxP7\n"
+        "NH/OnK9NHpk2ucRTe8TAnVbvF/TZzPJoIxAO/D3OWaW6df4R8en8u6GYzWFglAyT\n"
+        "sydGT8yfWD1FYUWgfrVRbg==\n"
+        "-----END PRIVATE KEY-----\n";
+
+static const std::string kTestRsa2048ClientCert =
+        "-----BEGIN CERTIFICATE-----\n"
+        "MIIDFzCCAf+gAwIBAgIBATANBgkqhkiG9w0BAQsFADAtMQswCQYDVQQGEwJVUzEQ\n"
+        "MA4GA1UECgwHQW5kcm9pZDEMMAoGA1UEAwwDQWRiMB4XDTIwMDEyMTIyMjU1NloX\n"
+        "DTMwMDExODIyMjU1NlowLTELMAkGA1UEBhMCVVMxEDAOBgNVBAoMB0FuZHJvaWQx\n"
+        "DDAKBgNVBAMMA0FkYjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAI3a\n"
+        "EXh1S5FTbet7JVONswffRPaekdIK53cb8SnAbSO9X5OLA4zGwdkrBvDTsd96SKrp\n"
+        "JxmoNOE1DhbZh05KPlWAPkGKacjGWaz+S7biDOL0I6aaLbTlU/il1Ub9olPSBVUx\n"
+        "0nhdtEFgIOzddnP6/1KmyIIeRxS5lTKeg4avqUkZNXkz/wL1dHBFL7FNFf0SCcbo\n"
+        "tsub/deFbjZ27LTDN+SIBgFttTNqC5NTvoBAoMdyCOAgNYwaHO+fKiK3edfJieaw\n"
+        "7HD8qqmQxcpCtRlA8CUPj7GfR+WHiCJmlevhnkFXCo56R1BS0F4wuD4KPdSWt8gc\n"
+        "27ejH/9/z2cKo/6SLJMCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B\n"
+        "Af8EBAMCAYYwHQYDVR0OBBYEFO/Mr5ygqqpyU/EHM9v7RDvcqaOkMA0GCSqGSIb3\n"
+        "DQEBCwUAA4IBAQAH33KMouzF2DYbjg90KDrDQr4rq3WfNb6P743knxdUFuvb+40U\n"
+        "QjC2OJZHkSexH7wfG/y6ic7vfCfF4clNs3QvU1lEjOZC57St8Fk7mdNdsWLwxEMD\n"
+        "uePFz0dvclSxNUHyCVMqNxddzQYzxiDWQRmXWrUBliMduQqEQelcxW2yDtg8bj+s\n"
+        "aMpR1ra9scaD4jzIZIIxLoOS9zBMuNRbgP217sZrniyGMhzoI1pZ/izN4oXpyH7O\n"
+        "THuaCzzRT3ph2f8EgmHSodz3ttgSf2DHzi/Ez1xUkk7NOlgNtmsxEdrM47+cC5ae\n"
+        "fIf2V+1o1JW8J7D11RmRbNPh3vfisueB4f88\n"
+        "-----END CERTIFICATE-----\n";
+
+static const std::string kTestRsa2048ClientPrivKey =
+        "-----BEGIN PRIVATE KEY-----\n"
+        "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCN2hF4dUuRU23r\n"
+        "eyVTjbMH30T2npHSCud3G/EpwG0jvV+TiwOMxsHZKwbw07Hfekiq6ScZqDThNQ4W\n"
+        "2YdOSj5VgD5BimnIxlms/ku24gzi9COmmi205VP4pdVG/aJT0gVVMdJ4XbRBYCDs\n"
+        "3XZz+v9SpsiCHkcUuZUynoOGr6lJGTV5M/8C9XRwRS+xTRX9EgnG6LbLm/3XhW42\n"
+        "duy0wzfkiAYBbbUzaguTU76AQKDHcgjgIDWMGhzvnyoit3nXyYnmsOxw/KqpkMXK\n"
+        "QrUZQPAlD4+xn0flh4giZpXr4Z5BVwqOekdQUtBeMLg+Cj3UlrfIHNu3ox//f89n\n"
+        "CqP+kiyTAgMBAAECggEAAa64eP6ggCob1P3c73oayYPIbvRqiQdAFOrr7Vwu7zbr\n"
+        "z0rde+n6RU0mrpc+4NuzyPMtrOGQiatLbidJB5Cx3z8U00ovqbCl7PtcgorOhFKe\n"
+        "VEzihebCcYyQqbWQcKtpDMhOgBxRwFoXieJb6VGXfa96FAZalCWvXgOrTl7/BF2X\n"
+        "qMqIm9nJi+yS5tIO8VdOsOmrMWRH/b/ENUcef4WpLoxTXr0EEgyKWraeZ/hhXo1e\n"
+        "z29dZKqdr9wMsq11NPsRddwS94jnDkXTo+EQyWVTfB7gb6yyp07s8jysaDb21tVv\n"
+        "UXB9MRhDV1mOv0ncXfXZ4/+4A2UahmZaLDAVLaat4QKBgQDAVRredhGRGl2Nkic3\n"
+        "KvZCAfyxug788CgasBdEiouz19iCCwcgMIDwnq0s3/WM7h/laCamT2x38riYDnpq\n"
+        "rkYMfuVtU9CjEL9pTrdfwbIRhTwYNqADaPz2mXwQUhRXutE5TIdgxxC/a+ZTh0qN\n"
+        "S+vhTj/4hf0IZhMh5Nqj7IPExQKBgQC8zxEzhmSGjys0GuE6Wl6Doo2TpiR6vwvi\n"
+        "xPLU9lmIz5eca/Rd/eERioFQqeoIWDLzx52DXuz6rUoQhbJWz9hP3yqCwXD+pbNP\n"
+        "oDJqDDbCC4IMYEb0IK/PEPH+gIpnTjoFcW+ecKDFG7W5Lt05J8WsJsfOaJvMrOU+\n"
+        "dLXq3IgxdwKBgQC5RAFq0v6e8G+3hFaEHL0z3igkpt3zJf7rnj37hx2FMmDa+3Z0\n"
+        "umQp5B9af61PgL12xLmeMBmC/Wp1BlVDV/Yf6Uhk5Hyv5t0KuomHEtTNbbLyfAPs\n"
+        "5P/vJu/L5NS1oT4S3LX3MineyjgGs+bLbpub3z1dzutrYLADUSiPCK/xJQKBgBQt\n"
+        "nQ0Ao+Wtj1R2OvPdjJRM3wyUiPmFSWPm4HzaBx+T8AQLlYYmB9O0FbXlMtnJc0iS\n"
+        "YMcVcgYoVu4FG9YjSF7g3s4yljzgwJUV7c1fmMqMKE3iTDLy+1cJ3JLycdgwiArk\n"
+        "4KTyLHxkRbuQwpvFIF8RlfD9RQlOwQE3v+llwDhpAoGBAL6XG6Rp6mBoD2Ds5c9R\n"
+        "943yYgSUes3ji1SI9zFqeJtj8Ml/enuK1xu+8E/BxB0//+vgZsH6i3i8GFwygKey\n"
+        "CGJF8CbiHc3EJc3NQIIRXcni/CGacf0HwC6m+PGFDBIpA4H2iDpVvCSofxttQiq0\n"
+        "/Z7HXmXUvZHVyYi/QzX2Gahj\n"
+        "-----END PRIVATE KEY-----\n";
+
+static const std::string kTestRsa2048UnknownPrivKey =
+        "-----BEGIN PRIVATE KEY-----\n"
+        "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCrIhr+CS+6UI0w\n"
+        "CTaVzQAicKBe6X531LeQAGYx7j5RLHR1QIoJ0WCc5msmXKe2VzcWuLbVdTGAIP1H\n"
+        "mwbPqlbO4ioxeJhiDv+WPuLG8+j4Iw1Yqxt8cfohxjfvNmIQM8aF5hGyyaaTetDF\n"
+        "EYWONoYCBC4WnFWgYCPb8mzWXlhHE3F66GnHpc32zydPTg3ZurGvSsFf7fNY9yRw\n"
+        "8WtwPiI6mpRxt+n2bQUp+LZ+g/3rXLFPg8uWDGYG7IvLluWc9gR9lxjL64t6ryLU\n"
+        "2cm7eTfDgLw/B1F/wEgCJDnby1JgQ4rq6klJO3BR2ooUr/7T343y5njG5hQJreV7\n"
+        "5ZnSmRLZAgMBAAECggEABPrfeHZFuWkj7KqN+DbAmt/2aMCodZ3+7/20+528WkIe\n"
+        "CvXzdmTth+9UHagLWNzpnVuHdYd9JuZ+3F00aelh8JAIDIu++naHhUSj9ohtRoBF\n"
+        "oIeNK5ZJAj/Zi5hkauaIz8dxyyc/VdIYfm2bundXd7pNqYqH2tyFWp6PwH67GKlZ\n"
+        "1lC7o8gKAK8sz9g0Ctdoe+hDqAsvYFCW4EWDM2qboucSgn8g3E/Gux/KrpXVv7d0\n"
+        "PMQ60m+dyTOCMGqXIoDR3TAvQR7ex5sQ/QZSREdxKy878s/2FY4ktxtCUWlhrmcI\n"
+        "VKtrDOGEKwNoiMluf2635rsVq2e01XhQlmdxbRFU0QKBgQDjOhhD1m9duFTQ2b+J\n"
+        "Xfn6m8Rs7sZqO4Az7gLOWmD/vYWlK4n2nZsh6u5/cB1N+PA+ncvvV4yKJAlLHxbT\n"
+        "pVvfzJ/jbUsj/NJg/w7+KYC9gXgRmBonuG2gRZF/5Otdlza4vMcoSkqGjlGxJyzL\n"
+        "+9umEziN3tEYMRwipYvt7BgbUQKBgQDAzaXryJ3YD3jpecy/+fSnQvFjpyeDRqU1\n"
+        "KDA9nxN5tJN6bnKhUlMhy64SsgvVX9jUuN7cK+qYV0uzdBn6kIAJNLWTdbtH93+e\n"
+        "vNVgluR3jmixW4QfY9vfZKdXZbVGNc0DFMi1vJqgxTgQ5Mq5PxxxRL4FsAF840V1\n"
+        "Wu9uhU0NCQKBgBfjga2QG8E0oeYbHmHouWE5gxsYt09v1fifqzfalJwOZsCIpUaC\n"
+        "J08Xjd9kABC0fT14BXqyL5pOU5PMPvAdUF1k++JDGUU9TTjZV9AsuNYziFYBMa6/\n"
+        "WvcgmT1i6cO7JAuj/SQlO1SOHdSME8+WOO9q0eVIaZ8repPB58YprhchAoGBAJyR\n"
+        "Y8AJdkTSq7nNszvi245IioYGY8vzPo3gSOyBlesrfOfbcTMYC3JSWNXNyFZKM2br\n"
+        "ie75qtRzb4IXMlGLrq3LI/jPjnpuvjBF4HFDl9yOxO3iB3UGPrM2pb4PVhnh7s4l\n"
+        "vqf2tQsBnPn7EbVFTu+ch0NPHqYwWWNnqS/zCBMhAoGBAIkYjOE0iD9W2FXee6VL\n"
+        "iN8wDqlqsGEEtLvykIDmTmM+ZX5ftQuPo18khpE9wQKmJ5OpoVTYIP1UsJFBakgo\n"
+        "+dGaf6xVuPvmydNFqixlW3z227n4Px6GX7CXlCaAleTeItezli+dWf/9astwTA3x\n"
+        "IazYzsxUUpZFC4dJ1GhBn3y1\n"
+        "-----END PRIVATE KEY-----\n";
+
+static const std::string kTestRsa2048UnknownCert =
+        "-----BEGIN CERTIFICATE-----\n"
+        "MIIDFzCCAf+gAwIBAgIBATANBgkqhkiG9w0BAQsFADAtMQswCQYDVQQGEwJVUzEQ\n"
+        "MA4GA1UECgwHQW5kcm9pZDEMMAoGA1UEAwwDQWRiMB4XDTIwMDEyNDE4MzMwNVoX\n"
+        "DTMwMDEyMTE4MzMwNVowLTELMAkGA1UEBhMCVVMxEDAOBgNVBAoMB0FuZHJvaWQx\n"
+        "DDAKBgNVBAMMA0FkYjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKsi\n"
+        "Gv4JL7pQjTAJNpXNACJwoF7pfnfUt5AAZjHuPlEsdHVAignRYJzmayZcp7ZXNxa4\n"
+        "ttV1MYAg/UebBs+qVs7iKjF4mGIO/5Y+4sbz6PgjDVirG3xx+iHGN+82YhAzxoXm\n"
+        "EbLJppN60MURhY42hgIELhacVaBgI9vybNZeWEcTcXroacelzfbPJ09ODdm6sa9K\n"
+        "wV/t81j3JHDxa3A+IjqalHG36fZtBSn4tn6D/etcsU+Dy5YMZgbsi8uW5Zz2BH2X\n"
+        "GMvri3qvItTZybt5N8OAvD8HUX/ASAIkOdvLUmBDiurqSUk7cFHaihSv/tPfjfLm\n"
+        "eMbmFAmt5XvlmdKZEtkCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B\n"
+        "Af8EBAMCAYYwHQYDVR0OBBYEFDtRSOm1ilhnq6bKN4qJ1ekK/PAkMA0GCSqGSIb3\n"
+        "DQEBCwUAA4IBAQAP6Q8/OxnBA3BO8oxKer0tjI4rZMefUhbAKUWXYjTTNEBm5//b\n"
+        "lVGP2RptO7bxj8w1L3rxsjmVcv2TqBOhrbJqvGVPE2ntoYlFhBBkRvmxuu1y5W9V\n"
+        "uJU7SF9lNmDXShTURULu3P8GdeT1HGeXzWQ4x7VhY9a3VIbmN5VxjB+3C6hYZxSs\n"
+        "DCpmidu/sR+n5Azlh6oqrhOxmv17PuF/ioTUsHd4y2Z41IvvO47oghxNDtboUUsg\n"
+        "LfsM1MOxVC9PqOfQphFU4i8owNIYzBMadDLw+1TSQj0ALqZVyc9Dq+WDFdz+JAE+\n"
+        "k7TkVU06UPGVSnLVzJeYwGCXQp3apBszY9vO\n"
+        "-----END CERTIFICATE-----\n";
+
+struct CAIssuerField {
+    int nid;
+    std::vector<uint8_t> val;
+};
+using CAIssuer = std::vector<CAIssuerField>;
+static std::vector<CAIssuer> kCAIssuers = {
+        {
+                {NID_commonName, {'a', 'b', 'c', 'd', 'e'}},
+                {NID_organizationName, {'d', 'e', 'f', 'g'}},
+        },
+        {
+                {NID_commonName, {'h', 'i', 'j', 'k', 'l', 'm'}},
+                {NID_countryName, {'n', 'o'}},
+        },
+};
+
+class AdbWifiTlsConnectionTest : public testing::Test {
+  protected:
+    virtual void SetUp() override {
+        android::base::Socketpair(SOCK_STREAM, &server_fd_, &client_fd_);
+        server_ = TlsConnection::Create(TlsConnection::Role::Server, kTestRsa2048ServerCert,
+                                        kTestRsa2048ServerPrivKey, server_fd_);
+        client_ = TlsConnection::Create(TlsConnection::Role::Client, kTestRsa2048ClientCert,
+                                        kTestRsa2048ClientPrivKey, client_fd_);
+        ASSERT_NE(nullptr, server_);
+        ASSERT_NE(nullptr, client_);
+    }
+
+    virtual void TearDown() override {
+        WaitForClientConnection();
+        // Shutdown the SSL connection first.
+        server_.reset();
+        client_.reset();
+    }
+
+    bssl::UniquePtr<STACK_OF(X509_NAME)> GetCAIssuerList() {
+        bssl::UniquePtr<STACK_OF(X509_NAME)> ret(sk_X509_NAME_new_null());
+        for (auto& issuer : kCAIssuers) {
+            bssl::UniquePtr<X509_NAME> name(X509_NAME_new());
+            for (auto& attr : issuer) {
+                CHECK(X509_NAME_add_entry_by_NID(name.get(), attr.nid, MBSTRING_ASC,
+                                                 attr.val.data(), attr.val.size(), -1, 0));
+            }
+
+            CHECK(bssl::PushToStack(ret.get(), std::move(name)));
+        }
+
+        return ret;
+    }
+
+    void StartClientHandshakeAsync(TlsError expected) {
+        client_thread_ = std::thread([=]() { EXPECT_EQ(client_->DoHandshake(), expected); });
+    }
+
+    void WaitForClientConnection() {
+        if (client_thread_.joinable()) {
+            client_thread_.join();
+        }
+    }
+
+    unique_fd server_fd_;
+    unique_fd client_fd_;
+    const std::vector<uint8_t> msg_{0xff, 0xab, 0x32, 0xf6, 0x12, 0x56};
+    std::unique_ptr<TlsConnection> server_;
+    std::unique_ptr<TlsConnection> client_;
+    std::thread client_thread_;
+};
+
+TEST_F(AdbWifiTlsConnectionTest, InvalidCreationParams) {
+    // Verify that passing empty certificate/private key results in a crash.
+    ASSERT_DEATH(
+            {
+                server_ = TlsConnection::Create(TlsConnection::Role::Server, "",
+                                                kTestRsa2048ServerPrivKey, server_fd_);
+            },
+            "");
+    ASSERT_DEATH(
+            {
+                server_ = TlsConnection::Create(TlsConnection::Role::Server, kTestRsa2048ServerCert,
+                                                "", server_fd_);
+            },
+            "");
+    ASSERT_DEATH(
+            {
+                client_ = TlsConnection::Create(TlsConnection::Role::Client, "",
+                                                kTestRsa2048ClientPrivKey, client_fd_);
+            },
+            "");
+    ASSERT_DEATH(
+            {
+                client_ = TlsConnection::Create(TlsConnection::Role::Client, kTestRsa2048ClientCert,
+                                                "", client_fd_);
+            },
+            "");
+}
+
+TEST_F(AdbWifiTlsConnectionTest, NoCertificateVerification) {
+    // Allow any certificate
+    server_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
+    client_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
+    StartClientHandshakeAsync(TlsError::Success);
+
+    // Handshake should succeed
+    ASSERT_EQ(server_->DoHandshake(), TlsError::Success);
+    WaitForClientConnection();
+
+    // Test client/server read and writes
+    client_thread_ = std::thread([&]() {
+        EXPECT_TRUE(client_->WriteFully(
+                std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+        // Try with overloaded ReadFully
+        std::vector<uint8_t> buf(msg_.size());
+        ASSERT_TRUE(client_->ReadFully(buf.data(), msg_.size()));
+        EXPECT_EQ(buf, msg_);
+    });
+
+    auto data = server_->ReadFully(msg_.size());
+    EXPECT_EQ(data, msg_);
+    EXPECT_TRUE(server_->WriteFully(
+            std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+
+    WaitForClientConnection();
+}
+
+TEST_F(AdbWifiTlsConnectionTest, NoTrustedCertificates) {
+    StartClientHandshakeAsync(TlsError::CertificateRejected);
+
+    // Handshake should not succeed
+    ASSERT_EQ(server_->DoHandshake(), TlsError::PeerRejectedCertificate);
+    WaitForClientConnection();
+
+    // All writes and reads should fail
+    client_thread_ = std::thread([&]() {
+        // Client write, server read should fail
+        EXPECT_FALSE(client_->WriteFully(
+                std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+        auto data = client_->ReadFully(msg_.size());
+        EXPECT_EQ(data.size(), 0);
+    });
+
+    auto data = server_->ReadFully(msg_.size());
+    EXPECT_EQ(data.size(), 0);
+    EXPECT_FALSE(server_->WriteFully(
+            std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+
+    WaitForClientConnection();
+}
+
+TEST_F(AdbWifiTlsConnectionTest, AddTrustedCertificates) {
+    // Add peer certificates
+    EXPECT_TRUE(client_->AddTrustedCertificate(kTestRsa2048ServerCert));
+    EXPECT_TRUE(server_->AddTrustedCertificate(kTestRsa2048ClientCert));
+
+    StartClientHandshakeAsync(TlsError::Success);
+
+    // Handshake should succeed
+    ASSERT_EQ(server_->DoHandshake(), TlsError::Success);
+    WaitForClientConnection();
+
+    // All read writes should succeed
+    client_thread_ = std::thread([&]() {
+        EXPECT_TRUE(client_->WriteFully(
+                std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+        auto data = client_->ReadFully(msg_.size());
+        EXPECT_EQ(data, msg_);
+    });
+
+    auto data = server_->ReadFully(msg_.size());
+    EXPECT_EQ(data, msg_);
+    EXPECT_TRUE(server_->WriteFully(
+            std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+
+    WaitForClientConnection();
+}
+
+TEST_F(AdbWifiTlsConnectionTest, AddTrustedCertificates_ClientWrongCert) {
+    // Server trusts a certificate, client has the wrong certificate
+    EXPECT_TRUE(server_->AddTrustedCertificate(kTestRsa2048UnknownCert));
+    // Client accepts any certificate
+    client_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
+
+    // Without enabling EnableClientPostHandshakeCheck(), DoHandshake() will
+    // succeed, because in TLS 1.3, the client doesn't get notified if the
+    // server rejected the certificate until a read operation is called.
+    StartClientHandshakeAsync(TlsError::Success);
+
+    // Handshake should fail for server, succeed for client
+    ASSERT_EQ(server_->DoHandshake(), TlsError::CertificateRejected);
+    WaitForClientConnection();
+
+    // Client writes will succeed, everything else will fail.
+    client_thread_ = std::thread([&]() {
+        EXPECT_TRUE(client_->WriteFully(
+                std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+        auto data = client_->ReadFully(msg_.size());
+        EXPECT_EQ(data.size(), 0);
+    });
+
+    auto data = server_->ReadFully(msg_.size());
+    EXPECT_EQ(data.size(), 0);
+    EXPECT_FALSE(server_->WriteFully(
+            std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+
+    WaitForClientConnection();
+}
+
+TEST_F(AdbWifiTlsConnectionTest, ExportKeyingMaterial) {
+    // Allow any certificate
+    server_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
+    client_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
+
+    // Add peer certificates
+    EXPECT_TRUE(client_->AddTrustedCertificate(kTestRsa2048ServerCert));
+    EXPECT_TRUE(server_->AddTrustedCertificate(kTestRsa2048ClientCert));
+
+    StartClientHandshakeAsync(TlsError::Success);
+
+    // Handshake should succeed
+    ASSERT_EQ(server_->DoHandshake(), TlsError::Success);
+    WaitForClientConnection();
+
+    // Verify the client and server's exported key material match.
+    const size_t key_size = 64;
+    auto client_key_material = client_->ExportKeyingMaterial(key_size);
+    ASSERT_FALSE(client_key_material.empty());
+    auto server_key_material = server_->ExportKeyingMaterial(key_size);
+    ASSERT_TRUE(!server_key_material.empty());
+    ASSERT_EQ(client_key_material.size(), key_size);
+    ASSERT_EQ(client_key_material, server_key_material);
+}
+
+TEST_F(AdbWifiTlsConnectionTest, SetCertVerifyCallback_ClientAcceptsServerRejects) {
+    // Client accepts all
+    client_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
+    // Server rejects all
+    server_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 0; });
+    // Client handshake should succeed, because in TLS 1.3, client does not
+    // realize that the peer rejected the certificate until after a read
+    // operation.
+    StartClientHandshakeAsync(TlsError::Success);
+
+    // Server handshake should fail
+    ASSERT_EQ(server_->DoHandshake(), TlsError::CertificateRejected);
+    WaitForClientConnection();
+}
+
+TEST_F(AdbWifiTlsConnectionTest, SetCertVerifyCallback_ClientAcceptsServerRejects_PostHSCheck) {
+    // Client accepts all
+    client_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
+    // Client should now get a failure in the handshake
+    client_->EnableClientPostHandshakeCheck(true);
+    // Server rejects all
+    server_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 0; });
+
+    // Client handshake should fail because server rejects everything
+    StartClientHandshakeAsync(TlsError::PeerRejectedCertificate);
+
+    // Server handshake should fail
+    ASSERT_EQ(server_->DoHandshake(), TlsError::CertificateRejected);
+    WaitForClientConnection();
+}
+
+TEST_F(AdbWifiTlsConnectionTest, SetCertVerifyCallback_ClientRejectsServerAccepts) {
+    // Client rejects all
+    client_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 0; });
+    // Server accepts all
+    server_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
+    // Client handshake should fail
+    StartClientHandshakeAsync(TlsError::CertificateRejected);
+
+    // Server handshake should fail
+    ASSERT_EQ(server_->DoHandshake(), TlsError::PeerRejectedCertificate);
+    WaitForClientConnection();
+}
+
+TEST_F(AdbWifiTlsConnectionTest, SetCertVerifyCallback_ClientRejectsServerAccepts_PostHSCheck) {
+    // Client rejects all
+    client_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 0; });
+    // This shouldn't affect the error types returned in the
+    // #SetCertVerifyCallback_ClientRejectsServerAccepts test, since
+    // the failure is still within the TLS 1.3 handshake.
+    client_->EnableClientPostHandshakeCheck(true);
+    // Server accepts all
+    server_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
+
+    // Client handshake should fail
+    StartClientHandshakeAsync(TlsError::CertificateRejected);
+
+    // Server handshake should fail
+    ASSERT_EQ(server_->DoHandshake(), TlsError::PeerRejectedCertificate);
+    WaitForClientConnection();
+}
+
+TEST_F(AdbWifiTlsConnectionTest, EnableClientPostHandshakeCheck_ClientWrongCert) {
+    client_->AddTrustedCertificate(kTestRsa2048ServerCert);
+    // client's DoHandshake() will fail if the server rejected the certificate
+    client_->EnableClientPostHandshakeCheck(true);
+
+    // Add peer certificates
+    EXPECT_TRUE(server_->AddTrustedCertificate(kTestRsa2048UnknownCert));
+
+    // Handshake should fail for client
+    StartClientHandshakeAsync(TlsError::PeerRejectedCertificate);
+
+    // Handshake should fail for server
+    ASSERT_EQ(server_->DoHandshake(), TlsError::CertificateRejected);
+    WaitForClientConnection();
+
+    // All read writes should fail
+    client_thread_ = std::thread([&]() {
+        EXPECT_FALSE(client_->WriteFully(
+                std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+        auto data = client_->ReadFully(msg_.size());
+        EXPECT_EQ(data.size(), 0);
+    });
+
+    auto data = server_->ReadFully(msg_.size());
+    EXPECT_EQ(data.size(), 0);
+    EXPECT_FALSE(server_->WriteFully(
+            std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+
+    WaitForClientConnection();
+}
+
+TEST_F(AdbWifiTlsConnectionTest, SetClientCAList_Empty) {
+    // Setting an empty CA list should not crash
+    server_->SetClientCAList(nullptr);
+    ASSERT_DEATH(
+            {
+                // Client cannot use this API
+                client_->SetClientCAList(nullptr);
+            },
+            "");
+}
+
+TEST_F(AdbWifiTlsConnectionTest, SetClientCAList_Smoke) {
+    auto bsslIssuerList = GetCAIssuerList();
+    server_->SetClientCAList(bsslIssuerList.get());
+    client_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
+
+    client_thread_ = std::thread([&]() {
+        client_->SetCertificateCallback([&](SSL* ssl) -> int {
+            const STACK_OF(X509_NAME)* received = SSL_get_client_CA_list(ssl);
+            EXPECT_NE(received, nullptr);
+            const size_t num_names = sk_X509_NAME_num(received);
+            EXPECT_EQ(kCAIssuers.size(), num_names);
+
+            // Client initially registered with the wrong key. Let's change it
+            // here to verify this callback actually changes the client
+            // certificate to the right one.
+            EXPECT_TRUE(TlsConnection::SetCertAndKey(ssl, kTestRsa2048UnknownCert,
+                                                     kTestRsa2048UnknownPrivKey));
+
+            const size_t buf_size = 256;
+            uint8_t buf[buf_size];
+            size_t idx = 0;
+            for (auto& issuer : kCAIssuers) {
+                auto* name = sk_X509_NAME_value(received, idx++);
+                for (auto& attr : issuer) {
+                    EXPECT_EQ(X509_NAME_get_text_by_NID(name, attr.nid,
+                                                        reinterpret_cast<char*>(buf), buf_size),
+                              attr.val.size());
+                    std::vector<uint8_t> out(buf, buf + attr.val.size());
+                    EXPECT_EQ(out, attr.val);
+                }
+            }
+
+            return 1;
+        });
+        // Client handshake should succeed
+        ASSERT_EQ(client_->DoHandshake(), TlsError::Success);
+    });
+
+    EXPECT_TRUE(server_->AddTrustedCertificate(kTestRsa2048UnknownCert));
+    // Server handshake should succeed
+    ASSERT_EQ(server_->DoHandshake(), TlsError::Success);
+    client_thread_.join();
+}
+
+TEST_F(AdbWifiTlsConnectionTest, SetClientCAList_AdbCAList) {
+    bssl::UniquePtr<STACK_OF(X509_NAME)> ca_list(sk_X509_NAME_new_null());
+    std::string keyhash = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+    auto issuer = CreateCAIssuerFromEncodedKey(keyhash);
+    ASSERT_TRUE(bssl::PushToStack(ca_list.get(), std::move(issuer)));
+    server_->SetClientCAList(ca_list.get());
+    client_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
+
+    client_thread_ = std::thread([&]() {
+        client_->SetCertificateCallback([&](SSL* ssl) -> int {
+            // Client initially registered with a certificate that is not trusted by
+            // the server. Let's test that we can change the certificate to the
+            // trusted one here.
+            const STACK_OF(X509_NAME)* received = SSL_get_client_CA_list(ssl);
+            EXPECT_NE(received, nullptr);
+            const size_t num_names = sk_X509_NAME_num(received);
+            EXPECT_EQ(1, num_names);
+
+            auto* name = sk_X509_NAME_value(received, 0);
+            EXPECT_NE(name, nullptr);
+            auto enc_key = ParseEncodedKeyFromCAIssuer(name);
+            EXPECT_EQ(keyhash, enc_key);
+
+            return 1;
+        });
+        // Client handshake should succeed
+        ASSERT_EQ(client_->DoHandshake(), TlsError::Success);
+    });
+
+    server_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
+    // Server handshake should succeed
+    ASSERT_EQ(server_->DoHandshake(), TlsError::Success);
+    client_thread_.join();
+}
+}  // namespace tls
+}  // namespace adb
diff --git a/adb/tls/tls_connection.cpp b/adb/tls/tls_connection.cpp
new file mode 100644
index 0000000..853cdac
--- /dev/null
+++ b/adb/tls/tls_connection.cpp
@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "adb/tls/tls_connection.h"
+
+#include <algorithm>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+
+using android::base::borrowed_fd;
+
+namespace adb {
+namespace tls {
+
+namespace {
+
+static constexpr char kExportedKeyLabel[] = "adb-label";
+
+class TlsConnectionImpl : public TlsConnection {
+  public:
+    explicit TlsConnectionImpl(Role role, std::string_view cert, std::string_view priv_key,
+                               borrowed_fd fd);
+    ~TlsConnectionImpl() override;
+
+    bool AddTrustedCertificate(std::string_view cert) override;
+    void SetCertVerifyCallback(CertVerifyCb cb) override;
+    void SetCertificateCallback(SetCertCb cb) override;
+    void SetClientCAList(STACK_OF(X509_NAME) * ca_list) override;
+    std::vector<uint8_t> ExportKeyingMaterial(size_t length) override;
+    void EnableClientPostHandshakeCheck(bool enable) override;
+    TlsError DoHandshake() override;
+    std::vector<uint8_t> ReadFully(size_t size) override;
+    bool ReadFully(void* buf, size_t size) override;
+    bool WriteFully(std::string_view data) override;
+
+    static bssl::UniquePtr<EVP_PKEY> EvpPkeyFromPEM(std::string_view pem);
+    static bssl::UniquePtr<CRYPTO_BUFFER> BufferFromPEM(std::string_view pem);
+
+  private:
+    static int SSLSetCertVerifyCb(X509_STORE_CTX* ctx, void* opaque);
+    static int SSLSetCertCb(SSL* ssl, void* opaque);
+
+    static bssl::UniquePtr<X509> X509FromBuffer(bssl::UniquePtr<CRYPTO_BUFFER> buffer);
+    static const char* SSLErrorString();
+    void Invalidate();
+    TlsError GetFailureReason(int err);
+    const char* RoleToString() { return role_ == Role::Server ? kServerRoleStr : kClientRoleStr; }
+
+    Role role_;
+    bssl::UniquePtr<EVP_PKEY> priv_key_;
+    bssl::UniquePtr<CRYPTO_BUFFER> cert_;
+
+    bssl::UniquePtr<STACK_OF(X509_NAME)> ca_list_;
+    bssl::UniquePtr<SSL_CTX> ssl_ctx_;
+    bssl::UniquePtr<SSL> ssl_;
+    std::vector<bssl::UniquePtr<X509>> known_certificates_;
+    bool client_verify_post_handshake_ = false;
+
+    CertVerifyCb cert_verify_cb_;
+    SetCertCb set_cert_cb_;
+    borrowed_fd fd_;
+    static constexpr char kClientRoleStr[] = "[client]: ";
+    static constexpr char kServerRoleStr[] = "[server]: ";
+};  // TlsConnectionImpl
+
+TlsConnectionImpl::TlsConnectionImpl(Role role, std::string_view cert, std::string_view priv_key,
+                                     borrowed_fd fd)
+    : role_(role), fd_(fd) {
+    CHECK(!cert.empty() && !priv_key.empty());
+    LOG(INFO) << RoleToString() << "Initializing adbwifi TlsConnection";
+    cert_ = BufferFromPEM(cert);
+    CHECK(cert_);
+    priv_key_ = EvpPkeyFromPEM(priv_key);
+    CHECK(priv_key_);
+}
+
+TlsConnectionImpl::~TlsConnectionImpl() {
+    // shutdown the SSL connection
+    if (ssl_ != nullptr) {
+        SSL_shutdown(ssl_.get());
+    }
+}
+
+// static
+const char* TlsConnectionImpl::SSLErrorString() {
+    auto sslerr = ERR_peek_last_error();
+    return ERR_reason_error_string(sslerr);
+}
+
+// static
+bssl::UniquePtr<EVP_PKEY> TlsConnectionImpl::EvpPkeyFromPEM(std::string_view pem) {
+    bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(pem.data(), pem.size()));
+    return bssl::UniquePtr<EVP_PKEY>(PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr));
+}
+
+// static
+bssl::UniquePtr<CRYPTO_BUFFER> TlsConnectionImpl::BufferFromPEM(std::string_view pem) {
+    bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(pem.data(), pem.size()));
+    char* name = nullptr;
+    char* header = nullptr;
+    uint8_t* data = nullptr;
+    long data_len = 0;
+
+    if (!PEM_read_bio(bio.get(), &name, &header, &data, &data_len)) {
+        LOG(ERROR) << "Failed to read certificate";
+        return nullptr;
+    }
+    OPENSSL_free(name);
+    OPENSSL_free(header);
+
+    auto ret = bssl::UniquePtr<CRYPTO_BUFFER>(CRYPTO_BUFFER_new(data, data_len, nullptr));
+    OPENSSL_free(data);
+    return ret;
+}
+
+// static
+bssl::UniquePtr<X509> TlsConnectionImpl::X509FromBuffer(bssl::UniquePtr<CRYPTO_BUFFER> buffer) {
+    if (!buffer) {
+        return nullptr;
+    }
+    return bssl::UniquePtr<X509>(X509_parse_from_buffer(buffer.get()));
+}
+
+// static
+int TlsConnectionImpl::SSLSetCertVerifyCb(X509_STORE_CTX* ctx, void* opaque) {
+    auto* p = reinterpret_cast<TlsConnectionImpl*>(opaque);
+    return p->cert_verify_cb_(ctx);
+}
+
+// static
+int TlsConnectionImpl::SSLSetCertCb(SSL* ssl, void* opaque) {
+    auto* p = reinterpret_cast<TlsConnectionImpl*>(opaque);
+    return p->set_cert_cb_(ssl);
+}
+
+bool TlsConnectionImpl::AddTrustedCertificate(std::string_view cert) {
+    // Create X509 buffer from the certificate string
+    auto buf = X509FromBuffer(BufferFromPEM(cert));
+    if (buf == nullptr) {
+        LOG(ERROR) << RoleToString() << "Failed to create a X509 buffer for the certificate.";
+        return false;
+    }
+    known_certificates_.push_back(std::move(buf));
+    return true;
+}
+
+void TlsConnectionImpl::SetCertVerifyCallback(CertVerifyCb cb) {
+    cert_verify_cb_ = cb;
+}
+
+void TlsConnectionImpl::SetCertificateCallback(SetCertCb cb) {
+    set_cert_cb_ = cb;
+}
+
+void TlsConnectionImpl::SetClientCAList(STACK_OF(X509_NAME) * ca_list) {
+    CHECK(role_ == Role::Server);
+    ca_list_.reset(ca_list != nullptr ? SSL_dup_CA_list(ca_list) : nullptr);
+}
+
+std::vector<uint8_t> TlsConnectionImpl::ExportKeyingMaterial(size_t length) {
+    if (ssl_.get() == nullptr) {
+        return {};
+    }
+
+    std::vector<uint8_t> out(length);
+    if (SSL_export_keying_material(ssl_.get(), out.data(), out.size(), kExportedKeyLabel,
+                                   sizeof(kExportedKeyLabel), nullptr, 0, false) == 0) {
+        return {};
+    }
+    return out;
+}
+
+void TlsConnectionImpl::EnableClientPostHandshakeCheck(bool enable) {
+    client_verify_post_handshake_ = enable;
+}
+
+TlsConnection::TlsError TlsConnectionImpl::GetFailureReason(int err) {
+    switch (ERR_GET_REASON(err)) {
+        case SSL_R_SSLV3_ALERT_BAD_CERTIFICATE:
+        case SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE:
+        case SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED:
+        case SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED:
+        case SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN:
+        case SSL_R_TLSV1_ALERT_ACCESS_DENIED:
+        case SSL_R_TLSV1_ALERT_UNKNOWN_CA:
+        case SSL_R_TLSV1_CERTIFICATE_REQUIRED:
+            return TlsError::PeerRejectedCertificate;
+        case SSL_R_CERTIFICATE_VERIFY_FAILED:
+            return TlsError::CertificateRejected;
+        default:
+            return TlsError::UnknownFailure;
+    }
+}
+
+TlsConnection::TlsError TlsConnectionImpl::DoHandshake() {
+    LOG(INFO) << RoleToString() << "Starting adbwifi tls handshake";
+    ssl_ctx_.reset(SSL_CTX_new(TLS_method()));
+    // TODO: Remove set_max_proto_version() once external/boringssl is updated
+    // past
+    // https://boringssl.googlesource.com/boringssl/+/58d56f4c59969a23e5f52014e2651c76fea2f877
+    if (ssl_ctx_.get() == nullptr ||
+        !SSL_CTX_set_min_proto_version(ssl_ctx_.get(), TLS1_3_VERSION) ||
+        !SSL_CTX_set_max_proto_version(ssl_ctx_.get(), TLS1_3_VERSION)) {
+        LOG(ERROR) << RoleToString() << "Failed to create SSL context";
+        return TlsError::UnknownFailure;
+    }
+
+    // Register user-supplied known certificates
+    for (auto const& cert : known_certificates_) {
+        if (X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx_.get()), cert.get()) == 0) {
+            LOG(ERROR) << RoleToString() << "Unable to add certificates into the X509_STORE";
+            return TlsError::UnknownFailure;
+        }
+    }
+
+    // Custom certificate verification
+    if (cert_verify_cb_) {
+        SSL_CTX_set_cert_verify_callback(ssl_ctx_.get(), SSLSetCertVerifyCb, this);
+    }
+
+    // set select certificate callback, if any.
+    if (set_cert_cb_) {
+        SSL_CTX_set_cert_cb(ssl_ctx_.get(), SSLSetCertCb, this);
+    }
+
+    // Server-allowed client CA list
+    if (ca_list_ != nullptr) {
+        bssl::UniquePtr<STACK_OF(X509_NAME)> names(SSL_dup_CA_list(ca_list_.get()));
+        SSL_CTX_set_client_CA_list(ssl_ctx_.get(), names.release());
+    }
+
+    // Register our certificate and private key.
+    std::vector<CRYPTO_BUFFER*> cert_chain = {
+            cert_.get(),
+    };
+    if (!SSL_CTX_set_chain_and_key(ssl_ctx_.get(), cert_chain.data(), cert_chain.size(),
+                                   priv_key_.get(), nullptr)) {
+        LOG(ERROR) << RoleToString()
+                   << "Unable to register the certificate chain file and private key ["
+                   << SSLErrorString() << "]";
+        Invalidate();
+        return TlsError::UnknownFailure;
+    }
+
+    SSL_CTX_set_verify(ssl_ctx_.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
+
+    // Okay! Let's try to do the handshake!
+    ssl_.reset(SSL_new(ssl_ctx_.get()));
+    if (!SSL_set_fd(ssl_.get(), fd_.get())) {
+        LOG(ERROR) << RoleToString() << "SSL_set_fd failed. [" << SSLErrorString() << "]";
+        return TlsError::UnknownFailure;
+    }
+
+    switch (role_) {
+        case Role::Server:
+            SSL_set_accept_state(ssl_.get());
+            break;
+        case Role::Client:
+            SSL_set_connect_state(ssl_.get());
+            break;
+    }
+    if (SSL_do_handshake(ssl_.get()) != 1) {
+        LOG(ERROR) << RoleToString() << "Handshake failed in SSL_accept/SSL_connect ["
+                   << SSLErrorString() << "]";
+        auto sslerr = ERR_get_error();
+        Invalidate();
+        return GetFailureReason(sslerr);
+    }
+
+    if (client_verify_post_handshake_ && role_ == Role::Client) {
+        uint8_t check;
+        // Try to peek one byte for any failures. This assumes on success that
+        // the server actually sends something.
+        if (SSL_peek(ssl_.get(), &check, 1) <= 0) {
+            LOG(ERROR) << RoleToString() << "Post-handshake SSL_peek failed [" << SSLErrorString()
+                       << "]";
+            auto sslerr = ERR_get_error();
+            Invalidate();
+            return GetFailureReason(sslerr);
+        }
+    }
+
+    LOG(INFO) << RoleToString() << "Handshake succeeded.";
+    return TlsError::Success;
+}
+
+void TlsConnectionImpl::Invalidate() {
+    ssl_.reset();
+    ssl_ctx_.reset();
+}
+
+std::vector<uint8_t> TlsConnectionImpl::ReadFully(size_t size) {
+    std::vector<uint8_t> buf(size);
+    if (!ReadFully(buf.data(), buf.size())) {
+        return {};
+    }
+
+    return buf;
+}
+
+bool TlsConnectionImpl::ReadFully(void* buf, size_t size) {
+    CHECK_GT(size, 0U);
+    if (!ssl_) {
+        LOG(ERROR) << RoleToString() << "Tried to read on a null SSL connection";
+        return false;
+    }
+
+    size_t offset = 0;
+    uint8_t* p8 = reinterpret_cast<uint8_t*>(buf);
+    while (size > 0) {
+        int bytes_read =
+                SSL_read(ssl_.get(), p8 + offset, std::min(static_cast<size_t>(INT_MAX), size));
+        if (bytes_read <= 0) {
+            LOG(ERROR) << RoleToString() << "SSL_read failed [" << SSLErrorString() << "]";
+            return false;
+        }
+        size -= bytes_read;
+        offset += bytes_read;
+    }
+    return true;
+}
+
+bool TlsConnectionImpl::WriteFully(std::string_view data) {
+    CHECK(!data.empty());
+    if (!ssl_) {
+        LOG(ERROR) << RoleToString() << "Tried to read on a null SSL connection";
+        return false;
+    }
+
+    while (!data.empty()) {
+        int bytes_out = SSL_write(ssl_.get(), data.data(),
+                                  std::min(static_cast<size_t>(INT_MAX), data.size()));
+        if (bytes_out <= 0) {
+            LOG(ERROR) << RoleToString() << "SSL_write failed [" << SSLErrorString() << "]";
+            return false;
+        }
+        data = data.substr(bytes_out);
+    }
+    return true;
+}
+}  // namespace
+
+// static
+std::unique_ptr<TlsConnection> TlsConnection::Create(TlsConnection::Role role,
+                                                     std::string_view cert,
+                                                     std::string_view priv_key, borrowed_fd fd) {
+    CHECK(!cert.empty());
+    CHECK(!priv_key.empty());
+
+    return std::make_unique<TlsConnectionImpl>(role, cert, priv_key, fd);
+}
+
+// static
+bool TlsConnection::SetCertAndKey(SSL* ssl, std::string_view cert, std::string_view priv_key) {
+    CHECK(ssl);
+    // Note: declaring these in local scope is okay because
+    // SSL_set_chain_and_key will increase the refcount (bssl::UpRef).
+    auto x509_cert = TlsConnectionImpl::BufferFromPEM(cert);
+    auto evp_pkey = TlsConnectionImpl::EvpPkeyFromPEM(priv_key);
+    if (x509_cert == nullptr || evp_pkey == nullptr) {
+        return false;
+    }
+
+    std::vector<CRYPTO_BUFFER*> cert_chain = {
+            x509_cert.get(),
+    };
+    if (!SSL_set_chain_and_key(ssl, cert_chain.data(), cert_chain.size(), evp_pkey.get(),
+                               nullptr)) {
+        LOG(ERROR) << "SSL_set_chain_and_key failed";
+        return false;
+    }
+
+    return true;
+}
+
+}  // namespace tls
+}  // namespace adb
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 9dd6ec6..8b3461a 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -36,6 +36,9 @@
 #include <set>
 #include <thread>
 
+#include <adb/crypto/rsa_2048_key.h>
+#include <adb/crypto/x509_generator.h>
+#include <adb/tls/tls_connection.h>
 #include <android-base/logging.h>
 #include <android-base/parsenetaddress.h>
 #include <android-base/stringprintf.h>
@@ -52,7 +55,10 @@
 #include "fdevent/fdevent.h"
 #include "sysdeps/chrono.h"
 
+using namespace adb::crypto;
+using namespace adb::tls;
 using android::base::ScopedLockAssertion;
+using TlsError = TlsConnection::TlsError;
 
 static void remove_transport(atransport* transport);
 static void transport_destroy(atransport* transport);
@@ -279,18 +285,7 @@
                    << "): started multiple times";
     }
 
-    read_thread_ = std::thread([this]() {
-        LOG(INFO) << this->transport_name_ << ": read thread spawning";
-        while (true) {
-            auto packet = std::make_unique<apacket>();
-            if (!underlying_->Read(packet.get())) {
-                PLOG(INFO) << this->transport_name_ << ": read failed";
-                break;
-            }
-            read_callback_(this, std::move(packet));
-        }
-        std::call_once(this->error_flag_, [this]() { this->error_callback_(this, "read failed"); });
-    });
+    StartReadThread();
 
     write_thread_ = std::thread([this]() {
         LOG(INFO) << this->transport_name_ << ": write thread spawning";
@@ -319,6 +314,46 @@
     started_ = true;
 }
 
+void BlockingConnectionAdapter::StartReadThread() {
+    read_thread_ = std::thread([this]() {
+        LOG(INFO) << this->transport_name_ << ": read thread spawning";
+        while (true) {
+            auto packet = std::make_unique<apacket>();
+            if (!underlying_->Read(packet.get())) {
+                PLOG(INFO) << this->transport_name_ << ": read failed";
+                break;
+            }
+
+            bool got_stls_cmd = false;
+            if (packet->msg.command == A_STLS) {
+                got_stls_cmd = true;
+            }
+
+            read_callback_(this, std::move(packet));
+
+            // If we received the STLS packet, we are about to perform the TLS
+            // handshake. So this read thread must stop and resume after the
+            // handshake completes otherwise this will interfere in the process.
+            if (got_stls_cmd) {
+                LOG(INFO) << this->transport_name_
+                          << ": Received STLS packet. Stopping read thread.";
+                return;
+            }
+        }
+        std::call_once(this->error_flag_, [this]() { this->error_callback_(this, "read failed"); });
+    });
+}
+
+bool BlockingConnectionAdapter::DoTlsHandshake(RSA* key, std::string* auth_key) {
+    std::lock_guard<std::mutex> lock(mutex_);
+    if (read_thread_.joinable()) {
+        read_thread_.join();
+    }
+    bool success = this->underlying_->DoTlsHandshake(key, auth_key);
+    StartReadThread();
+    return success;
+}
+
 void BlockingConnectionAdapter::Reset() {
     {
         std::lock_guard<std::mutex> lock(mutex_);
@@ -388,8 +423,36 @@
     return true;
 }
 
+FdConnection::FdConnection(unique_fd fd) : fd_(std::move(fd)) {}
+
+FdConnection::~FdConnection() {}
+
+bool FdConnection::DispatchRead(void* buf, size_t len) {
+    if (tls_ != nullptr) {
+        // The TlsConnection doesn't allow 0 byte reads
+        if (len == 0) {
+            return true;
+        }
+        return tls_->ReadFully(buf, len);
+    }
+
+    return ReadFdExactly(fd_.get(), buf, len);
+}
+
+bool FdConnection::DispatchWrite(void* buf, size_t len) {
+    if (tls_ != nullptr) {
+        // The TlsConnection doesn't allow 0 byte writes
+        if (len == 0) {
+            return true;
+        }
+        return tls_->WriteFully(std::string_view(reinterpret_cast<const char*>(buf), len));
+    }
+
+    return WriteFdExactly(fd_.get(), buf, len);
+}
+
 bool FdConnection::Read(apacket* packet) {
-    if (!ReadFdExactly(fd_.get(), &packet->msg, sizeof(amessage))) {
+    if (!DispatchRead(&packet->msg, sizeof(amessage))) {
         D("remote local: read terminated (message)");
         return false;
     }
@@ -401,7 +464,7 @@
 
     packet->payload.resize(packet->msg.data_length);
 
-    if (!ReadFdExactly(fd_.get(), &packet->payload[0], packet->payload.size())) {
+    if (!DispatchRead(&packet->payload[0], packet->payload.size())) {
         D("remote local: terminated (data)");
         return false;
     }
@@ -410,13 +473,13 @@
 }
 
 bool FdConnection::Write(apacket* packet) {
-    if (!WriteFdExactly(fd_.get(), &packet->msg, sizeof(packet->msg))) {
+    if (!DispatchWrite(&packet->msg, sizeof(packet->msg))) {
         D("remote local: write terminated");
         return false;
     }
 
     if (packet->msg.data_length) {
-        if (!WriteFdExactly(fd_.get(), &packet->payload[0], packet->msg.data_length)) {
+        if (!DispatchWrite(&packet->payload[0], packet->msg.data_length)) {
             D("remote local: write terminated");
             return false;
         }
@@ -425,6 +488,51 @@
     return true;
 }
 
+bool FdConnection::DoTlsHandshake(RSA* key, std::string* auth_key) {
+    bssl::UniquePtr<EVP_PKEY> evp_pkey(EVP_PKEY_new());
+    if (!EVP_PKEY_set1_RSA(evp_pkey.get(), key)) {
+        LOG(ERROR) << "EVP_PKEY_set1_RSA failed";
+        return false;
+    }
+    auto x509 = GenerateX509Certificate(evp_pkey.get());
+    auto x509_str = X509ToPEMString(x509.get());
+    auto evp_str = Key::ToPEMString(evp_pkey.get());
+#if ADB_HOST
+    tls_ = TlsConnection::Create(TlsConnection::Role::Client,
+#else
+    tls_ = TlsConnection::Create(TlsConnection::Role::Server,
+#endif
+                                 x509_str, evp_str, fd_);
+    CHECK(tls_);
+#if ADB_HOST
+    // TLS 1.3 gives the client no message if the server rejected the
+    // certificate. This will enable a check in the tls connection to check
+    // whether the client certificate got rejected. Note that this assumes
+    // that, on handshake success, the server speaks first.
+    tls_->EnableClientPostHandshakeCheck(true);
+    // Add callback to set the certificate when server issues the
+    // CertificateRequest.
+    tls_->SetCertificateCallback(adb_tls_set_certificate);
+    // Allow any server certificate
+    tls_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
+#else
+    // Add callback to check certificate against a list of known public keys
+    tls_->SetCertVerifyCallback(
+            [auth_key](X509_STORE_CTX* ctx) { return adbd_tls_verify_cert(ctx, auth_key); });
+    // Add the list of allowed client CA issuers
+    auto ca_list = adbd_tls_client_ca_list();
+    tls_->SetClientCAList(ca_list.get());
+#endif
+
+    auto err = tls_->DoHandshake();
+    if (err == TlsError::Success) {
+        return true;
+    }
+
+    tls_.reset();
+    return false;
+}
+
 void FdConnection::Close() {
     adb_shutdown(fd_.get());
     fd_.reset();
@@ -750,6 +858,26 @@
     }
 }
 
+void kick_all_tcp_tls_transports() {
+    std::lock_guard<std::recursive_mutex> lock(transport_lock);
+    for (auto t : transport_list) {
+        if (t->IsTcpDevice() && t->use_tls) {
+            t->Kick();
+        }
+    }
+}
+
+#if !ADB_HOST
+void kick_all_transports_by_auth_key(std::string_view auth_key) {
+    std::lock_guard<std::recursive_mutex> lock(transport_lock);
+    for (auto t : transport_list) {
+        if (auth_key == t->auth_key) {
+            t->Kick();
+        }
+    }
+}
+#endif
+
 /* the fdevent select pump is single threaded */
 void register_transport(atransport* transport) {
     tmsg m;
@@ -1026,6 +1154,10 @@
     return protocol_version;
 }
 
+int atransport::get_tls_version() const {
+    return tls_version;
+}
+
 size_t atransport::get_max_payload() const {
     return max_payload;
 }
@@ -1221,8 +1353,9 @@
 #endif  // ADB_HOST
 
 bool register_socket_transport(unique_fd s, std::string serial, int port, int local,
-                               atransport::ReconnectCallback reconnect, int* error) {
+                               atransport::ReconnectCallback reconnect, bool use_tls, int* error) {
     atransport* t = new atransport(std::move(reconnect), kCsOffline);
+    t->use_tls = use_tls;
 
     D("transport: %s init'ing for socket %d, on port %d", serial.c_str(), s.get(), port);
     if (init_socket_transport(t, std::move(s), port, local) < 0) {
@@ -1360,6 +1493,15 @@
 }
 
 #if ADB_HOST
+std::shared_ptr<RSA> atransport::Key() {
+    if (keys_.empty()) {
+        return nullptr;
+    }
+
+    std::shared_ptr<RSA> result = keys_[0];
+    return result;
+}
+
 std::shared_ptr<RSA> atransport::NextKey() {
     if (keys_.empty()) {
         LOG(INFO) << "fetching keys for transport " << this->serial_name();
@@ -1367,10 +1509,11 @@
 
         // We should have gotten at least one key: the one that's automatically generated.
         CHECK(!keys_.empty());
+    } else {
+        keys_.pop_front();
     }
 
     std::shared_ptr<RSA> result = keys_[0];
-    keys_.pop_front();
     return result;
 }
 
diff --git a/adb/transport.h b/adb/transport.h
index 5a750ee..8a0f62a 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -43,6 +43,14 @@
 
 typedef std::unordered_set<std::string> FeatureSet;
 
+namespace adb {
+namespace tls {
+
+class TlsConnection;
+
+}  // namespace tls
+}  // namespace adb
+
 const FeatureSet& supported_features();
 
 // Encodes and decodes FeatureSet objects into human-readable strings.
@@ -104,6 +112,8 @@
     virtual void Start() = 0;
     virtual void Stop() = 0;
 
+    virtual bool DoTlsHandshake(RSA* key, std::string* auth_key = nullptr) = 0;
+
     // Stop, and reset the device if it's a USB connection.
     virtual void Reset();
 
@@ -128,6 +138,8 @@
     virtual bool Read(apacket* packet) = 0;
     virtual bool Write(apacket* packet) = 0;
 
+    virtual bool DoTlsHandshake(RSA* key, std::string* auth_key = nullptr) = 0;
+
     // Terminate a connection.
     // This method must be thread-safe, and must cause concurrent Reads/Writes to terminate.
     // Formerly known as 'Kick' in atransport.
@@ -146,9 +158,12 @@
 
     virtual void Start() override final;
     virtual void Stop() override final;
+    virtual bool DoTlsHandshake(RSA* key, std::string* auth_key) override final;
 
     virtual void Reset() override final;
 
+  private:
+    void StartReadThread() REQUIRES(mutex_);
     bool started_ GUARDED_BY(mutex_) = false;
     bool stopped_ GUARDED_BY(mutex_) = false;
 
@@ -164,16 +179,22 @@
 };
 
 struct FdConnection : public BlockingConnection {
-    explicit FdConnection(unique_fd fd) : fd_(std::move(fd)) {}
+    explicit FdConnection(unique_fd fd);
+    ~FdConnection();
 
     bool Read(apacket* packet) override final;
     bool Write(apacket* packet) override final;
+    bool DoTlsHandshake(RSA* key, std::string* auth_key) override final;
 
     void Close() override;
     virtual void Reset() override final { Close(); }
 
   private:
+    bool DispatchRead(void* buf, size_t len);
+    bool DispatchWrite(void* buf, size_t len);
+
     unique_fd fd_;
+    std::unique_ptr<adb::tls::TlsConnection> tls_;
 };
 
 struct UsbConnection : public BlockingConnection {
@@ -182,6 +203,7 @@
 
     bool Read(apacket* packet) override final;
     bool Write(apacket* packet) override final;
+    bool DoTlsHandshake(RSA* key, std::string* auth_key) override final;
 
     void Close() override final;
     virtual void Reset() override final;
@@ -279,6 +301,12 @@
     std::string device;
     std::string devpath;
 
+    // If this is set, the transport will initiate the connection with a
+    // START_TLS command, instead of AUTH.
+    bool use_tls = false;
+    int tls_version = A_STLS_VERSION;
+    int get_tls_version() const;
+
 #if !ADB_HOST
     // Used to provide the key to the framework.
     std::string auth_key;
@@ -288,6 +316,8 @@
     bool IsTcpDevice() const { return type == kTransportLocal; }
 
 #if ADB_HOST
+    // The current key being authorized.
+    std::shared_ptr<RSA> Key();
     std::shared_ptr<RSA> NextKey();
     void ResetKeys();
 #endif
@@ -400,6 +430,10 @@
 atransport* find_transport(const char* serial);
 void kick_all_tcp_devices();
 void kick_all_transports();
+void kick_all_tcp_tls_transports();
+#if !ADB_HOST
+void kick_all_transports_by_auth_key(std::string_view auth_key);
+#endif
 
 void register_transport(atransport* transport);
 void register_usb_transport(usb_handle* h, const char* serial,
@@ -410,7 +444,8 @@
 
 /* cause new transports to be init'd and added to the list */
 bool register_socket_transport(unique_fd s, std::string serial, int port, int local,
-                               atransport::ReconnectCallback reconnect, int* error = nullptr);
+                               atransport::ReconnectCallback reconnect, bool use_tls,
+                               int* error = nullptr);
 
 // This should only be used for transports with connection_state == kCsNoPerm.
 void unregister_usb_transport(usb_handle* usb);
diff --git a/adb/transport_fd.cpp b/adb/transport_fd.cpp
index 8d2ad66..b9b4f42 100644
--- a/adb/transport_fd.cpp
+++ b/adb/transport_fd.cpp
@@ -155,6 +155,11 @@
         thread_.join();
     }
 
+    bool DoTlsHandshake(RSA* key, std::string* auth_key) override final {
+        LOG(FATAL) << "Not supported yet";
+        return false;
+    }
+
     void WakeThread() {
         uint64_t buf = 0;
         if (TEMP_FAILURE_RETRY(adb_write(wake_fd_write_.get(), &buf, sizeof(buf))) != sizeof(buf)) {
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index c726186..5ec8e16 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -126,7 +126,8 @@
     };
 
     int error;
-    if (!register_socket_transport(std::move(fd), serial, port, 0, std::move(reconnect), &error)) {
+    if (!register_socket_transport(std::move(fd), serial, port, 0, std::move(reconnect), false,
+                                   &error)) {
         if (error == EALREADY) {
             *response = android::base::StringPrintf("already connected to %s", serial.c_str());
         } else if (error == EPERM) {
@@ -163,8 +164,9 @@
         close_on_exec(fd.get());
         disable_tcp_nagle(fd.get());
         std::string serial = getEmulatorSerialString(console_port);
-        if (register_socket_transport(std::move(fd), std::move(serial), adb_port, 1,
-                                      [](atransport*) { return ReconnectResult::Abort; })) {
+        if (register_socket_transport(
+                    std::move(fd), std::move(serial), adb_port, 1,
+                    [](atransport*) { return ReconnectResult::Abort; }, false)) {
             return 0;
         }
     }
@@ -271,8 +273,9 @@
             std::string serial = android::base::StringPrintf("host-%d", fd.get());
             // We don't care about port value in "register_socket_transport" as it is used
             // only from ADB_HOST. "server_socket_thread" is never called from ADB_HOST.
-            register_socket_transport(std::move(fd), std::move(serial), 0, 1,
-                                      [](atransport*) { return ReconnectResult::Abort; });
+            register_socket_transport(
+                    std::move(fd), std::move(serial), 0, 1,
+                    [](atransport*) { return ReconnectResult::Abort; }, false);
         }
     }
     D("transport: server_socket_thread() exiting");
@@ -365,7 +368,7 @@
     if (local) {
         auto emulator_connection = std::make_unique<EmulatorConnection>(std::move(fd), adb_port);
         t->SetConnection(
-            std::make_unique<BlockingConnectionAdapter>(std::move(emulator_connection)));
+                std::make_unique<BlockingConnectionAdapter>(std::move(emulator_connection)));
         std::lock_guard<std::mutex> lock(local_transports_lock);
         atransport* existing_transport = find_emulator_transport_by_adb_port_locked(adb_port);
         if (existing_transport != nullptr) {
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index 3e87522..fb81b37 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -171,6 +171,12 @@
     return true;
 }
 
+bool UsbConnection::DoTlsHandshake(RSA* key, std::string* auth_key) {
+    // TODO: support TLS for usb connections
+    LOG(FATAL) << "Not supported yet.";
+    return false;
+}
+
 void UsbConnection::Reset() {
     usb_reset(handle_);
     usb_kick(handle_);
diff --git a/base/Android.bp b/base/Android.bp
index 8351461..25c74f2 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -33,6 +33,7 @@
 cc_library_headers {
     name: "libbase_headers",
     vendor_available: true,
+    ramdisk_available: true,
     recovery_available: true,
     host_supported: true,
     native_bridge_supported: true,
@@ -56,6 +57,7 @@
         "chrono_utils.cpp",
         "cmsg.cpp",
         "file.cpp",
+        "liblog_symbols.cpp",
         "logging.cpp",
         "mapped_file.cpp",
         "parsebool.cpp",
@@ -68,6 +70,10 @@
         "test_utils.cpp",
     ],
 
+    static: {
+        cflags: ["-DNO_LIBLOG_DLSYM"],
+    },
+
     cppflags: ["-Wexit-time-destructors"],
     shared_libs: ["liblog"],
     target: {
@@ -107,6 +113,7 @@
     name: "libbase",
     defaults: ["libbase_defaults"],
     vendor_available: true,
+    ramdisk_available: true,
     recovery_available: true,
     host_supported: true,
     native_bridge_supported: true,
@@ -121,6 +128,10 @@
     static_libs: ["fmtlib"],
     whole_static_libs: ["fmtlib"],
     export_static_lib_headers: ["fmtlib"],
+    apex_available: [
+        "//apex_available:anyapex",
+        "//apex_available:platform",
+    ],
 }
 
 cc_library_static {
diff --git a/base/expected_test.cpp b/base/expected_test.cpp
index a74bc1d..47e396a 100644
--- a/base/expected_test.cpp
+++ b/base/expected_test.cpp
@@ -408,11 +408,11 @@
 
 TEST(Expected, testTest) {
   exp_int e = 10;
-  EXPECT_TRUE(e);
+  EXPECT_TRUE(e.ok());
   EXPECT_TRUE(e.has_value());
 
   exp_int e2 = unexpected(10);
-  EXPECT_FALSE(e2);
+  EXPECT_FALSE(e2.ok());
   EXPECT_FALSE(e2.has_value());
 }
 
@@ -499,24 +499,6 @@
   EXPECT_TRUE(e4 != e3);
 }
 
-TEST(Expected, testCompareWithSameValue) {
-  exp_int e = 10;
-  int value = 10;
-  EXPECT_TRUE(e == value);
-  EXPECT_TRUE(value == e);
-  EXPECT_FALSE(e != value);
-  EXPECT_FALSE(value != e);
-}
-
-TEST(Expected, testCompareWithDifferentValue) {
-  exp_int e = 10;
-  int value = 20;
-  EXPECT_FALSE(e == value);
-  EXPECT_FALSE(value == e);
-  EXPECT_TRUE(e != value);
-  EXPECT_TRUE(value != e);
-}
-
 TEST(Expected, testCompareWithSameError) {
   exp_int e = unexpected(10);
   exp_int::unexpected_type error = 10;
@@ -589,12 +571,12 @@
     }
   };
 
-  EXPECT_FALSE(divide(10, 0));
+  EXPECT_FALSE(divide(10, 0).ok());
   EXPECT_EQ("divide by zero", divide(10, 0).error().message);
   EXPECT_EQ(-1, divide(10, 0).error().cause);
 
-  EXPECT_TRUE(divide(10, 3));
-  EXPECT_EQ(QR(3, 1), divide(10, 3));
+  EXPECT_TRUE(divide(10, 3).ok());
+  EXPECT_EQ(QR(3, 1), *divide(10, 3));
 }
 
 TEST(Expected, testPair) {
@@ -607,7 +589,7 @@
   };
 
   auto r = test(true);
-  EXPECT_TRUE(r);
+  EXPECT_TRUE(r.ok());
   EXPECT_EQ("yes", r->first);
 }
 
@@ -621,9 +603,9 @@
   };
 
   auto r = test(true);
-  EXPECT_TRUE(r);
+  EXPECT_TRUE(r.ok());
   r = test(false);
-  EXPECT_FALSE(r);
+  EXPECT_FALSE(r.ok());
   EXPECT_EQ(10, r.error());
 }
 
@@ -772,7 +754,7 @@
 
   ConstructorTracker::Reset();
   auto result1 = test("");
-  ASSERT_TRUE(result1);
+  ASSERT_TRUE(result1.ok());
   EXPECT_EQ("literal string", result1->string);
   EXPECT_EQ(1U, ConstructorTracker::constructor_called);
   EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
@@ -782,7 +764,7 @@
 
   ConstructorTracker::Reset();
   auto result2 = test("test2");
-  ASSERT_TRUE(result2);
+  ASSERT_TRUE(result2.ok());
   EXPECT_EQ("test2test22", result2->string);
   EXPECT_EQ(1U, ConstructorTracker::constructor_called);
   EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
@@ -792,7 +774,7 @@
 
   ConstructorTracker::Reset();
   auto result3 = test("test3");
-  ASSERT_TRUE(result3);
+  ASSERT_TRUE(result3.ok());
   EXPECT_EQ("test3 test3", result3->string);
   EXPECT_EQ(1U, ConstructorTracker::constructor_called);
   EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
@@ -804,22 +786,22 @@
 TEST(Expected, testNested) {
   expected<exp_string, std::string> e = "hello";
 
+  EXPECT_TRUE(e.ok());
   EXPECT_TRUE(e.has_value());
   EXPECT_TRUE(e.value().has_value());
-  EXPECT_TRUE(e);
-  EXPECT_TRUE(*e);
+  EXPECT_TRUE(e->ok());
   EXPECT_EQ("hello", e.value().value());
 
   expected<exp_string, std::string> e2 = unexpected("world");
   EXPECT_FALSE(e2.has_value());
-  EXPECT_FALSE(e2);
+  EXPECT_FALSE(e2.ok());
   EXPECT_EQ("world", e2.error());
 
   expected<exp_string, std::string> e3 = exp_string(unexpected("world"));
   EXPECT_TRUE(e3.has_value());
   EXPECT_FALSE(e3.value().has_value());
-  EXPECT_TRUE(e3);
-  EXPECT_FALSE(*e3);
+  EXPECT_TRUE(e3.ok());
+  EXPECT_FALSE(e3->ok());
   EXPECT_EQ("world", e3.value().error());
 }
 
diff --git a/base/include/android-base/errno_restorer.h b/base/include/android-base/errno_restorer.h
new file mode 100644
index 0000000..1c8597c
--- /dev/null
+++ b/base/include/android-base/errno_restorer.h
@@ -0,0 +1,42 @@
+/*
+ * 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 "errno.h"
+
+#include "android-base/macros.h"
+
+namespace android {
+namespace base {
+
+class ErrnoRestorer {
+ public:
+  ErrnoRestorer() : saved_errno_(errno) {}
+
+  ~ErrnoRestorer() { errno = saved_errno_; }
+
+  // Allow this object to be used as part of && operation.
+  operator bool() const { return true; }
+
+ private:
+  const int saved_errno_;
+
+  DISALLOW_COPY_AND_ASSIGN(ErrnoRestorer);
+};
+
+}  // namespace base
+}  // namespace android
diff --git a/base/include/android-base/expected.h b/base/include/android-base/expected.h
index 030ef35e..9603bb1 100644
--- a/base/include/android-base/expected.h
+++ b/base/include/android-base/expected.h
@@ -111,6 +111,7 @@
     !(!std::is_convertible_v<const U&, T> ||
      !std::is_convertible_v<const G&, E>) /* non-explicit */
   )>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr expected(const expected<U, G>& rhs) {
     if (rhs.has_value()) var_ = rhs.value();
     else var_ = unexpected(rhs.error());
@@ -149,6 +150,7 @@
     !(!std::is_convertible_v<const U&, T> ||
      !std::is_convertible_v<const G&, E>) /* non-explicit */
   )>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr expected(expected<U, G>&& rhs) {
     if (rhs.has_value()) var_ = std::move(rhs.value());
     else var_ = unexpected(std::move(rhs.error()));
@@ -180,6 +182,7 @@
                 !std::is_same_v<unexpected<E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
                 std::is_convertible_v<U&&, T> /* non-explicit */
                 )>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr expected(U&& v) : var_(std::in_place_index<0>, std::forward<U>(v)) {}
 
   template <class U = T _ENABLE_IF(
@@ -195,6 +198,7 @@
     std::is_constructible_v<E, const G&> &&
     std::is_convertible_v<const G&, E> /* non-explicit */
   )>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr expected(const unexpected<G>& e)
   : var_(std::in_place_index<1>, e.value()) {}
 
@@ -209,6 +213,7 @@
     std::is_constructible_v<E, G&&> &&
     std::is_convertible_v<G&&, E> /* non-explicit */
   )>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr expected(unexpected<G>&& e)
   : var_(std::in_place_index<1>, std::move(e.value())) {}
 
@@ -326,6 +331,7 @@
 
   constexpr explicit operator bool() const noexcept { return has_value(); }
   constexpr bool has_value() const noexcept { return var_.index() == 0; }
+  constexpr bool ok() const noexcept { return has_value(); }
 
   constexpr const T& value() const& { return std::get<T>(var_); }
   constexpr T& value() & { return std::get<T>(var_); }
@@ -361,16 +367,6 @@
   template<class T1, class E1, class T2, class E2>
   friend constexpr bool operator!=(const expected<T1, E1>& x, const expected<T2, E2>& y);
 
-  // comparison with T
-  template<class T1, class E1, class T2>
-  friend constexpr bool operator==(const expected<T1, E1>&, const T2&);
-  template<class T1, class E1, class T2>
-  friend constexpr bool operator==(const T2&, const expected<T1, E1>&);
-  template<class T1, class E1, class T2>
-  friend constexpr bool operator!=(const expected<T1, E1>&, const T2&);
-  template<class T1, class E1, class T2>
-  friend constexpr bool operator!=(const T2&, const expected<T1, E1>&);
-
   // Comparison with unexpected<E>
   template<class T1, class E1, class E2>
   friend constexpr bool operator==(const expected<T1, E1>&, const unexpected<E2>&);
@@ -405,24 +401,6 @@
   return !(x == y);
 }
 
-// comparison with T
-template<class T1, class E1, class T2>
-constexpr bool operator==(const expected<T1, E1>& x, const T2& y) {
-  return x.has_value() && (*x == y);
-}
-template<class T1, class E1, class T2>
-constexpr bool operator==(const T2& x, const expected<T1, E1>& y) {
-  return y.has_value() && (x == *y);
-}
-template<class T1, class E1, class T2>
-constexpr bool operator!=(const expected<T1, E1>& x, const T2& y) {
-  return !x.has_value() || (*x != y);
-}
-template<class T1, class E1, class T2>
-constexpr bool operator!=(const T2& x, const expected<T1, E1>& y) {
-  return !y.has_value() || (x != *y);
-}
-
 // Comparison with unexpected<E>
 template<class T1, class E1, class E2>
 constexpr bool operator==(const expected<T1, E1>& x, const unexpected<E2>& y) {
@@ -457,6 +435,7 @@
     std::is_void_v<U> &&
     std::is_convertible_v<const G&, E> /* non-explicit */
   )>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr expected(const expected<U, G>& rhs) {
     if (!rhs.has_value()) var_ = unexpected(rhs.error());
   }
@@ -473,6 +452,7 @@
     std::is_void_v<U> &&
     std::is_convertible_v<const G&&, E> /* non-explicit */
   )>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr expected(expected<U, G>&& rhs) {
     if (!rhs.has_value()) var_ = unexpected(std::move(rhs.error()));
   }
@@ -489,6 +469,7 @@
     std::is_constructible_v<E, const G&> &&
     std::is_convertible_v<const G&, E> /* non-explicit */
   )>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr expected(const unexpected<G>& e)
   : var_(std::in_place_index<1>, e.value()) {}
 
@@ -503,6 +484,7 @@
     std::is_constructible_v<E, G&&> &&
     std::is_convertible_v<G&&, E> /* non-explicit */
   )>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr expected(unexpected<G>&& e)
   : var_(std::in_place_index<1>, std::move(e.value())) {}
 
@@ -576,6 +558,7 @@
   // observers
   constexpr explicit operator bool() const noexcept { return has_value(); }
   constexpr bool has_value() const noexcept { return var_.index() == 0; }
+  constexpr bool ok() const noexcept { return has_value(); }
 
   constexpr void value() const& { if (!has_value()) std::get<0>(var_); }
 
@@ -640,6 +623,7 @@
                 std::is_constructible_v<E, Err> &&
                 !std::is_same_v<std::remove_cv_t<std::remove_reference_t<E>>, std::in_place_t> &&
                 !std::is_same_v<std::remove_cv_t<std::remove_reference_t<E>>, unexpected>)>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr unexpected(Err&& e) : val_(std::forward<Err>(e)) {}
 
   template<class U, class... Args _ENABLE_IF(
@@ -660,6 +644,7 @@
     !std::is_convertible_v<const unexpected<Err>, E> &&
     std::is_convertible_v<Err, E> /* non-explicit */
   )>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr unexpected(const unexpected<Err>& rhs)
   : val_(rhs.value()) {}
 
@@ -690,6 +675,7 @@
     !std::is_convertible_v<const unexpected<Err>, E> &&
     std::is_convertible_v<Err, E> /* non-explicit */
   )>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr unexpected(unexpected<Err>&& rhs)
   : val_(std::move(rhs.value())) {}
 
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index ab6476c..accc225 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -64,6 +64,7 @@
 #include <memory>
 #include <ostream>
 
+#include "android-base/errno_restorer.h"
 #include "android-base/macros.h"
 
 // Note: DO NOT USE DIRECTLY. Use LOG_TAG instead.
@@ -85,7 +86,7 @@
   INFO,
   WARNING,
   ERROR,
-  FATAL_WITHOUT_ABORT,
+  FATAL_WITHOUT_ABORT,  // For loggability tests, this is considered identical to FATAL.
   FATAL,
 };
 
@@ -93,6 +94,8 @@
   DEFAULT,
   MAIN,
   SYSTEM,
+  RADIO,
+  CRASH,
 };
 
 using LogFunction = std::function<void(LogId, LogSeverity, const char*, const char*,
@@ -113,10 +116,8 @@
 
 void DefaultAborter(const char* abort_message);
 
-std::string GetDefaultTag();
 void SetDefaultTag(const std::string& tag);
 
-#ifdef __ANDROID__
 // We expose this even though it is the default because a user that wants to
 // override the default log buffer will have to construct this themselves.
 class LogdLogger {
@@ -129,7 +130,6 @@
  private:
   LogId default_log_id_;
 };
-#endif
 
 // Configure logging based on ANDROID_LOG_TAGS environment variable.
 // We need to parse a string that looks like
@@ -155,27 +155,6 @@
 // Replace the current aborter.
 void SetAborter(AbortFunction&& aborter);
 
-class ErrnoRestorer {
- public:
-  ErrnoRestorer()
-      : saved_errno_(errno) {
-  }
-
-  ~ErrnoRestorer() {
-    errno = saved_errno_;
-  }
-
-  // Allow this object to be used as part of && operation.
-  operator bool() const {
-    return true;
-  }
-
- private:
-  const int saved_errno_;
-
-  DISALLOW_COPY_AND_ASSIGN(ErrnoRestorer);
-};
-
 // A helper macro that produces an expression that accepts both a qualified name and an
 // unqualified name for a LogSeverity, and returns a LogSeverity value.
 // Note: DO NOT USE DIRECTLY. This is an implementation detail.
@@ -211,8 +190,8 @@
 #define ABORT_AFTER_LOG_FATAL_EXPR(x) ABORT_AFTER_LOG_EXPR_IF(true, x)
 
 // Defines whether the given severity will be logged or silently swallowed.
-#define WOULD_LOG(severity) \
-  (UNLIKELY((SEVERITY_LAMBDA(severity)) >= ::android::base::GetMinimumLogSeverity()) || \
+#define WOULD_LOG(severity)                                                              \
+  (UNLIKELY(::android::base::ShouldLog(SEVERITY_LAMBDA(severity), _LOG_TAG_INTERNAL)) || \
    MUST_LOG_MESSAGE(severity))
 
 // Get an ostream that can be used for logging at the given severity and to the default
@@ -222,20 +201,16 @@
 // 1) This will not check whether the severity is high enough. One should use WOULD_LOG to filter
 //    usage manually.
 // 2) This does not save and restore errno.
-#define LOG_STREAM(severity) LOG_STREAM_TO(DEFAULT, severity)
-
-// Get an ostream that can be used for logging at the given severity and to the
-// given destination. The same notes as for LOG_STREAM apply.
-#define LOG_STREAM_TO(dest, severity)                                           \
-  ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::dest,        \
-                              SEVERITY_LAMBDA(severity), _LOG_TAG_INTERNAL, -1) \
+#define LOG_STREAM(severity)                                                                    \
+  ::android::base::LogMessage(__FILE__, __LINE__, SEVERITY_LAMBDA(severity), _LOG_TAG_INTERNAL, \
+                              -1)                                                               \
       .stream()
 
 // Logs a message to logcat on Android otherwise to stderr. If the severity is
 // FATAL it also causes an abort. For example:
 //
 //     LOG(FATAL) << "We didn't expect to reach here";
-#define LOG(severity) LOG_TO(DEFAULT, severity)
+#define LOG(severity) LOGGING_PREAMBLE(severity) && LOG_STREAM(severity)
 
 // Checks if we want to log something, and sets up appropriate RAII objects if
 // so.
@@ -245,21 +220,12 @@
    ABORT_AFTER_LOG_EXPR_IF((SEVERITY_LAMBDA(severity)) == ::android::base::FATAL, true) && \
    ::android::base::ErrnoRestorer())
 
-// Logs a message to logcat with the specified log ID on Android otherwise to
-// stderr. If the severity is FATAL it also causes an abort.
-// Use an expression here so we can support the << operator following the macro,
-// like "LOG(DEBUG) << xxx;".
-#define LOG_TO(dest, severity) LOGGING_PREAMBLE(severity) && LOG_STREAM_TO(dest, severity)
-
 // A variant of LOG that also logs the current errno value. To be used when
 // library calls fail.
-#define PLOG(severity) PLOG_TO(DEFAULT, severity)
-
-// Behaves like PLOG, but logs to the specified log ID.
-#define PLOG_TO(dest, severity)                                                        \
-  LOGGING_PREAMBLE(severity) &&                                                        \
-      ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::dest,           \
-                                  SEVERITY_LAMBDA(severity), _LOG_TAG_INTERNAL, errno) \
+#define PLOG(severity)                                                           \
+  LOGGING_PREAMBLE(severity) &&                                                  \
+      ::android::base::LogMessage(__FILE__, __LINE__, SEVERITY_LAMBDA(severity), \
+                                  _LOG_TAG_INTERNAL, errno)                      \
           .stream()
 
 // Marker that code is yet to be implemented.
@@ -272,24 +238,23 @@
 //
 //     CHECK(false == true) results in a log message of
 //       "Check failed: false == true".
-#define CHECK(x)                                                                 \
-  LIKELY((x)) || ABORT_AFTER_LOG_FATAL_EXPR(false) ||                            \
-      ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT,  \
-                                  ::android::base::FATAL, _LOG_TAG_INTERNAL, -1) \
-              .stream()                                                          \
+#define CHECK(x)                                                                                 \
+  LIKELY((x)) || ABORT_AFTER_LOG_FATAL_EXPR(false) ||                                            \
+      ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::FATAL, _LOG_TAG_INTERNAL, \
+                                  -1)                                                            \
+              .stream()                                                                          \
           << "Check failed: " #x << " "
 
 // clang-format off
 // Helper for CHECK_xx(x,y) macros.
-#define CHECK_OP(LHS, RHS, OP)                                                                 \
-  for (auto _values = ::android::base::MakeEagerEvaluator(LHS, RHS);                           \
-       UNLIKELY(!(_values.lhs OP _values.rhs));                                                \
-       /* empty */)                                                                            \
-  ABORT_AFTER_LOG_FATAL                                                                        \
-  ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT,                    \
-                              ::android::base::FATAL, _LOG_TAG_INTERNAL, -1)                   \
-          .stream()                                                                            \
-      << "Check failed: " << #LHS << " " << #OP << " " << #RHS << " (" #LHS "=" << _values.lhs \
+#define CHECK_OP(LHS, RHS, OP)                                                                   \
+  for (auto _values = ::android::base::MakeEagerEvaluator(LHS, RHS);                             \
+       UNLIKELY(!(_values.lhs OP _values.rhs));                                                  \
+       /* empty */)                                                                              \
+  ABORT_AFTER_LOG_FATAL                                                                          \
+  ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::FATAL, _LOG_TAG_INTERNAL, -1) \
+          .stream()                                                                              \
+      << "Check failed: " << #LHS << " " << #OP << " " << #RHS << " (" #LHS "=" << _values.lhs   \
       << ", " #RHS "=" << _values.rhs << ") "
 // clang-format on
 
@@ -311,8 +276,8 @@
 #define CHECK_STROP(s1, s2, sense)                                             \
   while (UNLIKELY((strcmp(s1, s2) == 0) != (sense)))                           \
     ABORT_AFTER_LOG_FATAL                                                      \
-    ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT,  \
-                                ::android::base::FATAL, _LOG_TAG_INTERNAL, -1) \
+    ::android::base::LogMessage(__FILE__, __LINE__,  ::android::base::FATAL,   \
+                                 _LOG_TAG_INTERNAL, -1)                        \
         .stream()                                                              \
         << "Check failed: " << "\"" << (s1) << "\""                            \
         << ((sense) ? " == " : " != ") << "\"" << (s2) << "\""
@@ -431,8 +396,10 @@
 // of a CHECK. The destructor will abort if the severity is FATAL.
 class LogMessage {
  public:
-  LogMessage(const char* file, unsigned int line, LogId id, LogSeverity severity, const char* tag,
+  // LogId has been deprecated, but this constructor must exist for prebuilts.
+  LogMessage(const char* file, unsigned int line, LogId, LogSeverity severity, const char* tag,
              int error);
+  LogMessage(const char* file, unsigned int line, LogSeverity severity, const char* tag, int error);
 
   ~LogMessage();
 
@@ -441,8 +408,8 @@
   std::ostream& stream();
 
   // The routine that performs the actual logging.
-  static void LogLine(const char* file, unsigned int line, LogId id, LogSeverity severity,
-                      const char* tag, const char* msg);
+  static void LogLine(const char* file, unsigned int line, LogSeverity severity, const char* tag,
+                      const char* msg);
 
  private:
   const std::unique_ptr<LogMessageData> data_;
@@ -456,6 +423,9 @@
 // Set the minimum severity level for logging, returning the old severity.
 LogSeverity SetMinimumLogSeverity(LogSeverity new_severity);
 
+// Return whether or not a log message with the associated tag should be logged.
+bool ShouldLog(LogSeverity severity, const char* tag);
+
 // Allows to temporarily change the minimum severity level for logging.
 class ScopedLogSeverity {
  public:
@@ -474,9 +444,6 @@
 // Emit a warning of ostream<< with std::string*. The intention was most likely to print *string.
 //
 // Note: for this to work, we need to have this in a namespace.
-// Note: lots of ifdef magic to make this work with Clang (platform) vs GCC (windows tools)
-// Note: using diagnose_if(true) under Clang and nothing under GCC/mingw as there is no common
-//       attribute support.
 // Note: using a pragma because "-Wgcc-compat" (included in "-Weverything") complains about
 //       diagnose_if.
 // Note: to print the pointer, use "<< static_cast<const void*>(string_pointer)" instead.
@@ -486,8 +453,8 @@
 #pragma clang diagnostic ignored "-Wgcc-compat"
 #define OSTREAM_STRING_POINTER_USAGE_WARNING \
     __attribute__((diagnose_if(true, "Unexpected logging of string pointer", "warning")))
-inline std::ostream& operator<<(std::ostream& stream, const std::string* string_pointer)
-    OSTREAM_STRING_POINTER_USAGE_WARNING {
+inline OSTREAM_STRING_POINTER_USAGE_WARNING
+std::ostream& operator<<(std::ostream& stream, const std::string* string_pointer) {
   return stream << static_cast<const void*>(string_pointer);
 }
 #pragma clang diagnostic pop
diff --git a/base/include/android-base/result.h b/base/include/android-base/result.h
index 1b763af..5e65876 100644
--- a/base/include/android-base/result.h
+++ b/base/include/android-base/result.h
@@ -90,6 +90,7 @@
   ResultError(T&& message, int code) : message_(std::forward<T>(message)), code_(code) {}
 
   template <typename T>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   operator android::base::expected<T, ResultError>() {
     return android::base::unexpected(ResultError(message_, code_));
   }
@@ -118,9 +119,11 @@
 class Error {
  public:
   Error() : errno_(0), append_errno_(false) {}
+  // NOLINTNEXTLINE(google-explicit-constructor)
   Error(int errno_to_append) : errno_(errno_to_append), append_errno_(true) {}
 
   template <typename T>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   operator android::base::expected<T, ResultError>() {
     return android::base::unexpected(ResultError(str(), errno_));
   }
@@ -153,11 +156,11 @@
   Error& operator=(const Error&) = delete;
   Error& operator=(Error&&) = delete;
 
-  template <typename... Args>
-  friend Error Errorf(const char* fmt, const Args&... args);
+  template <typename T, typename... Args>
+  friend Error ErrorfImpl(const T&& fmt, const Args&... args);
 
-  template <typename... Args>
-  friend Error ErrnoErrorf(const char* fmt, const Args&... args);
+  template <typename T, typename... Args>
+  friend Error ErrnoErrorfImpl(const T&& fmt, const Args&... args);
 
  private:
   Error(bool append_errno, int errno_to_append, const std::string& message)
@@ -188,18 +191,44 @@
   return ErrorCode(code, args...);
 }
 
-template <typename... Args>
-inline Error Errorf(const char* fmt, const Args&... args) {
+template <typename T, typename... Args>
+inline Error ErrorfImpl(const T&& fmt, const Args&... args) {
   return Error(false, ErrorCode(0, args...), fmt::format(fmt, args...));
 }
 
-template <typename... Args>
-inline Error ErrnoErrorf(const char* fmt, const Args&... args) {
+template <typename T, typename... Args>
+inline Error ErrnoErrorfImpl(const T&& fmt, const Args&... args) {
   return Error(true, errno, fmt::format(fmt, args...));
 }
 
+#define Errorf(fmt, ...) android::base::ErrorfImpl(FMT_STRING(fmt), ##__VA_ARGS__)
+#define ErrnoErrorf(fmt, ...) android::base::ErrnoErrorfImpl(FMT_STRING(fmt), ##__VA_ARGS__)
+
 template <typename T>
 using Result = android::base::expected<T, ResultError>;
 
+// Macros for testing the results of functions that return android::base::Result.
+// These also work with base::android::expected.
+
+#define CHECK_RESULT_OK(stmt)       \
+  do {                              \
+    const auto& tmp = (stmt);       \
+    CHECK(tmp.ok()) << tmp.error(); \
+  } while (0)
+
+#define ASSERT_RESULT_OK(stmt)            \
+  do {                                    \
+    const auto& tmp = (stmt);             \
+    ASSERT_TRUE(tmp.ok()) << tmp.error(); \
+  } while (0)
+
+#define EXPECT_RESULT_OK(stmt)            \
+  do {                                    \
+    auto tmp = (stmt);                    \
+    EXPECT_TRUE(tmp.ok()) << tmp.error(); \
+  } while (0)
+
+// TODO: Maybe add RETURN_IF_ERROR() and ASSIGN_OR_RETURN()
+
 }  // namespace base
 }  // namespace android
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index 1605daf..c4a0aad 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -116,6 +116,8 @@
   bool operator<(int rhs) const { return get() < rhs; }
   bool operator==(int rhs) const { return get() == rhs; }
   bool operator!=(int rhs) const { return get() != rhs; }
+  bool operator==(const unique_fd_impl& rhs) const { return get() == rhs.get(); }
+  bool operator!=(const unique_fd_impl& rhs) const { return get() != rhs.get(); }
 
   // Catch bogus error checks (i.e.: "!fd" instead of "fd != -1").
   bool operator!() const = delete;
diff --git a/base/liblog_symbols.cpp b/base/liblog_symbols.cpp
new file mode 100644
index 0000000..d5dfcd2
--- /dev/null
+++ b/base/liblog_symbols.cpp
@@ -0,0 +1,85 @@
+/*
+ * 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 "liblog_symbols.h"
+
+#if defined(__ANDROID__) && !defined(NO_LIBLOG_DLSYM)
+#include <dlfcn.h>
+#endif
+
+namespace android {
+namespace base {
+
+#if defined(__ANDROID__) && !defined(NO_LIBLOG_DLSYM)
+
+const std::optional<LibLogFunctions>& GetLibLogFunctions() {
+  static std::optional<LibLogFunctions> liblog_functions = []() -> std::optional<LibLogFunctions> {
+    void* liblog_handle = dlopen("liblog.so", RTLD_NOW);
+    if (liblog_handle == nullptr) {
+      return {};
+    }
+
+    LibLogFunctions real_liblog_functions = {};
+
+#define DLSYM(name)                                                                   \
+  real_liblog_functions.name =                                                        \
+      reinterpret_cast<decltype(LibLogFunctions::name)>(dlsym(liblog_handle, #name)); \
+  if (real_liblog_functions.name == nullptr) {                                        \
+    return {};                                                                        \
+  }
+
+    DLSYM(__android_log_set_logger)
+    DLSYM(__android_log_write_logger_data)
+    DLSYM(__android_log_logd_logger)
+    DLSYM(__android_log_stderr_logger)
+    DLSYM(__android_log_set_aborter)
+    DLSYM(__android_log_call_aborter)
+    DLSYM(__android_log_default_aborter)
+    DLSYM(__android_log_set_minimum_priority);
+    DLSYM(__android_log_get_minimum_priority);
+    DLSYM(__android_log_set_default_tag);
+#undef DLSYM
+
+    return real_liblog_functions;
+  }();
+
+  return liblog_functions;
+}
+
+#else
+
+const std::optional<LibLogFunctions>& GetLibLogFunctions() {
+  static std::optional<LibLogFunctions> liblog_functions = []() -> std::optional<LibLogFunctions> {
+    return LibLogFunctions{
+        .__android_log_set_logger = __android_log_set_logger,
+        .__android_log_write_logger_data = __android_log_write_logger_data,
+        .__android_log_logd_logger = __android_log_logd_logger,
+        .__android_log_stderr_logger = __android_log_stderr_logger,
+        .__android_log_set_aborter = __android_log_set_aborter,
+        .__android_log_call_aborter = __android_log_call_aborter,
+        .__android_log_default_aborter = __android_log_default_aborter,
+        .__android_log_set_minimum_priority = __android_log_set_minimum_priority,
+        .__android_log_get_minimum_priority = __android_log_get_minimum_priority,
+        .__android_log_set_default_tag = __android_log_set_default_tag,
+    };
+  }();
+  return liblog_functions;
+}
+
+#endif
+
+}  // namespace base
+}  // namespace android
diff --git a/base/liblog_symbols.h b/base/liblog_symbols.h
new file mode 100644
index 0000000..d3134e9
--- /dev/null
+++ b/base/liblog_symbols.h
@@ -0,0 +1,47 @@
+/*
+ * 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 <optional>
+
+#include <android/log.h>
+
+namespace android {
+namespace base {
+
+struct LibLogFunctions {
+  void (*__android_log_set_logger)(__android_logger_function logger);
+  void (*__android_log_write_logger_data)(struct __android_logger_data* logger_data,
+                                          const char* msg);
+
+  void (*__android_log_logd_logger)(const struct __android_logger_data* logger_data,
+                                    const char* msg);
+  void (*__android_log_stderr_logger)(const struct __android_logger_data* logger_data,
+                                      const char* message);
+
+  void (*__android_log_set_aborter)(__android_aborter_function aborter);
+  void (*__android_log_call_aborter)(const char* abort_message);
+  void (*__android_log_default_aborter)(const char* abort_message);
+  int (*__android_log_set_minimum_priority)(int priority);
+  int (*__android_log_get_minimum_priority)();
+  void (*__android_log_set_default_tag)(const char* tag);
+};
+
+const std::optional<LibLogFunctions>& GetLibLogFunctions();
+
+}  // namespace base
+}  // namespace android
diff --git a/base/logging.cpp b/base/logging.cpp
index f89168c..f42b996 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -36,17 +36,18 @@
 #include <sys/uio.h>
 #endif
 
+#include <atomic>
 #include <iostream>
 #include <limits>
 #include <mutex>
+#include <optional>
 #include <sstream>
 #include <string>
 #include <utility>
 #include <vector>
 
-// Headers for LogMessage::LogLine.
-#ifdef __ANDROID__
 #include <android/log.h>
+#ifdef __ANDROID__
 #include <android/set_abort_message.h>
 #else
 #include <sys/types.h>
@@ -59,6 +60,8 @@
 #include <android-base/strings.h>
 #include <android-base/threads.h>
 
+#include "liblog_symbols.h"
+
 namespace android {
 namespace base {
 
@@ -115,11 +118,84 @@
 }
 #endif
 
+static LogId log_id_tToLogId(int buffer_id) {
+  switch (buffer_id) {
+    case LOG_ID_MAIN:
+      return MAIN;
+    case LOG_ID_SYSTEM:
+      return SYSTEM;
+    case LOG_ID_RADIO:
+      return RADIO;
+    case LOG_ID_CRASH:
+      return CRASH;
+    case LOG_ID_DEFAULT:
+    default:
+      return DEFAULT;
+  }
+}
+
+static int LogIdTolog_id_t(LogId log_id) {
+  switch (log_id) {
+    case MAIN:
+      return LOG_ID_MAIN;
+    case SYSTEM:
+      return LOG_ID_SYSTEM;
+    case RADIO:
+      return LOG_ID_RADIO;
+    case CRASH:
+      return LOG_ID_CRASH;
+    case DEFAULT:
+    default:
+      return LOG_ID_DEFAULT;
+  }
+}
+
+static LogSeverity PriorityToLogSeverity(int priority) {
+  switch (priority) {
+    case ANDROID_LOG_DEFAULT:
+      return INFO;
+    case ANDROID_LOG_VERBOSE:
+      return VERBOSE;
+    case ANDROID_LOG_DEBUG:
+      return DEBUG;
+    case ANDROID_LOG_INFO:
+      return INFO;
+    case ANDROID_LOG_WARN:
+      return WARNING;
+    case ANDROID_LOG_ERROR:
+      return ERROR;
+    case ANDROID_LOG_FATAL:
+      return FATAL;
+    default:
+      return FATAL;
+  }
+}
+
+static android_LogPriority LogSeverityToPriority(LogSeverity severity) {
+  switch (severity) {
+    case VERBOSE:
+      return ANDROID_LOG_VERBOSE;
+    case DEBUG:
+      return ANDROID_LOG_DEBUG;
+    case INFO:
+      return ANDROID_LOG_INFO;
+    case WARNING:
+      return ANDROID_LOG_WARN;
+    case ERROR:
+      return ANDROID_LOG_ERROR;
+    case FATAL_WITHOUT_ABORT:
+    case FATAL:
+    default:
+      return ANDROID_LOG_FATAL;
+  }
+}
+
 static std::mutex& LoggingLock() {
   static auto& logging_lock = *new std::mutex();
   return logging_lock;
 }
 
+// Only used for Q fallback.
 static LogFunction& Logger() {
 #ifdef __ANDROID__
   static auto& logger = *new LogFunction(LogdLogger());
@@ -129,35 +205,39 @@
   return logger;
 }
 
+// Only used for Q fallback.
 static AbortFunction& Aborter() {
   static auto& aborter = *new AbortFunction(DefaultAborter);
   return aborter;
 }
 
+// Only used for Q fallback.
 static std::recursive_mutex& TagLock() {
   static auto& tag_lock = *new std::recursive_mutex();
   return tag_lock;
 }
+// Only used for Q fallback.
 static std::string* gDefaultTag;
-std::string GetDefaultTag() {
-  std::lock_guard<std::recursive_mutex> lock(TagLock());
-  if (gDefaultTag == nullptr) {
-    return "";
-  }
-  return *gDefaultTag;
-}
+
 void SetDefaultTag(const std::string& tag) {
-  std::lock_guard<std::recursive_mutex> lock(TagLock());
-  if (gDefaultTag != nullptr) {
-    delete gDefaultTag;
-    gDefaultTag = nullptr;
-  }
-  if (!tag.empty()) {
-    gDefaultTag = new std::string(tag);
+  static auto& liblog_functions = GetLibLogFunctions();
+  if (liblog_functions) {
+    liblog_functions->__android_log_set_default_tag(tag.c_str());
+  } else {
+    std::lock_guard<std::recursive_mutex> lock(TagLock());
+    if (gDefaultTag != nullptr) {
+      delete gDefaultTag;
+      gDefaultTag = nullptr;
+    }
+    if (!tag.empty()) {
+      gDefaultTag = new std::string(tag);
+    }
   }
 }
 
 static bool gInitialized = false;
+
+// Only used for Q fallback.
 static LogSeverity gMinimumLogSeverity = INFO;
 
 #if defined(__linux__)
@@ -218,8 +298,13 @@
   static_assert(arraysize(log_characters) - 1 == FATAL + 1,
                 "Mismatch in size of log_characters and values in LogSeverity");
   char severity_char = log_characters[severity];
-  fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s:%u] %s\n", tag ? tag : "nullptr", severity_char,
-          timestamp, getpid(), GetThreadId(), file, line, message);
+  if (file != nullptr) {
+    fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s:%u] %s\n", tag ? tag : "nullptr", severity_char,
+            timestamp, getpid(), GetThreadId(), file, line, message);
+  } else {
+    fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s\n", tag ? tag : "nullptr", severity_char,
+            timestamp, getpid(), GetThreadId(), message);
+  }
 }
 
 void StdioLogger(LogId, LogSeverity severity, const char* /*tag*/, const char* /*file*/,
@@ -242,41 +327,35 @@
 }
 
 
-#ifdef __ANDROID__
 LogdLogger::LogdLogger(LogId default_log_id) : default_log_id_(default_log_id) {
 }
 
 void LogdLogger::operator()(LogId id, LogSeverity severity, const char* tag,
                             const char* file, unsigned int line,
                             const char* message) {
-  static constexpr android_LogPriority kLogSeverityToAndroidLogPriority[] = {
-      ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO,
-      ANDROID_LOG_WARN,    ANDROID_LOG_ERROR, ANDROID_LOG_FATAL,
-      ANDROID_LOG_FATAL,
-  };
-  static_assert(arraysize(kLogSeverityToAndroidLogPriority) == FATAL + 1,
-                "Mismatch in size of kLogSeverityToAndroidLogPriority and values in LogSeverity");
-
-  int priority = kLogSeverityToAndroidLogPriority[severity];
+  android_LogPriority priority = LogSeverityToPriority(severity);
   if (id == DEFAULT) {
     id = default_log_id_;
   }
 
-  static constexpr log_id kLogIdToAndroidLogId[] = {
-    LOG_ID_MAX, LOG_ID_MAIN, LOG_ID_SYSTEM,
-  };
-  static_assert(arraysize(kLogIdToAndroidLogId) == SYSTEM + 1,
-                "Mismatch in size of kLogIdToAndroidLogId and values in LogId");
-  log_id lg_id = kLogIdToAndroidLogId[id];
+  int lg_id = LogIdTolog_id_t(id);
 
-  if (priority == ANDROID_LOG_FATAL) {
-    __android_log_buf_print(lg_id, priority, tag, "%s:%u] %s", file, line,
-                            message);
+  char log_message_with_file[4068];  // LOGGER_ENTRY_MAX_PAYLOAD, not available in the NDK.
+  if (priority == ANDROID_LOG_FATAL && file != nullptr) {
+    snprintf(log_message_with_file, sizeof(log_message_with_file), "%s:%u] %s", file, line,
+             message);
+    message = log_message_with_file;
+  }
+
+  static auto& liblog_functions = GetLibLogFunctions();
+  if (liblog_functions) {
+    __android_logger_data logger_data = {sizeof(__android_logger_data),     lg_id, priority, tag,
+                                         static_cast<const char*>(nullptr), 0};
+    liblog_functions->__android_log_logd_logger(&logger_data, message);
   } else {
     __android_log_buf_print(lg_id, priority, tag, "%s", message);
   }
 }
-#endif
 
 void InitLogging(char* argv[], LogFunction&& logger, AbortFunction&& aborter) {
   SetLogger(std::forward<LogFunction>(logger));
@@ -307,27 +386,27 @@
     if (spec.size() == 3 && StartsWith(spec, "*:")) {
       switch (spec[2]) {
         case 'v':
-          gMinimumLogSeverity = VERBOSE;
+          SetMinimumLogSeverity(VERBOSE);
           continue;
         case 'd':
-          gMinimumLogSeverity = DEBUG;
+          SetMinimumLogSeverity(DEBUG);
           continue;
         case 'i':
-          gMinimumLogSeverity = INFO;
+          SetMinimumLogSeverity(INFO);
           continue;
         case 'w':
-          gMinimumLogSeverity = WARNING;
+          SetMinimumLogSeverity(WARNING);
           continue;
         case 'e':
-          gMinimumLogSeverity = ERROR;
+          SetMinimumLogSeverity(ERROR);
           continue;
         case 'f':
-          gMinimumLogSeverity = FATAL_WITHOUT_ABORT;
+          SetMinimumLogSeverity(FATAL_WITHOUT_ABORT);
           continue;
         // liblog will even suppress FATAL if you say 's' for silent, but that's
         // crazy!
         case 's':
-          gMinimumLogSeverity = FATAL_WITHOUT_ABORT;
+          SetMinimumLogSeverity(FATAL_WITHOUT_ABORT);
           continue;
       }
     }
@@ -337,24 +416,56 @@
 }
 
 void SetLogger(LogFunction&& logger) {
-  std::lock_guard<std::mutex> lock(LoggingLock());
-  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_logger_data* logger_data,
+                                                  const char* message) {
+      auto log_id = log_id_tToLogId(logger_data->buffer_id);
+      auto severity = PriorityToLogSeverity(logger_data->priority);
+
+      auto& function = *logger_function.load(std::memory_order_acquire);
+      function(log_id, severity, logger_data->tag, logger_data->file, logger_data->line, message);
+    });
+    delete old_logger_function;
+  } else {
+    std::lock_guard<std::mutex> lock(LoggingLock());
+    Logger() = std::move(logger);
+  }
 }
 
 void SetAborter(AbortFunction&& aborter) {
-  std::lock_guard<std::mutex> lock(LoggingLock());
-  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));
+    __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);
+  }
 }
 
 // This indirection greatly reduces the stack impact of having lots of
 // checks/logging in a function.
 class LogMessageData {
  public:
-  LogMessageData(const char* file, unsigned int line, LogId id, LogSeverity severity,
-                 const char* tag, int error)
+  LogMessageData(const char* file, unsigned int line, LogSeverity severity, const char* tag,
+                 int error)
       : file_(GetFileBasename(file)),
         line_number_(line),
-        id_(id),
         severity_(severity),
         tag_(tag),
         error_(error) {}
@@ -373,10 +484,6 @@
 
   const char* GetTag() const { return tag_; }
 
-  LogId GetId() const {
-    return id_;
-  }
-
   int GetError() const {
     return error_;
   }
@@ -393,7 +500,6 @@
   std::ostringstream buffer_;
   const char* const file_;
   const unsigned int line_number_;
-  const LogId id_;
   const LogSeverity severity_;
   const char* const tag_;
   const int error_;
@@ -401,9 +507,13 @@
   DISALLOW_COPY_AND_ASSIGN(LogMessageData);
 };
 
-LogMessage::LogMessage(const char* file, unsigned int line, LogId id, LogSeverity severity,
+LogMessage::LogMessage(const char* file, unsigned int line, LogId, LogSeverity severity,
                        const char* tag, int error)
-    : data_(new LogMessageData(file, line, id, severity, tag, error)) {}
+    : LogMessage(file, line, severity, tag, error) {}
+
+LogMessage::LogMessage(const char* file, unsigned int line, LogSeverity severity, const char* tag,
+                       int error)
+    : data_(new LogMessageData(file, line, severity, tag, error)) {}
 
 LogMessage::~LogMessage() {
   // Check severity again. This is duplicate work wrt/ LOG macros, but not LOG_STREAM.
@@ -429,16 +539,16 @@
     // Do the actual logging with the lock held.
     std::lock_guard<std::mutex> lock(LoggingLock());
     if (msg.find('\n') == std::string::npos) {
-      LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(), data_->GetSeverity(),
-              data_->GetTag(), msg.c_str());
+      LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(),
+              msg.c_str());
     } else {
       msg += '\n';
       size_t i = 0;
       while (i < msg.size()) {
         size_t nl = msg.find('\n', i);
         msg[nl] = '\0';
-        LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(), data_->GetSeverity(),
-                data_->GetTag(), &msg[i]);
+        LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(),
+                &msg[i]);
         // Undo the zero-termination so we can give the complete message to the aborter.
         msg[nl] = '\n';
         i = nl + 1;
@@ -448,7 +558,12 @@
 
   // Abort if necessary.
   if (data_->GetSeverity() == FATAL) {
-    Aborter()(msg.c_str());
+    static auto& liblog_functions = GetLibLogFunctions();
+    if (liblog_functions) {
+      liblog_functions->__android_log_call_aborter(msg.c_str());
+    } else {
+      Aborter()(msg.c_str());
+    }
   }
 }
 
@@ -456,27 +571,60 @@
   return data_->GetBuffer();
 }
 
-void LogMessage::LogLine(const char* file, unsigned int line, LogId id, LogSeverity severity,
-                         const char* tag, const char* message) {
-  if (tag == nullptr) {
-    std::lock_guard<std::recursive_mutex> lock(TagLock());
-    if (gDefaultTag == nullptr) {
-      gDefaultTag = new std::string(getprogname());
-    }
-    Logger()(id, severity, gDefaultTag->c_str(), file, line, message);
+void LogMessage::LogLine(const char* file, unsigned int line, LogSeverity severity, const char* tag,
+                         const char* message) {
+  static auto& liblog_functions = GetLibLogFunctions();
+  auto priority = LogSeverityToPriority(severity);
+  if (liblog_functions) {
+    __android_logger_data logger_data = {
+        sizeof(__android_logger_data), LOG_ID_DEFAULT, priority, tag, file, line};
+    __android_log_write_logger_data(&logger_data, message);
   } else {
-    Logger()(id, severity, tag, file, line, message);
+    if (tag == nullptr) {
+      std::lock_guard<std::recursive_mutex> lock(TagLock());
+      if (gDefaultTag == nullptr) {
+        gDefaultTag = new std::string(getprogname());
+      }
+
+      Logger()(DEFAULT, severity, gDefaultTag->c_str(), file, line, message);
+    } else {
+      Logger()(DEFAULT, severity, tag, file, line, message);
+    }
   }
 }
 
 LogSeverity GetMinimumLogSeverity() {
+  static auto& liblog_functions = GetLibLogFunctions();
+  if (liblog_functions) {
+    return PriorityToLogSeverity(liblog_functions->__android_log_get_minimum_priority());
+  } else {
     return gMinimumLogSeverity;
+  }
+}
+
+bool ShouldLog(LogSeverity severity, const char* tag) {
+  static auto& liblog_functions = GetLibLogFunctions();
+  // Even though we're not using the R liblog functions in this function, if we're running on Q,
+  // we need to fall back to using gMinimumLogSeverity, since __android_log_is_loggable() will not
+  // take into consideration the value from SetMinimumLogSeverity().
+  if (liblog_functions) {
+    int priority = LogSeverityToPriority(severity);
+    return __android_log_is_loggable(priority, tag, ANDROID_LOG_INFO);
+  } else {
+    return severity >= gMinimumLogSeverity;
+  }
 }
 
 LogSeverity SetMinimumLogSeverity(LogSeverity new_severity) {
-  LogSeverity old_severity = gMinimumLogSeverity;
-  gMinimumLogSeverity = new_severity;
-  return old_severity;
+  static auto& liblog_functions = GetLibLogFunctions();
+  if (liblog_functions) {
+    auto priority = LogSeverityToPriority(new_severity);
+    return PriorityToLogSeverity(liblog_functions->__android_log_set_minimum_priority(priority));
+  } else {
+    LogSeverity old_severity = gMinimumLogSeverity;
+    gMinimumLogSeverity = new_severity;
+    return old_severity;
+  }
 }
 
 ScopedLogSeverity::ScopedLogSeverity(LogSeverity new_severity) {
diff --git a/base/logging_test.cpp b/base/logging_test.cpp
index 3113fb4..3a453e6 100644
--- a/base/logging_test.cpp
+++ b/base/logging_test.cpp
@@ -140,10 +140,6 @@
   CHECK_WOULD_LOG_ENABLED(FATAL);
 }
 
-TEST(logging, WOULD_LOG_FATAL_WITHOUT_ABORT_disabled) {
-  CHECK_WOULD_LOG_DISABLED(FATAL_WITHOUT_ABORT);
-}
-
 TEST(logging, WOULD_LOG_FATAL_WITHOUT_ABORT_enabled) {
   CHECK_WOULD_LOG_ENABLED(FATAL_WITHOUT_ABORT);
 }
@@ -266,10 +262,6 @@
     CheckMessage(cap2, android::base::severity, "foobar"); \
   } \
 
-TEST(logging, LOG_STREAM_FATAL_WITHOUT_ABORT_disabled) {
-  CHECK_LOG_STREAM_DISABLED(FATAL_WITHOUT_ABORT);
-}
-
 TEST(logging, LOG_STREAM_FATAL_WITHOUT_ABORT_enabled) {
   ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(FATAL_WITHOUT_ABORT));
 }
@@ -352,10 +344,6 @@
   ASSERT_DEATH({SuppressAbortUI(); LOG(::android::base::FATAL) << "foobar";}, "foobar");
 }
 
-TEST(logging, LOG_FATAL_WITHOUT_ABORT_disabled) {
-  CHECK_LOG_DISABLED(FATAL_WITHOUT_ABORT);
-}
-
 TEST(logging, LOG_FATAL_WITHOUT_ABORT_enabled) {
   ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(FATAL_WITHOUT_ABORT));
 }
@@ -508,10 +496,6 @@
   ASSERT_DEATH({SuppressAbortUI(); PLOG(::android::base::FATAL) << "foobar";}, "foobar");
 }
 
-TEST(logging, PLOG_FATAL_WITHOUT_ABORT_disabled) {
-  CHECK_PLOG_DISABLED(FATAL_WITHOUT_ABORT);
-}
-
 TEST(logging, PLOG_FATAL_WITHOUT_ABORT_enabled) {
   ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(FATAL_WITHOUT_ABORT));
 }
@@ -619,21 +603,6 @@
   LOG(ERROR) << "foobar";
 }
 
-TEST(logging, SetDefaultTag) {
-  constexpr const char* expected_tag = "test_tag";
-  constexpr const char* expected_msg = "foobar";
-  CapturedStderr cap;
-  {
-    std::string old_default_tag = android::base::GetDefaultTag();
-    android::base::SetDefaultTag(expected_tag);
-    android::base::ScopedLogSeverity sls(android::base::LogSeverity::INFO);
-    LOG(INFO) << expected_msg;
-    android::base::SetDefaultTag(old_default_tag);
-  }
-  ASSERT_NO_FATAL_FAILURE(
-      CheckMessage(cap, android::base::LogSeverity::INFO, expected_msg, expected_tag));
-}
-
 TEST(logging, StdioLogger) {
   CapturedStderr cap_err;
   CapturedStdout cap_out;
diff --git a/base/result_test.cpp b/base/result_test.cpp
index 2ee4057..c0ac0fd 100644
--- a/base/result_test.cpp
+++ b/base/result_test.cpp
@@ -30,7 +30,7 @@
 
 TEST(result, result_accessors) {
   Result<std::string> result = "success";
-  ASSERT_TRUE(result);
+  ASSERT_RESULT_OK(result);
   ASSERT_TRUE(result.has_value());
 
   EXPECT_EQ("success", *result);
@@ -40,7 +40,7 @@
 }
 
 TEST(result, result_accessors_rvalue) {
-  ASSERT_TRUE(Result<std::string>("success"));
+  ASSERT_TRUE(Result<std::string>("success").ok());
   ASSERT_TRUE(Result<std::string>("success").has_value());
 
   EXPECT_EQ("success", *Result<std::string>("success"));
@@ -51,12 +51,12 @@
 
 TEST(result, result_void) {
   Result<void> ok = {};
-  EXPECT_TRUE(ok);
+  EXPECT_RESULT_OK(ok);
   ok.value();  // should not crash
   ASSERT_DEATH(ok.error(), "");
 
   Result<void> fail = Error() << "failure" << 1;
-  EXPECT_FALSE(fail);
+  EXPECT_FALSE(fail.ok());
   EXPECT_EQ("failure1", fail.error().message());
   EXPECT_EQ(0, fail.error().code());
   EXPECT_TRUE(ok != fail);
@@ -66,8 +66,8 @@
     if (ok) return {};
     else return Error() << "failure" << 1;
   };
-  EXPECT_TRUE(test(true));
-  EXPECT_FALSE(test(false));
+  EXPECT_TRUE(test(true).ok());
+  EXPECT_FALSE(test(false).ok());
   test(true).value();  // should not crash
   ASSERT_DEATH(test(true).error(), "");
   ASSERT_DEATH(test(false).value(), "");
@@ -76,7 +76,7 @@
 
 TEST(result, result_error) {
   Result<void> result = Error() << "failure" << 1;
-  ASSERT_FALSE(result);
+  ASSERT_FALSE(result.ok());
   ASSERT_FALSE(result.has_value());
 
   EXPECT_EQ(0, result.error().code());
@@ -85,7 +85,7 @@
 
 TEST(result, result_error_empty) {
   Result<void> result = Error();
-  ASSERT_FALSE(result);
+  ASSERT_FALSE(result.ok());
   ASSERT_FALSE(result.has_value());
 
   EXPECT_EQ(0, result.error().code());
@@ -100,7 +100,7 @@
   // create is.
 
   auto MakeRvalueErrorResult = []() -> Result<void> { return Error() << "failure" << 1; };
-  ASSERT_FALSE(MakeRvalueErrorResult());
+  ASSERT_FALSE(MakeRvalueErrorResult().ok());
   ASSERT_FALSE(MakeRvalueErrorResult().has_value());
 
   EXPECT_EQ(0, MakeRvalueErrorResult().error().code());
@@ -112,7 +112,7 @@
   errno = test_errno;
   Result<void> result = ErrnoError() << "failure" << 1;
 
-  ASSERT_FALSE(result);
+  ASSERT_FALSE(result.ok());
   ASSERT_FALSE(result.has_value());
 
   EXPECT_EQ(test_errno, result.error().code());
@@ -124,7 +124,7 @@
   errno = test_errno;
   Result<void> result = ErrnoError();
 
-  ASSERT_FALSE(result);
+  ASSERT_FALSE(result.ok());
   ASSERT_FALSE(result.has_value());
 
   EXPECT_EQ(test_errno, result.error().code());
@@ -135,12 +135,12 @@
   auto error_text = "test error"s;
   Result<void> result = Error() << error_text;
 
-  ASSERT_FALSE(result);
+  ASSERT_FALSE(result.ok());
   ASSERT_FALSE(result.has_value());
 
   Result<std::string> result2 = result.error();
 
-  ASSERT_FALSE(result2);
+  ASSERT_FALSE(result2.ok());
   ASSERT_FALSE(result2.has_value());
 
   EXPECT_EQ(0, result2.error().code());
@@ -151,12 +151,12 @@
   auto error_text = "test error"s;
   Result<void> result = Error() << error_text;
 
-  ASSERT_FALSE(result);
+  ASSERT_FALSE(result.ok());
   ASSERT_FALSE(result.has_value());
 
   Result<std::string> result2 = Error() << result.error();
 
-  ASSERT_FALSE(result2);
+  ASSERT_FALSE(result2.ok());
   ASSERT_FALSE(result2.has_value());
 
   EXPECT_EQ(0, result2.error().code());
@@ -171,12 +171,12 @@
 
   errno = 0;
 
-  ASSERT_FALSE(result);
+  ASSERT_FALSE(result.ok());
   ASSERT_FALSE(result.has_value());
 
   Result<std::string> result2 = Error() << result.error();
 
-  ASSERT_FALSE(result2);
+  ASSERT_FALSE(result2.ok());
   ASSERT_FALSE(result2.has_value());
 
   EXPECT_EQ(test_errno, result2.error().code());
@@ -186,7 +186,7 @@
 TEST(result, constructor_forwarding) {
   auto result = Result<std::string>(std::in_place, 5, 'a');
 
-  ASSERT_TRUE(result);
+  ASSERT_RESULT_OK(result);
   ASSERT_TRUE(result.has_value());
 
   EXPECT_EQ("aaaaa", *result);
@@ -254,7 +254,7 @@
   // are called.
 
   auto result1 = ReturnConstructorTracker("");
-  ASSERT_TRUE(result1);
+  ASSERT_RESULT_OK(result1);
   EXPECT_EQ("literal string", result1->string);
   EXPECT_EQ(1U, ConstructorTracker::constructor_called);
   EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
@@ -263,7 +263,7 @@
   EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
 
   auto result2 = ReturnConstructorTracker("test2");
-  ASSERT_TRUE(result2);
+  ASSERT_RESULT_OK(result2);
   EXPECT_EQ("test2test22", result2->string);
   EXPECT_EQ(2U, ConstructorTracker::constructor_called);
   EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
@@ -272,7 +272,7 @@
   EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
 
   auto result3 = ReturnConstructorTracker("test3");
-  ASSERT_TRUE(result3);
+  ASSERT_RESULT_OK(result3);
   EXPECT_EQ("test3 test3", result3->string);
   EXPECT_EQ(3U, ConstructorTracker::constructor_called);
   EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
@@ -287,11 +287,11 @@
 TEST(result, result_result_with_success) {
   auto return_result_result_with_success = []() -> Result<Result<void>> { return Result<void>(); };
   auto result = return_result_result_with_success();
-  ASSERT_TRUE(result);
-  ASSERT_TRUE(*result);
+  ASSERT_RESULT_OK(result);
+  ASSERT_RESULT_OK(*result);
 
   auto inner_result = result.value();
-  ASSERT_TRUE(inner_result);
+  ASSERT_RESULT_OK(inner_result);
 }
 
 TEST(result, result_result_with_failure) {
@@ -299,8 +299,8 @@
     return Result<void>(ResultError("failure string", 6));
   };
   auto result = return_result_result_with_error();
-  ASSERT_TRUE(result);
-  ASSERT_FALSE(*result);
+  ASSERT_RESULT_OK(result);
+  ASSERT_FALSE(result->ok());
   EXPECT_EQ("failure string", (*result).error().message());
   EXPECT_EQ(6, (*result).error().code());
 }
@@ -320,7 +320,7 @@
   };
 
   auto result = return_test_struct();
-  ASSERT_TRUE(result);
+  ASSERT_RESULT_OK(result);
   EXPECT_EQ(36, result->value_);
 }
 
@@ -344,13 +344,13 @@
   errno = 1;
   int old_errno = errno;
   Result<int> result = Error() << "Failed" << SetErrnoToTwo<char>;
-  ASSERT_FALSE(result);
+  ASSERT_FALSE(result.ok());
   EXPECT_EQ(old_errno, errno);
 
   errno = 1;
   old_errno = errno;
   Result<int> result2 = ErrnoError() << "Failed" << SetErrnoToTwo<char>;
-  ASSERT_FALSE(result2);
+  ASSERT_FALSE(result2.ok());
   EXPECT_EQ(old_errno, errno);
   EXPECT_EQ(old_errno, result2.error().code());
 }
@@ -362,7 +362,7 @@
   result = Errorf("{} {}!", std::string("hello"), std::string("world"));
   EXPECT_EQ("hello world!", result.error().message());
 
-  result = Errorf("{h} {w}!", fmt::arg("w", "world"), fmt::arg("h", "hello"));
+  result = Errorf("{1} {0}!", "world", "hello");
   EXPECT_EQ("hello world!", result.error().message());
 
   result = Errorf("hello world!");
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
index 5e2d171..edff26d 100644
--- a/bootstat/Android.bp
+++ b/bootstat/Android.bp
@@ -30,8 +30,8 @@
         "libbase",
         "libcutils",
         "liblog",
-        "libmetricslogger",
     ],
+    static_libs: ["libgtest_prod"],
 }
 
 // bootstat static library
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index 8979b0c..2f2919f 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -7,7 +7,7 @@
 # - watch adb logcat -b all -d -s bootstat
 # - watch adb logcat -b all -d | audit2allow
 # - wait until screen is up, boot has completed, can mean wait for
-#   sys.boot_completed=1 and sys.logbootcomplete=1 to be true
+#   sys.boot_completed=1 and sys.bootstat.first_boot_completed=1 to be true
 #
 # All test frames, and nothing else, must be function names prefixed and
 # specifiged with the pattern 'test_<test>() {' as this is also how the
@@ -230,16 +230,16 @@
       if [ -n "`get_property sys.boot.reason`" ]
       then
         vals=`get_property |
-              sed -n 's/[[]sys[.]\(boot_completed\|logbootcomplete\)[]]: [[]\([01]\)[]]$/\1=\2/p'`
-        if [ "${vals}" = "`echo boot_completed=1 ; echo logbootcomplete=1`" ]
-        then
-          sleep 1
-          break
-        fi
-        if [ "${vals}" = "`echo logbootcomplete=1 ; echo boot_completed=1`" ]
-        then
-          sleep 1
-          break
+              sed -n 's/[[]sys[.]\(boot_completed\|logbootcomplete\|bootstat[.]first_boot_completed\)[]]: [[]\([01]\)[]]$/\1=\2/p'`
+        if [ X"${vals}" != X"${vals##*boot_completed=1}" ]; then
+          if [ X"${vals}" != X"${vals##*logbootcomple=1}" ]; then
+            sleep 1
+            break
+          fi
+          if [ X"${vals}" != X"${vals##*bootstat.first_boot_completed=1}" ]; then
+            sleep 1
+            break
+          fi
         fi
       fi
     fi
@@ -384,15 +384,15 @@
 init    : processing action (boot) from (/system/etc/init/bootstat.rc
 init    : processing action (ro.boot.bootreason=*) from (/system/etc/init/bootstat.rc
 init    : processing action (ro.boot.bootreason=* && post-fs) from (/system/etc/init/bootstat.rc
-init    : processing action (zygote-start) from (/system/etc/init/bootstat.rc
-init    : processing action (sys.boot_completed=1 && sys.logbootcomplete=1) from (/system/etc/init/bootstat.rc
+init    : processing action (sys.bootstat.first_zygote_start=0 && zygote-start) from (/system/etc/init/bootstat.rc
+init    : processing action (sys.boot_completed=1 && sys.bootstat.first_boot_completed=0) from (/system/etc/init/bootstat.rc
  (/system/bin/bootstat --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l)'
  (/system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l)'
  (/system/bin/bootstat -r post_decrypt_time_elapsed)'
-init    : Command 'exec - system log -- /system/bin/bootstat --record_boot_complete' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc:
-init    : Command 'exec - system log -- /system/bin/bootstat --record_boot_reason' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc:
-init    : Command 'exec - system log -- /system/bin/bootstat --record_time_since_factory_reset' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc:
-init    : Command 'exec_background - system log -- /system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc
+init    : Command 'exec - system log -- /system/bin/bootstat --record_boot_complete' action=sys.boot_completed=1 && sys.bootstat.first_boot_completed=0 (/system/etc/init/bootstat.rc:
+init    : Command 'exec - system log -- /system/bin/bootstat --record_boot_reason' action=sys.boot_completed=1 && sys.bootstat.first_boot_completed=0 (/system/etc/init/bootstat.rc:
+init    : Command 'exec - system log -- /system/bin/bootstat --record_time_since_factory_reset' action=sys.boot_completed=1 && sys.bootstat.first_boot_completed=0 (/system/etc/init/bootstat.rc:
+init    : Command 'exec_background - system log -- /system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l' action=sys.boot_completed=1 && sys.bootstat.first_boot_completed=0 (/system/etc/init/bootstat.rc
  (/system/bin/bootstat --record_boot_complete)'...
  (/system/bin/bootstat --record_boot_complete)' (pid${SPACE}
  (/system/bin/bootstat --record_boot_reason)'...
@@ -636,7 +636,7 @@
   rm -r ${ANDROID_PRODUCT_OUT}/obj/ETC/system_build_prop_intermediates ||
     true
   pushd ${ANDROID_BUILD_TOP} >&2
-  make -j50 >&2
+  build/soong/soong_ui.bash --make-mode >&2
   if [ ${?} != 0 ]; then
     popd >&2
     return 1
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index d08a59f..a9c1676 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -32,6 +32,8 @@
 #include <memory>
 #include <regex>
 #include <string>
+#include <string_view>
+#include <unordered_map>
 #include <utility>
 #include <vector>
 
@@ -44,21 +46,138 @@
 #include <android/log.h>
 #include <cutils/android_reboot.h>
 #include <cutils/properties.h>
-#include <metricslogger/metrics_logger.h>
 #include <statslog.h>
 
 #include "boot_event_record_store.h"
 
 namespace {
 
+struct AtomInfo {
+  int32_t atom;
+  int32_t event;
+};
+
+// Maps BootEvent used inside bootstat into statsd atom defined in
+// frameworks/base/cmds/statsd/src/atoms.proto.
+const std::unordered_map<std::string_view, AtomInfo> kBootEventToAtomInfo = {
+    // ELAPSED_TIME
+    {"ro.boottime.init",
+     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+      android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__ANDROID_INIT_STAGE_1}},
+    {"boot_complete",
+     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+      android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__BOOT_COMPLETE}},
+    {"boot_decryption_complete",
+     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+      android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__BOOT_COMPLETE_ENCRYPTION}},
+    {"boot_complete_no_encryption",
+     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+      android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__BOOT_COMPLETE_NO_ENCRYPTION}},
+    {"boot_complete_post_decrypt",
+     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+      android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__BOOT_COMPLETE_POST_DECRYPT}},
+    {"factory_reset_boot_complete",
+     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+      android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__FACTORY_RESET_BOOT_COMPLETE}},
+    {"factory_reset_boot_complete_no_encryption",
+     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+      android::util::
+          BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__FACTORY_RESET_BOOT_COMPLETE_NO_ENCRYPTION}},
+    {"factory_reset_boot_complete_post_decrypt",
+     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+      android::util::
+          BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__FACTORY_RESET_BOOT_COMPLETE_POST_DECRYPT}},
+    {"ota_boot_complete",
+     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+      android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__OTA_BOOT_COMPLETE}},
+    {"ota_boot_complete_no_encryption",
+     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+      android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__OTA_BOOT_COMPLETE_NO_ENCRYPTION}},
+    {"ota_boot_complete_post_decrypt",
+     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+      android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__OTA_BOOT_COMPLETE_POST_DECRYPT}},
+    {"post_decrypt_time_elapsed",
+     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+      android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__POST_DECRYPT}},
+    // DURATION
+    {"absolute_boot_time",
+     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,
+      android::util::BOOT_TIME_EVENT_DURATION__EVENT__ABSOLUTE_BOOT_TIME}},
+    {"boottime.bootloader.1BLE",
+     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,
+      android::util::BOOT_TIME_EVENT_DURATION__EVENT__BOOTLOADER_FIRST_STAGE_EXEC}},
+    {"boottime.bootloader.1BLL",
+     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,
+      android::util::BOOT_TIME_EVENT_DURATION__EVENT__BOOTLOADER_FIRST_STAGE_LOAD}},
+    {"boottime.bootloader.KL",
+     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,
+      android::util::BOOT_TIME_EVENT_DURATION__EVENT__BOOTLOADER_KERNEL_LOAD}},
+    {"boottime.bootloader.2BLE",
+     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,
+      android::util::BOOT_TIME_EVENT_DURATION__EVENT__BOOTLOADER_SECOND_STAGE_EXEC}},
+    {"boottime.bootloader.2BLL",
+     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,
+      android::util::BOOT_TIME_EVENT_DURATION__EVENT__BOOTLOADER_SECOND_STAGE_LOAD}},
+    {"boottime.bootloader.SW",
+     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,
+      android::util::BOOT_TIME_EVENT_DURATION__EVENT__BOOTLOADER_UI_WAIT}},
+    {"boottime.bootloader.total",
+     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,
+      android::util::BOOT_TIME_EVENT_DURATION__EVENT__BOOTLOADER_TOTAL}},
+    {"boottime.init.cold_boot_wait",
+     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,
+      android::util::BOOT_TIME_EVENT_DURATION__EVENT__COLDBOOT_WAIT}},
+    {"time_since_factory_reset",
+     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,
+      android::util::BOOT_TIME_EVENT_DURATION__EVENT__FACTORY_RESET_TIME_SINCE_RESET}},
+    {"ro.boottime.init.first_stage",
+     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,
+      android::util::BOOT_TIME_EVENT_DURATION__EVENT__ANDROID_INIT_STAGE_1}},
+    {"ro.boottime.init.selinux",
+     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,
+      android::util::BOOT_TIME_EVENT_DURATION__EVENT__SELINUX_INIT}},
+    // UTC_TIME
+    {"factory_reset",
+     {android::util::BOOT_TIME_EVENT_UTC_TIME_REPORTED,
+      android::util::BOOT_TIME_EVENT_UTC_TIME__EVENT__FACTORY_RESET_RESET_TIME}},
+    {"factory_reset_current_time",
+     {android::util::BOOT_TIME_EVENT_UTC_TIME_REPORTED,
+      android::util::BOOT_TIME_EVENT_UTC_TIME__EVENT__FACTORY_RESET_CURRENT_TIME}},
+    {"factory_reset_record_value",
+     {android::util::BOOT_TIME_EVENT_UTC_TIME_REPORTED,
+      android::util::BOOT_TIME_EVENT_UTC_TIME__EVENT__FACTORY_RESET_RECORD_VALUE}},
+    // ERROR_CODE
+    {"factory_reset_current_time_failure",
+     {android::util::BOOT_TIME_EVENT_ERROR_CODE_REPORTED,
+      android::util::BOOT_TIME_EVENT_ERROR_CODE__EVENT__FACTORY_RESET_CURRENT_TIME_FAILURE}},
+};
+
 // Scans the boot event record store for record files and logs each boot event
 // via EventLog.
 void LogBootEvents() {
   BootEventRecordStore boot_event_store;
-
   auto events = boot_event_store.GetAllBootEvents();
-  for (auto i = events.cbegin(); i != events.cend(); ++i) {
-    android::metricslogger::LogHistogram(i->first, i->second);
+  std::vector<std::string_view> notSupportedEvents;
+  for (const auto& event : events) {
+    const auto& name = event.first;
+    const auto& info = kBootEventToAtomInfo.find(name);
+    if (info != kBootEventToAtomInfo.end()) {
+      if (info->second.atom == android::util::BOOT_TIME_EVENT_ERROR_CODE_REPORTED) {
+        android::util::stats_write(static_cast<int32_t>(info->second.atom),
+                                   static_cast<int32_t>(info->second.event),
+                                   static_cast<int32_t>(event.second));
+      } else {
+        android::util::stats_write(static_cast<int32_t>(info->second.atom),
+                                   static_cast<int32_t>(info->second.event),
+                                   static_cast<int64_t>(event.second));
+      }
+    } else {
+      notSupportedEvents.push_back(name);
+    }
+  }
+  if (!notSupportedEvents.empty()) {
+    LOG(WARNING) << "LogBootEvents, atomInfo not defined for events:"
+                 << android::base::Join(notSupportedEvents, ',');
   }
 }
 
@@ -312,6 +431,11 @@
     {"reboot,unknown[0-9]*", 183},
     {"reboot,longkey,.*", 184},
     {"reboot,boringssl-self-check-failed", 185},
+    {"reboot,userspace_failed,shutdown_aborted", 186},
+    {"reboot,userspace_failed,watchdog_triggered", 187},
+    {"reboot,userspace_failed,watchdog_fork", 188},
+    {"reboot,userspace_failed,*", 189},
+    {"reboot,mount_userdata_failed", 190},
 };
 
 // Converts a string value representing the reason the system booted to an
@@ -746,6 +870,7 @@
 
 const char system_reboot_reason_property[] = "sys.boot.reason";
 const char last_reboot_reason_property[] = LAST_REBOOT_REASON_PROPERTY;
+const char last_reboot_reason_file[] = LAST_REBOOT_REASON_FILE;
 const char last_last_reboot_reason_property[] = "sys.boot.reason.last";
 constexpr size_t history_reboot_reason_size = 4;
 const char history_reboot_reason_property[] = LAST_REBOOT_REASON_PROPERTY ".history";
@@ -935,7 +1060,9 @@
     if (isBluntRebootReason(ret)) {
       // Content buffer no longer will have console data. Beware if more
       // checks added below, that depend on parsing console content.
-      content = android::base::GetProperty(last_reboot_reason_property, "");
+      if (!android::base::ReadFileToString(last_reboot_reason_file, &content)) {
+        content = android::base::GetProperty(last_reboot_reason_property, "");
+      }
       transformReason(content);
 
       // Anything in last is better than 'super-blunt' reboot or shutdown.
@@ -1109,7 +1236,10 @@
   // Record the scrubbed system_boot_reason to the property
   BootReasonAddToHistory(system_boot_reason);
   // Shift last_reboot_reason_property to last_last_reboot_reason_property
-  auto last_boot_reason = android::base::GetProperty(last_reboot_reason_property, "");
+  std::string last_boot_reason;
+  if (!android::base::ReadFileToString(last_reboot_reason_file, &last_boot_reason)) {
+    last_boot_reason = android::base::GetProperty(last_reboot_reason_property, "");
+  }
   if (last_boot_reason.empty() || isKernelRebootReason(system_boot_reason)) {
     last_boot_reason = system_boot_reason;
   } else {
@@ -1117,6 +1247,7 @@
   }
   android::base::SetProperty(last_last_reboot_reason_property, last_boot_reason);
   android::base::SetProperty(last_reboot_reason_property, "");
+  unlink(last_reboot_reason_file);
 }
 
 // Gets the boot time offset. This is useful when Android is running in a
@@ -1207,13 +1338,17 @@
   const auto reason = android::base::GetProperty(bootloader_reboot_reason_property, "");
 
   if (reason.empty()) {
+    // TODO(b/148575354): Replace with statsd.
     // Log an empty boot reason value as '<EMPTY>' to ensure the value is intentional
     // (and not corruption anywhere else in the reporting pipeline).
-    android::metricslogger::LogMultiAction(android::metricslogger::ACTION_BOOT,
-                                           android::metricslogger::FIELD_PLATFORM_REASON, "<EMPTY>");
+    // android::metricslogger::LogMultiAction(android::metricslogger::ACTION_BOOT,
+    //                                        android::metricslogger::FIELD_PLATFORM_REASON,
+    //                                        "<EMPTY>");
   } else {
-    android::metricslogger::LogMultiAction(android::metricslogger::ACTION_BOOT,
-                                           android::metricslogger::FIELD_PLATFORM_REASON, reason);
+    // TODO(b/148575354): Replace with statsd.
+    // android::metricslogger::LogMultiAction(android::metricslogger::ACTION_BOOT,
+    //                                        android::metricslogger::FIELD_PLATFORM_REASON,
+    //                                        reason);
   }
 
   // Log the raw bootloader_boot_reason property value.
@@ -1242,8 +1377,12 @@
 
   if (current_time_utc < 0) {
     // UMA does not display negative values in buckets, so convert to positive.
-    android::metricslogger::LogHistogram("factory_reset_current_time_failure",
-                                         std::abs(current_time_utc));
+    // Logging via BootEventRecordStore.
+    android::util::stats_write(
+        static_cast<int32_t>(android::util::BOOT_TIME_EVENT_ERROR_CODE_REPORTED),
+        static_cast<int32_t>(
+            android::util::BOOT_TIME_EVENT_ERROR_CODE__EVENT__FACTORY_RESET_CURRENT_TIME_FAILURE),
+        static_cast<int32_t>(std::abs(current_time_utc)));
 
     // Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram
     // is losing records somehow.
@@ -1251,7 +1390,11 @@
                                            std::abs(current_time_utc));
     return;
   } else {
-    android::metricslogger::LogHistogram("factory_reset_current_time", current_time_utc);
+    android::util::stats_write(
+        static_cast<int32_t>(android::util::BOOT_TIME_EVENT_UTC_TIME_REPORTED),
+        static_cast<int32_t>(
+            android::util::BOOT_TIME_EVENT_UTC_TIME__EVENT__FACTORY_RESET_CURRENT_TIME),
+        static_cast<int64_t>(current_time_utc));
 
     // Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram
     // is losing records somehow.
@@ -1271,7 +1414,11 @@
   // Calculate and record the difference in time between now and the
   // factory_reset time.
   time_t factory_reset_utc = record.second;
-  android::metricslogger::LogHistogram("factory_reset_record_value", factory_reset_utc);
+  android::util::stats_write(
+      static_cast<int32_t>(android::util::BOOT_TIME_EVENT_UTC_TIME_REPORTED),
+      static_cast<int32_t>(
+          android::util::BOOT_TIME_EVENT_UTC_TIME__EVENT__FACTORY_RESET_RECORD_VALUE),
+      static_cast<int64_t>(factory_reset_utc));
 
   // Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram
   // is losing records somehow.
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index 85caf25..a350fe7 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -50,34 +50,34 @@
 on post-fs-data && property:init.svc.bootanim=running && property:ro.crypto.type=block
     exec_background - system log -- /system/bin/bootstat -r post_decrypt_time_elapsed
 
-# sys.logbootcomplete is a signal to enable the bootstat logging mechanism.
-# This signaling is necessary to prevent logging boot metrics after a runtime
-# restart (e.g., adb shell stop && adb shell start).  /proc/uptime is not reset
-# during a runtime restart, which leads to false boot time metrics being reported.
+# Initialize bootstat state machine.
 #
-# The 'on boot' event occurs once per hard boot (device power on), which
-# switches the flag on. If the device performs a runtime restart, the flag is
-# switched off and cannot be switched on until the device hard boots again.
-
-# Enable bootstat logging on boot.
-on boot
-    setprop sys.logbootcomplete 1
-
-# Disable further bootstat logging on a runtime restart. A runtime restart is
-# signaled by the zygote stopping.
-on property:init.svc.zygote=stopping
-    setprop sys.logbootcomplete 0
+# sys.bootstat.first_boot_completed: responsible for making sure that record_boot_complete happens
+# only once per device hard reboot. Possible values:
+#
+#   sys.bootstat.first_boot_completed=0 - first boot completed trigger wasn't processed yet.
+#   sys.bootstat.first_boot_completed=1 - first boot completed trigger was processed and
+#                                         record_boot_complete was called. Subsequent boot completed
+#                                         triggers (e.g. due to userspace reboot) won't retrigger
+#                                         record_boot_complete
+#
+# IMPORTANT, ro.persistent_properties.ready=1 trigger is used here to ensure that we initialize
+# state machine only once, which as result ensures that bootstat --set_system_boot_reason and
+# bootstat --record_boot_complete will be called only once per full reboot.
+on property:ro.persistent_properties.ready=true
+  setprop sys.bootstat.first_boot_completed 0
 
 # Set boot reason
-on zygote-start
+on property:ro.persistent_properties.ready=true
     # Converts bootloader boot reason and persist.sys.boot.reason to system boot reason
     # Need go after persist peroperties are loaded which is right before zygote-start trigger
     exec_background - system log -- /system/bin/bootstat --set_system_boot_reason
 
 # Record boot complete metrics.
-on property:sys.boot_completed=1 && property:sys.logbootcomplete=1
+on property:sys.boot_completed=1 && property:sys.bootstat.first_boot_completed=0
     # Record boot_complete and related stats (decryption, etc).
     # Record the boot reason.
     # Record time since factory reset.
     # Log all boot events.
     exec_background - system log -- /system/bin/bootstat --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l
+    setprop sys.bootstat.first_boot_completed 1
diff --git a/code_coverage/Android.bp b/code_coverage/Android.bp
new file mode 100644
index 0000000..b51c802
--- /dev/null
+++ b/code_coverage/Android.bp
@@ -0,0 +1,83 @@
+
+prebuilt_etc {
+    name: "code_coverage.policy",
+    sub_dir: "seccomp_policy",
+    filename_from_src: true,
+    arch: {
+        arm: {
+            src: "empty_policy/code_coverage.arm.policy",
+            product_variables: {
+                native_coverage: {
+                    src: "seccomp_policy/code_coverage.arm.policy",
+                },
+            },
+        },
+        arm64: {
+            src: "empty_policy/code_coverage.arm64.policy",
+            product_variables: {
+                native_coverage: {
+                    src: "seccomp_policy/code_coverage.arm64.policy",
+                },
+            },
+        },
+        x86: {
+            src: "empty_policy/code_coverage.x86.policy",
+            product_variables: {
+                native_coverage: {
+                    src: "seccomp_policy/code_coverage.x86.policy",
+                },
+            },
+        },
+        x86_64: {
+            src: "empty_policy/code_coverage.x86_64.policy",
+            product_variables: {
+                native_coverage: {
+                    src: "seccomp_policy/code_coverage.x86_64.policy",
+                },
+            },
+        },
+    },
+    required: [
+        "code_coverage.policy.other",
+    ],
+}
+
+prebuilt_etc {
+    name: "code_coverage.policy.other",
+    sub_dir: "seccomp_policy",
+    filename_from_src: true,
+    arch: {
+        arm: {
+            src: "empty_policy/code_coverage.arm64.policy",
+            product_variables: {
+                native_coverage: {
+                    src: "seccomp_policy/code_coverage.arm64.policy",
+                },
+            },
+        },
+        arm64: {
+            src: "empty_policy/code_coverage.arm.policy",
+            product_variables: {
+                native_coverage: {
+                    src: "seccomp_policy/code_coverage.arm.policy",
+                },
+            },
+        },
+        x86: {
+            src: "empty_policy/code_coverage.x86_64.policy",
+            product_variables: {
+                native_coverage: {
+                    src: "seccomp_policy/code_coverage.x86_64.policy",
+                },
+            },
+        },
+        x86_64: {
+            src: "empty_policy/code_coverage.x86.policy",
+            product_variables: {
+                native_coverage: {
+                    src: "seccomp_policy/code_coverage.x86.policy",
+                },
+            },
+        },
+    },
+}
diff --git a/code_coverage/Android.mk b/code_coverage/Android.mk
deleted file mode 100644
index 80ab36b..0000000
--- a/code_coverage/Android.mk
+++ /dev/null
@@ -1,37 +0,0 @@
-# policies to allow processes inside minijail to dump code coverage information
-#
-
-LOCAL_PATH := $(call my-dir)
-
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := code_coverage.policy
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MULTILIB := both
-
-ifeq ($(TARGET_ARCH), $(filter $(TARGET_ARCH), arm arm64))
-LOCAL_MODULE_STEM_32 := code_coverage.arm.policy
-LOCAL_MODULE_STEM_64 := code_coverage.arm64.policy
-endif
-
-ifeq ($(TARGET_ARCH), $(filter $(TARGET_ARCH), x86 x86_64))
-LOCAL_MODULE_STEM_32 := code_coverage.x86.policy
-LOCAL_MODULE_STEM_64 := code_coverage.x86_64.policy
-endif
-
-# different files for different configurations
-ifeq ($(NATIVE_COVERAGE),true)
-LOCAL_SRC_FILES_arm := seccomp_policy/code_coverage.arm.policy
-LOCAL_SRC_FILES_arm64 := seccomp_policy/code_coverage.arm64.policy
-LOCAL_SRC_FILES_x86 := seccomp_policy/code_coverage.x86.policy
-LOCAL_SRC_FILES_x86_64 := seccomp_policy/code_coverage.x86_64.policy
-else
-LOCAL_SRC_FILES_arm := empty_policy/code_coverage.arm.policy
-LOCAL_SRC_FILES_arm64 := empty_policy/code_coverage.arm64.policy
-LOCAL_SRC_FILES_x86 := empty_policy/code_coverage.x86.policy
-LOCAL_SRC_FILES_x86_64 := empty_policy/code_coverage.x86_64.policy
-endif
-
-LOCAL_MODULE_TARGET_ARCH := arm arm64 x86 x86_64
-LOCAL_MODULE_PATH := $(TARGET_OUT)/etc/seccomp_policy
-include $(BUILD_PREBUILT)
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 0602e0a..f28c778 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -74,6 +74,7 @@
     header_libs: [
         "libbase_headers",
         "libdebuggerd_common_headers",
+        "bionic_libc_platform_headers",
     ],
 
     whole_static_libs: [
@@ -92,6 +93,9 @@
     defaults: ["debuggerd_defaults"],
     srcs: ["handler/debuggerd_fallback_nop.cpp"],
 
+    header_libs: ["bionic_libc_platform_headers"],
+    export_header_lib_headers: ["bionic_libc_platform_headers"],
+
     whole_static_libs: [
         "libdebuggerd_handler_core",
     ],
@@ -119,6 +123,10 @@
         "liblzma",
         "libcutils",
     ],
+
+    header_libs: ["bionic_libc_platform_headers"],
+    export_header_lib_headers: ["bionic_libc_platform_headers"],
+
     target: {
         recovery: {
             exclude_static_libs: [
@@ -138,15 +146,21 @@
         "util.cpp",
     ],
 
-    header_libs: ["libdebuggerd_common_headers"],
-
     shared_libs: [
         "libbase",
         "libcutils",
         "libprocinfo",
     ],
 
-    export_header_lib_headers: ["libdebuggerd_common_headers"],
+    header_libs: [
+        "libdebuggerd_common_headers",
+        "bionic_libc_platform_headers",
+    ],
+    export_header_lib_headers: [
+        "libdebuggerd_common_headers",
+        "bionic_libc_platform_headers",
+    ],
+
     export_include_dirs: ["include"],
 }
 
@@ -157,6 +171,7 @@
 
     srcs: [
         "libdebuggerd/backtrace.cpp",
+        "libdebuggerd/gwp_asan.cpp",
         "libdebuggerd/open_files_list.cpp",
         "libdebuggerd/tombstone.cpp",
         "libdebuggerd/utility.cpp",
@@ -167,6 +182,10 @@
 
     // Needed for private/bionic_fdsan.h
     include_dirs: ["bionic/libc"],
+    header_libs: [
+        "bionic_libc_platform_headers",
+        "gwp_asan_headers",
+    ],
 
     static_libs: [
         "libdexfile_support_static",  // libunwindstack dependency
@@ -176,6 +195,9 @@
         "libcutils",
         "liblog",
     ],
+
+    whole_static_libs: ["gwp_asan_crash_handler"],
+
     target: {
         recovery: {
             exclude_static_libs: [
@@ -230,6 +252,12 @@
 
     static_libs: [
         "libdebuggerd",
+        "libgmock",
+    ],
+
+    header_libs: [
+        "bionic_libc_platform_headers",
+        "gwp_asan_headers",
     ],
 
     local_include_dirs: [
@@ -277,6 +305,10 @@
         },
     },
 
+    header_libs: [
+        "bionic_libc_platform_headers",
+    ],
+
     static_libs: [
         "libtombstoned_client_static",
         "libdebuggerd",
@@ -317,7 +349,10 @@
     ],
     defaults: ["debuggerd_defaults"],
 
-    header_libs: ["libdebuggerd_common_headers"],
+    header_libs: [
+        "bionic_libc_platform_headers",
+        "libdebuggerd_common_headers"
+    ],
 
     static_libs: [
         "libbase",
@@ -329,6 +364,48 @@
     init_rc: ["tombstoned/tombstoned.rc"],
 }
 
-subdirs = [
-    "crasher",
-]
+prebuilt_etc {
+    name: "crash_dump.policy",
+    sub_dir: "seccomp_policy",
+    filename_from_src: true,
+    arch: {
+        arm: {
+            src: "seccomp_policy/crash_dump.arm.policy",
+        },
+        arm64: {
+            src: "seccomp_policy/crash_dump.arm64.policy",
+        },
+        x86: {
+            src: "seccomp_policy/crash_dump.x86.policy",
+        },
+        x86_64: {
+            src: "seccomp_policy/crash_dump.x86_64.policy",
+        },
+    },
+    required: [
+        "crash_dump.policy_other",
+    ],
+}
+
+
+// NB -- this installs "the other" architecture. (puts 32 bit config in on 64 bit device)
+// or at least that is the intention so that we get both of them populated
+prebuilt_etc {
+    name: "crash_dump.policy_other",
+    sub_dir: "seccomp_policy",
+    filename_from_src: true,
+    arch: {
+        arm: {
+            src: "seccomp_policy/crash_dump.arm64.policy",
+        },
+        arm64: {
+            src: "seccomp_policy/crash_dump.arm.policy",
+        },
+        x86: {
+            src: "seccomp_policy/crash_dump.x86_64.policy",
+        },
+        x86_64: {
+            src: "seccomp_policy/crash_dump.x86.policy",
+        },
+    },
+}
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk
deleted file mode 100644
index c03b41d..0000000
--- a/debuggerd/Android.mk
+++ /dev/null
@@ -1,24 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := crash_dump.policy
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MULTILIB := both
-
-ifeq ($(TARGET_ARCH), $(filter $(TARGET_ARCH), arm arm64))
-LOCAL_MODULE_STEM_32 := crash_dump.arm.policy
-LOCAL_MODULE_STEM_64 := crash_dump.arm64.policy
-endif
-
-ifeq ($(TARGET_ARCH), $(filter $(TARGET_ARCH), x86 x86_64))
-LOCAL_MODULE_STEM_32 := crash_dump.x86.policy
-LOCAL_MODULE_STEM_64 := crash_dump.x86_64.policy
-endif
-
-LOCAL_MODULE_PATH := $(TARGET_OUT)/etc/seccomp_policy
-LOCAL_SRC_FILES_arm := seccomp_policy/crash_dump.arm.policy
-LOCAL_SRC_FILES_arm64 := seccomp_policy/crash_dump.arm64.policy
-LOCAL_SRC_FILES_x86 := seccomp_policy/crash_dump.x86.policy
-LOCAL_SRC_FILES_x86_64 := seccomp_policy/crash_dump.x86_64.policy
-LOCAL_MODULE_TARGET_ARCH := arm arm64 x86 x86_64
-include $(BUILD_PREBUILT)
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 7e35a2f..5c02738 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -35,6 +35,7 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
+#include <bionic/reserved_signals.h>
 #include <cutils/sockets.h>
 #include <procinfo/process.h>
 
@@ -50,7 +51,7 @@
 using android::base::WriteStringToFd;
 
 static bool send_signal(pid_t pid, const DebuggerdDumpType dump_type) {
-  const int signal = (dump_type == kDebuggerdJavaBacktrace) ? SIGQUIT : DEBUGGER_SIGNAL;
+  const int signal = (dump_type == kDebuggerdJavaBacktrace) ? SIGQUIT : BIONIC_SIGNAL_DEBUGGER;
   sigval val;
   val.sival_int = (dump_type == kDebuggerdNativeBacktrace) ? 1 : 0;
 
diff --git a/debuggerd/client/debuggerd_client_test.cpp b/debuggerd/client/debuggerd_client_test.cpp
index 2545cd6..ebb8d86 100644
--- a/debuggerd/client/debuggerd_client_test.cpp
+++ b/debuggerd/client/debuggerd_client_test.cpp
@@ -73,8 +73,8 @@
   unique_fd pipe_read, pipe_write;
   ASSERT_TRUE(Pipe(&pipe_read, &pipe_write));
 
-  // 64 MiB should be enough for everyone.
-  constexpr int PIPE_SIZE = 64 * 1024 * 1024;
+  // 16 MiB should be enough for everyone.
+  constexpr int PIPE_SIZE = 16 * 1024 * 1024;
   ASSERT_EQ(PIPE_SIZE, fcntl(pipe_read.get(), F_SETPIPE_SZ, PIPE_SIZE));
 
   // Wait for a bit to let the child spawn all of its threads.
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index cb55745..3e99880 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -40,6 +40,7 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
+#include <bionic/reserved_signals.h>
 #include <cutils/sockets.h>
 #include <log/log.h>
 #include <private/android_filesystem_config.h>
@@ -254,7 +255,8 @@
 
 static void ReadCrashInfo(unique_fd& fd, siginfo_t* siginfo,
                           std::unique_ptr<unwindstack::Regs>* regs, uintptr_t* abort_msg_address,
-                          uintptr_t* fdsan_table_address) {
+                          uintptr_t* fdsan_table_address, uintptr_t* gwp_asan_state,
+                          uintptr_t* gwp_asan_metadata) {
   std::aligned_storage<sizeof(CrashInfo) + 1, alignof(CrashInfo)>::type buf;
   CrashInfo* crash_info = reinterpret_cast<CrashInfo*>(&buf);
   ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), &buf, sizeof(buf)));
@@ -271,6 +273,10 @@
         expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV2);
         break;
 
+      case 3:
+        expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV3);
+        break;
+
       default:
         LOG(FATAL) << "unexpected CrashInfo version: " << crash_info->header.version;
         break;
@@ -283,7 +289,13 @@
   }
 
   *fdsan_table_address = 0;
+  *gwp_asan_state = 0;
+  *gwp_asan_metadata = 0;
   switch (crash_info->header.version) {
+    case 3:
+      *gwp_asan_state = crash_info->data.v3.gwp_asan_state;
+      *gwp_asan_metadata = crash_info->data.v3.gwp_asan_metadata;
+      FALLTHROUGH_INTENDED;
     case 2:
       *fdsan_table_address = crash_info->data.v2.fdsan_table_address;
       FALLTHROUGH_INTENDED;
@@ -415,6 +427,8 @@
   DebuggerdDumpType dump_type;
   uintptr_t abort_msg_address = 0;
   uintptr_t fdsan_table_address = 0;
+  uintptr_t gwp_asan_state = 0;
+  uintptr_t gwp_asan_metadata = 0;
 
   Initialize(argv);
   ParseArgs(argc, argv, &pseudothread_tid, &dump_type);
@@ -476,7 +490,7 @@
       if (thread == g_target_thread) {
         // Read the thread's registers along with the rest of the crash info out of the pipe.
         ReadCrashInfo(input_pipe, &siginfo, &info.registers, &abort_msg_address,
-                      &fdsan_table_address);
+                      &fdsan_table_address, &gwp_asan_state, &gwp_asan_metadata);
         info.siginfo = &siginfo;
         info.signo = info.siginfo->si_signo;
       } else {
@@ -511,13 +525,13 @@
 
   // Defer the message until later, for readability.
   bool wait_for_gdb = android::base::GetBoolProperty("debug.debuggerd.wait_for_gdb", false);
-  if (siginfo.si_signo == DEBUGGER_SIGNAL) {
+  if (siginfo.si_signo == BIONIC_SIGNAL_DEBUGGER) {
     wait_for_gdb = false;
   }
 
   // Detach from all of our attached threads before resuming.
   for (const auto& [tid, thread] : thread_info) {
-    int resume_signal = thread.signo == DEBUGGER_SIGNAL ? 0 : thread.signo;
+    int resume_signal = thread.signo == BIONIC_SIGNAL_DEBUGGER ? 0 : thread.signo;
     if (wait_for_gdb) {
       resume_signal = 0;
       if (tgkill(target_process, tid, SIGSTOP) != 0) {
@@ -555,10 +569,10 @@
             << " (target tid = " << g_target_thread << ")";
 
   int signo = siginfo.si_signo;
-  bool fatal_signal = signo != DEBUGGER_SIGNAL;
+  bool fatal_signal = signo != BIONIC_SIGNAL_DEBUGGER;
   bool backtrace = false;
 
-  // si_value is special when used with DEBUGGER_SIGNAL.
+  // si_value is special when used with BIONIC_SIGNAL_DEBUGGER.
   //   0: dump tombstone
   //   1: dump backtrace
   if (!fatal_signal) {
@@ -591,7 +605,8 @@
     {
       ATRACE_NAME("engrave_tombstone");
       engrave_tombstone(std::move(g_output_fd), &unwinder, thread_info, g_target_thread,
-                        abort_msg_address, &open_files, &amfd_data);
+                        abort_msg_address, &open_files, &amfd_data, gwp_asan_state,
+                        gwp_asan_metadata);
     }
   }
 
diff --git a/debuggerd/crasher/Android.bp b/debuggerd/crasher/Android.bp
index 7bec470..e86f499 100644
--- a/debuggerd/crasher/Android.bp
+++ b/debuggerd/crasher/Android.bp
@@ -44,6 +44,7 @@
     name: "crasher",
 
     defaults: ["crasher-defaults"],
+    header_libs: ["bionic_libc_platform_headers"],
     shared_libs: [
         "libbase",
         "liblog",
@@ -65,6 +66,7 @@
     defaults: ["crasher-defaults"],
     cppflags: ["-DSTATIC_CRASHER"],
     static_executable: true,
+    header_libs: ["bionic_libc_platform_headers"],
     static_libs: [
         "libdebuggerd_handler",
         "libbase",
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 99729dc..6a8cc56 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -31,6 +31,7 @@
 
 #include <android/fdsan.h>
 #include <android/set_abort_message.h>
+#include <bionic/reserved_signals.h>
 
 #include <android-base/cmsg.h>
 #include <android-base/file.h>
@@ -398,7 +399,7 @@
   unique_fd output_fd;
   StartProcess([]() {
     android_set_abort_message("not actually aborting");
-    raise(DEBUGGER_SIGNAL);
+    raise(BIONIC_SIGNAL_DEBUGGER);
     exit(0);
   });
   StartIntercept(&output_fd);
@@ -466,7 +467,7 @@
 
   sigval val;
   val.sival_int = 1;
-  ASSERT_EQ(0, sigqueue(crasher_pid, DEBUGGER_SIGNAL, val)) << strerror(errno);
+  ASSERT_EQ(0, sigqueue(crasher_pid, BIONIC_SIGNAL_DEBUGGER, val)) << strerror(errno);
   FinishIntercept(&intercept_result);
   ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
   ConsumeFd(std::move(output_fd), &result);
@@ -734,7 +735,7 @@
 
   siginfo.si_value.sival_int = dump_type == kDebuggerdNativeBacktrace;
 
-  if (syscall(__NR_rt_tgsigqueueinfo, getpid(), gettid(), DEBUGGER_SIGNAL, &siginfo) != 0) {
+  if (syscall(__NR_rt_tgsigqueueinfo, getpid(), gettid(), BIONIC_SIGNAL_DEBUGGER, &siginfo) != 0) {
     PLOG(ERROR) << "libdebuggerd_client: failed to send signal to self";
     return false;
   }
@@ -887,7 +888,7 @@
       errx(2, "first waitpid returned %d (%s), expected failure with ECHILD", rc, strerror(errno));
     }
 
-    raise(DEBUGGER_SIGNAL);
+    raise(BIONIC_SIGNAL_DEBUGGER);
 
     errno = 0;
     rc = TEMP_FAILURE_RETRY(waitpid(-1, &status, __WALL | __WNOTHREAD));
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index bbec612..9bcbdb3 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -42,6 +42,7 @@
 #include <android-base/file.h>
 #include <android-base/unique_fd.h>
 #include <async_safe/log.h>
+#include <bionic/reserved_signals.h>
 #include <unwindstack/DexFiles.h>
 #include <unwindstack/JitDebug.h>
 #include <unwindstack/Maps.h>
@@ -272,7 +273,7 @@
         siginfo.si_pid = getpid();
         siginfo.si_uid = getuid();
 
-        if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, DEBUGGER_SIGNAL, &siginfo) != 0) {
+        if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, BIONIC_SIGNAL_DEBUGGER, &siginfo) != 0) {
           async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s",
                                 tid, strerror(errno));
           return false;
@@ -340,7 +341,7 @@
 
 extern "C" void debuggerd_fallback_handler(siginfo_t* info, ucontext_t* ucontext,
                                            void* abort_message) {
-  if (info->si_signo == DEBUGGER_SIGNAL && info->si_value.sival_ptr != nullptr) {
+  if (info->si_signo == BIONIC_SIGNAL_DEBUGGER && info->si_value.sival_ptr != nullptr) {
     return trace_handler(info, ucontext);
   } else {
     return crash_handler(info, ucontext, abort_message);
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index b90ca80..8b4b630 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -49,8 +49,10 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
+#include <android-base/macros.h>
 #include <android-base/unique_fd.h>
 #include <async_safe/log.h>
+#include <bionic/reserved_signals.h>
 #include <cutils/properties.h>
 
 #include <libdebuggerd/utility.h>
@@ -175,7 +177,7 @@
     thread_name[MAX_TASK_NAME_LEN] = 0;
   }
 
-  if (info->si_signo == DEBUGGER_SIGNAL) {
+  if (info->si_signo == BIONIC_SIGNAL_DEBUGGER) {
     async_safe_format_log(ANDROID_LOG_INFO, "libc", "Requested dump for tid %d (%s)", __gettid(),
                           thread_name);
     return;
@@ -297,6 +299,8 @@
   void* ucontext;
   uintptr_t abort_msg;
   uintptr_t fdsan_table;
+  uintptr_t gwp_asan_state;
+  uintptr_t gwp_asan_metadata;
 };
 
 // Logging and contacting debuggerd requires free file descriptors, which we might not have.
@@ -307,7 +311,7 @@
 static void* pseudothread_stack;
 
 static DebuggerdDumpType get_dump_type(const debugger_thread_info* thread_info) {
-  if (thread_info->siginfo->si_signo == DEBUGGER_SIGNAL &&
+  if (thread_info->siginfo->si_signo == BIONIC_SIGNAL_DEBUGGER &&
       thread_info->siginfo->si_value.sival_int) {
     return kDebuggerdNativeBacktrace;
   }
@@ -341,23 +345,25 @@
   }
 
   // ucontext_t is absurdly large on AArch64, so piece it together manually with writev.
-  uint32_t version = 2;
-  constexpr size_t expected = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV2);
+  uint32_t version = 3;
+  constexpr size_t expected = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV3);
 
   errno = 0;
   if (fcntl(output_write.get(), F_SETPIPE_SZ, expected) < static_cast<int>(expected)) {
     fatal_errno("failed to set pipe buffer size");
   }
 
-  struct iovec iovs[5] = {
+  struct iovec iovs[] = {
       {.iov_base = &version, .iov_len = sizeof(version)},
       {.iov_base = thread_info->siginfo, .iov_len = sizeof(siginfo_t)},
       {.iov_base = thread_info->ucontext, .iov_len = sizeof(ucontext_t)},
       {.iov_base = &thread_info->abort_msg, .iov_len = sizeof(uintptr_t)},
       {.iov_base = &thread_info->fdsan_table, .iov_len = sizeof(uintptr_t)},
+      {.iov_base = &thread_info->gwp_asan_state, .iov_len = sizeof(uintptr_t)},
+      {.iov_base = &thread_info->gwp_asan_metadata, .iov_len = sizeof(uintptr_t)},
   };
 
-  ssize_t rc = TEMP_FAILURE_RETRY(writev(output_write.get(), iovs, 5));
+  ssize_t rc = TEMP_FAILURE_RETRY(writev(output_write.get(), iovs, arraysize(iovs)));
   if (rc == -1) {
     fatal_errno("failed to write crash info");
   } else if (rc != expected) {
@@ -429,7 +435,7 @@
     async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper crashed or stopped");
   }
 
-  if (thread_info->siginfo->si_signo != DEBUGGER_SIGNAL) {
+  if (thread_info->siginfo->si_signo != BIONIC_SIGNAL_DEBUGGER) {
     // For crashes, we don't need to minimize pause latency.
     // Wait for the dump to complete before having the process exit, to avoid being murdered by
     // ActivityManager or init.
@@ -446,7 +452,7 @@
   // exited with the correct exit status (e.g. so that sh will report
   // "Segmentation fault" instead of "Killed"). For this to work, we need
   // to deregister our signal handler for that signal before continuing.
-  if (info->si_signo != DEBUGGER_SIGNAL) {
+  if (info->si_signo != BIONIC_SIGNAL_DEBUGGER) {
     signal(info->si_signo, SIG_DFL);
     int rc = syscall(SYS_rt_tgsigqueueinfo, __getpid(), __gettid(), info->si_signo, info);
     if (rc != 0) {
@@ -484,8 +490,10 @@
   }
 
   void* abort_message = nullptr;
+  const gwp_asan::AllocatorState* gwp_asan_state = nullptr;
+  const gwp_asan::AllocationMetadata* gwp_asan_metadata = nullptr;
   uintptr_t si_val = reinterpret_cast<uintptr_t>(info->si_ptr);
-  if (signal_number == DEBUGGER_SIGNAL) {
+  if (signal_number == BIONIC_SIGNAL_DEBUGGER) {
     if (info->si_code == SI_QUEUE && info->si_pid == __getpid()) {
       // Allow for the abort message to be explicitly specified via the sigqueue value.
       // Keep the bottom bit intact for representing whether we want a backtrace or a tombstone.
@@ -498,6 +506,12 @@
     if (g_callbacks.get_abort_message) {
       abort_message = g_callbacks.get_abort_message();
     }
+    if (g_callbacks.get_gwp_asan_state) {
+      gwp_asan_state = g_callbacks.get_gwp_asan_state();
+    }
+    if (g_callbacks.get_gwp_asan_metadata) {
+      gwp_asan_metadata = g_callbacks.get_gwp_asan_metadata();
+    }
   }
 
   // If sival_int is ~0, it means that the fallback handler has been called
@@ -531,6 +545,8 @@
       .ucontext = context,
       .abort_msg = reinterpret_cast<uintptr_t>(abort_message),
       .fdsan_table = reinterpret_cast<uintptr_t>(android_fdsan_get_fd_table()),
+      .gwp_asan_state = reinterpret_cast<uintptr_t>(gwp_asan_state),
+      .gwp_asan_metadata = reinterpret_cast<uintptr_t>(gwp_asan_metadata),
   };
 
   // Set PR_SET_DUMPABLE to 1, so that crash_dump can ptrace us.
@@ -576,7 +592,7 @@
     fatal_errno("failed to restore traceable");
   }
 
-  if (info->si_signo == DEBUGGER_SIGNAL) {
+  if (info->si_signo == BIONIC_SIGNAL_DEBUGGER) {
     // If the signal is fatal, don't unlock the mutex to prevent other crashing threads from
     // starting to dump right before our death.
     pthread_mutex_unlock(&crash_mutex);
@@ -591,19 +607,20 @@
     g_callbacks = *callbacks;
   }
 
-  void* thread_stack_allocation =
-    mmap(nullptr, PAGE_SIZE * 3, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+  size_t thread_stack_pages = 8;
+  void* thread_stack_allocation = mmap(nullptr, PAGE_SIZE * (thread_stack_pages + 2), PROT_NONE,
+                                       MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
   if (thread_stack_allocation == MAP_FAILED) {
     fatal_errno("failed to allocate debuggerd thread stack");
   }
 
   char* stack = static_cast<char*>(thread_stack_allocation) + PAGE_SIZE;
-  if (mprotect(stack, PAGE_SIZE, PROT_READ | PROT_WRITE) != 0) {
+  if (mprotect(stack, PAGE_SIZE * thread_stack_pages, PROT_READ | PROT_WRITE) != 0) {
     fatal_errno("failed to mprotect debuggerd thread stack");
   }
 
   // Stack grows negatively, set it to the last byte in the page...
-  stack = (stack + PAGE_SIZE - 1);
+  stack = (stack + thread_stack_pages * PAGE_SIZE - 1);
   // and align it.
   stack -= 15;
   pseudothread_stack = stack;
diff --git a/debuggerd/include/debuggerd/handler.h b/debuggerd/include/debuggerd/handler.h
index 7196e0a..4f24360 100644
--- a/debuggerd/include/debuggerd/handler.h
+++ b/debuggerd/include/debuggerd/handler.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <bionic/reserved_signals.h>
 #include <signal.h>
 #include <stdint.h>
 #include <sys/cdefs.h>
@@ -23,21 +24,30 @@
 
 __BEGIN_DECLS
 
+// Forward declare these classes so not everyone has to include GWP-ASan
+// headers.
+namespace gwp_asan {
+struct AllocatorState;
+struct AllocationMetadata;
+};  // namespace gwp_asan
+
 // These callbacks are called in a signal handler, and thus must be async signal safe.
 // If null, the callbacks will not be called.
 typedef struct {
   struct abort_msg_t* (*get_abort_message)();
   void (*post_dump)();
+  const struct gwp_asan::AllocatorState* (*get_gwp_asan_state)();
+  const struct gwp_asan::AllocationMetadata* (*get_gwp_asan_metadata)();
 } debuggerd_callbacks_t;
 
 void debuggerd_init(debuggerd_callbacks_t* callbacks);
 
 // DEBUGGER_ACTION_DUMP_TOMBSTONE and DEBUGGER_ACTION_DUMP_BACKTRACE are both
-// triggered via DEBUGGER_SIGNAL. The debugger_action_t is sent via si_value
+// triggered via BIONIC_SIGNAL_DEBUGGER. The debugger_action_t is sent via si_value
 // using sigqueue(2) or equivalent. If no si_value is specified (e.g. if the
 // signal is sent by kill(2)), the default behavior is to print the backtrace
 // to the log.
-#define DEBUGGER_SIGNAL (__SIGRTMIN + 3)
+#define DEBUGGER_SIGNAL BIONIC_SIGNAL_DEBUGGER
 
 static void __attribute__((__unused__)) debuggerd_register_handlers(struct sigaction* action) {
   sigaction(SIGABRT, action, nullptr);
@@ -50,7 +60,7 @@
 #endif
   sigaction(SIGSYS, action, nullptr);
   sigaction(SIGTRAP, action, nullptr);
-  sigaction(DEBUGGER_SIGNAL, action, nullptr);
+  sigaction(BIONIC_SIGNAL_DEBUGGER, action, nullptr);
 }
 
 __END_DECLS
diff --git a/debuggerd/libdebuggerd/gwp_asan.cpp b/debuggerd/libdebuggerd/gwp_asan.cpp
new file mode 100644
index 0000000..53df783
--- /dev/null
+++ b/debuggerd/libdebuggerd/gwp_asan.cpp
@@ -0,0 +1,273 @@
+/*
+ * 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 "libdebuggerd/gwp_asan.h"
+#include "libdebuggerd/utility.h"
+
+#include "gwp_asan/common.h"
+#include "gwp_asan/crash_handler.h"
+
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/Unwinder.h>
+
+// Retrieve GWP-ASan state from `state_addr` inside the process at
+// `process_memory`. Place the state into `*state`.
+static bool retrieve_gwp_asan_state(unwindstack::Memory* process_memory, uintptr_t state_addr,
+                                    gwp_asan::AllocatorState* state) {
+  return process_memory->ReadFully(state_addr, state, sizeof(*state));
+}
+
+// Retrieve the GWP-ASan metadata pool from `metadata_addr` inside the process
+// at `process_memory`. The number of metadata slots is retrieved from the
+// allocator state provided. This function returns a heap-allocated copy of the
+// metadata pool whose ownership should be managed by the caller. Returns
+// nullptr on failure.
+static const gwp_asan::AllocationMetadata* retrieve_gwp_asan_metadata(
+    unwindstack::Memory* process_memory, const gwp_asan::AllocatorState& state,
+    uintptr_t metadata_addr) {
+  if (state.MaxSimultaneousAllocations > 1024) {
+    ALOGE(
+        "Error when retrieving GWP-ASan metadata, MSA from state (%zu) "
+        "exceeds maximum allowed (1024).",
+        state.MaxSimultaneousAllocations);
+    return nullptr;
+  }
+
+  gwp_asan::AllocationMetadata* meta =
+      new gwp_asan::AllocationMetadata[state.MaxSimultaneousAllocations];
+  if (!process_memory->ReadFully(metadata_addr, meta,
+                                 sizeof(*meta) * state.MaxSimultaneousAllocations)) {
+    ALOGE(
+        "Error when retrieving GWP-ASan metadata, could not retrieve %zu "
+        "pieces of metadata.",
+        state.MaxSimultaneousAllocations);
+    delete[] meta;
+    meta = nullptr;
+  }
+  return meta;
+}
+
+GwpAsanCrashData::GwpAsanCrashData(unwindstack::Memory* process_memory,
+                                   uintptr_t gwp_asan_state_ptr, uintptr_t gwp_asan_metadata_ptr,
+                                   const ThreadInfo& thread_info) {
+  if (!process_memory || !gwp_asan_metadata_ptr || !gwp_asan_state_ptr) return;
+  // Extract the GWP-ASan regions from the dead process.
+  if (!retrieve_gwp_asan_state(process_memory, gwp_asan_state_ptr, &state_)) return;
+  metadata_.reset(retrieve_gwp_asan_metadata(process_memory, state_, gwp_asan_metadata_ptr));
+  if (!metadata_.get()) return;
+
+  // Get the external crash address from the thread info.
+  crash_address_ = 0u;
+  if (signal_has_si_addr(thread_info.siginfo)) {
+    crash_address_ = reinterpret_cast<uintptr_t>(thread_info.siginfo->si_addr);
+  }
+
+  // Ensure the error belongs to GWP-ASan.
+  if (!__gwp_asan_error_is_mine(&state_, crash_address_)) return;
+
+  is_gwp_asan_responsible_ = true;
+  thread_id_ = thread_info.tid;
+
+  // Grab the internal error address, if it exists.
+  uintptr_t internal_crash_address = __gwp_asan_get_internal_crash_address(&state_);
+  if (internal_crash_address) {
+    crash_address_ = internal_crash_address;
+  }
+
+  // Get other information from the internal state.
+  error_ = __gwp_asan_diagnose_error(&state_, metadata_.get(), crash_address_);
+  error_string_ = gwp_asan::ErrorToString(error_);
+  responsible_allocation_ = __gwp_asan_get_metadata(&state_, metadata_.get(), crash_address_);
+}
+
+bool GwpAsanCrashData::CrashIsMine() const {
+  return is_gwp_asan_responsible_;
+}
+
+void GwpAsanCrashData::DumpCause(log_t* log) const {
+  if (!CrashIsMine()) {
+    ALOGE("Internal Error: DumpCause() on a non-GWP-ASan crash.");
+    return;
+  }
+
+  if (error_ == gwp_asan::Error::UNKNOWN) {
+    _LOG(log, logtype::HEADER, "Cause: [GWP-ASan]: Unknown error occurred at 0x%" PRIxPTR ".\n",
+         crash_address_);
+    return;
+  }
+
+  if (!responsible_allocation_) {
+    _LOG(log, logtype::HEADER, "Cause: [GWP-ASan]: %s at 0x%" PRIxPTR ".\n", error_string_,
+         crash_address_);
+    return;
+  }
+
+  uintptr_t alloc_address = __gwp_asan_get_allocation_address(responsible_allocation_);
+  size_t alloc_size = __gwp_asan_get_allocation_size(responsible_allocation_);
+
+  if (crash_address_ == alloc_address) {
+    // Use After Free on a 41-byte allocation at 0xdeadbeef.
+    _LOG(log, logtype::HEADER, "Cause: [GWP-ASan]: %s on a %zu-byte allocation at 0x%" PRIxPTR "\n",
+         error_string_, alloc_size, alloc_address);
+    return;
+  }
+
+  uintptr_t diff;
+  const char* location_str;
+
+  if (crash_address_ < alloc_address) {
+    // Buffer Underflow, 6 bytes left of a 41-byte allocation at 0xdeadbeef.
+    location_str = "left of";
+    diff = alloc_address - crash_address_;
+  } else if (crash_address_ - alloc_address < alloc_size) {
+    // Use After Free, 40 bytes into a 41-byte allocation at 0xdeadbeef.
+    location_str = "into";
+    diff = crash_address_ - alloc_address;
+  } else {
+    // Buffer Overflow, 6 bytes right of a 41-byte allocation at 0xdeadbeef, or
+    // Invalid Free, 47 bytes right of a 41-byte allocation at 0xdeadbeef.
+    location_str = "right of";
+    diff = crash_address_ - alloc_address;
+    if (error_ == gwp_asan::Error::BUFFER_OVERFLOW) {
+      diff -= alloc_size;
+    }
+  }
+
+  // Suffix of 'bytes', i.e. 4 bytes' vs. '1 byte'.
+  const char* byte_suffix = "s";
+  if (diff == 1) {
+    byte_suffix = "";
+  }
+  _LOG(log, logtype::HEADER,
+       "Cause: [GWP-ASan]: %s, %" PRIuPTR " byte%s %s a %zu-byte allocation at 0x%" PRIxPTR "\n",
+       error_string_, diff, byte_suffix, location_str, alloc_size, alloc_address);
+}
+
+// Build a frame for symbolization using the maps from the provided unwinder.
+// The constructed frame contains just enough information to be used to
+// symbolize a GWP-ASan stack trace.
+static unwindstack::FrameData BuildFrame(unwindstack::Unwinder* unwinder, uintptr_t pc,
+                                         size_t frame_num) {
+  unwindstack::FrameData frame;
+  frame.num = frame_num;
+
+  unwindstack::Maps* maps = unwinder->GetMaps();
+  unwindstack::MapInfo* map_info = maps->Find(pc);
+  if (!map_info) {
+    frame.rel_pc = pc;
+    return frame;
+  }
+
+  unwindstack::Elf* elf =
+      map_info->GetElf(unwinder->GetProcessMemory(), unwindstack::Regs::CurrentArch());
+
+  uint64_t relative_pc = elf->GetRelPc(pc, map_info);
+
+  // Create registers just to get PC adjustment. Doesn't matter what they point
+  // to.
+  unwindstack::Regs* regs = unwindstack::Regs::CreateFromLocal();
+  uint64_t pc_adjustment = regs->GetPcAdjustment(relative_pc, elf);
+  relative_pc -= pc_adjustment;
+  // The debug PC may be different if the PC comes from the JIT.
+  uint64_t debug_pc = relative_pc;
+
+  // If we don't have a valid ELF file, check the JIT.
+  if (!elf->valid()) {
+    unwindstack::JitDebug jit_debug(unwinder->GetProcessMemory());
+    uint64_t jit_pc = pc - pc_adjustment;
+    unwindstack::Elf* jit_elf = jit_debug.GetElf(maps, jit_pc);
+    if (jit_elf != nullptr) {
+      debug_pc = jit_pc;
+      elf = jit_elf;
+    }
+  }
+
+  // Copy all the things we need into the frame for symbolization.
+  frame.rel_pc = relative_pc;
+  frame.pc = pc - pc_adjustment;
+  frame.map_name = map_info->name;
+  frame.map_elf_start_offset = map_info->elf_start_offset;
+  frame.map_exact_offset = map_info->offset;
+  frame.map_start = map_info->start;
+  frame.map_end = map_info->end;
+  frame.map_flags = map_info->flags;
+  frame.map_load_bias = elf->GetLoadBias();
+
+  if (!elf->GetFunctionName(relative_pc, &frame.function_name, &frame.function_offset)) {
+    frame.function_name = "";
+    frame.function_offset = 0;
+  }
+  return frame;
+}
+
+constexpr size_t kMaxTraceLength = gwp_asan::AllocationMetadata::kMaxTraceLengthToCollect;
+
+bool GwpAsanCrashData::HasDeallocationTrace() const {
+  assert(CrashIsMine() && "HasDeallocationTrace(): Crash is not mine!");
+  if (!responsible_allocation_ || !__gwp_asan_is_deallocated(responsible_allocation_)) {
+    return false;
+  }
+  return true;
+}
+
+void GwpAsanCrashData::DumpDeallocationTrace(log_t* log, unwindstack::Unwinder* unwinder) const {
+  assert(HasDeallocationTrace() && "DumpDeallocationTrace(): No dealloc trace!");
+  uint64_t thread_id = __gwp_asan_get_deallocation_thread_id(responsible_allocation_);
+
+  std::unique_ptr<uintptr_t> frames(new uintptr_t[kMaxTraceLength]);
+  size_t num_frames =
+      __gwp_asan_get_deallocation_trace(responsible_allocation_, frames.get(), kMaxTraceLength);
+
+  if (thread_id == gwp_asan::kInvalidThreadID) {
+    _LOG(log, logtype::BACKTRACE, "\ndeallocated by thread <unknown>:\n");
+  } else {
+    _LOG(log, logtype::BACKTRACE, "\ndeallocated by thread %" PRIu64 ":\n", thread_id);
+  }
+
+  unwinder->SetDisplayBuildID(true);
+  for (size_t i = 0; i < num_frames; ++i) {
+    unwindstack::FrameData frame_data = BuildFrame(unwinder, frames.get()[i], i);
+    _LOG(log, logtype::BACKTRACE, "    %s\n", unwinder->FormatFrame(frame_data).c_str());
+  }
+}
+
+bool GwpAsanCrashData::HasAllocationTrace() const {
+  assert(CrashIsMine() && "HasAllocationTrace(): Crash is not mine!");
+  return responsible_allocation_ != nullptr;
+}
+
+void GwpAsanCrashData::DumpAllocationTrace(log_t* log, unwindstack::Unwinder* unwinder) const {
+  assert(HasAllocationTrace() && "DumpAllocationTrace(): No dealloc trace!");
+  uint64_t thread_id = __gwp_asan_get_allocation_thread_id(responsible_allocation_);
+
+  std::unique_ptr<uintptr_t> frames(new uintptr_t[kMaxTraceLength]);
+  size_t num_frames =
+      __gwp_asan_get_allocation_trace(responsible_allocation_, frames.get(), kMaxTraceLength);
+
+  if (thread_id == gwp_asan::kInvalidThreadID) {
+    _LOG(log, logtype::BACKTRACE, "\nallocated by thread <unknown>:\n");
+  } else {
+    _LOG(log, logtype::BACKTRACE, "\nallocated by thread %" PRIu64 ":\n", thread_id);
+  }
+
+  unwinder->SetDisplayBuildID(true);
+  for (size_t i = 0; i < num_frames; ++i) {
+    unwindstack::FrameData frame_data = BuildFrame(unwinder, frames.get()[i], i);
+    _LOG(log, logtype::BACKTRACE, "    %s\n", unwinder->FormatFrame(frame_data).c_str());
+  }
+}
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h b/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
new file mode 100644
index 0000000..aef4c62
--- /dev/null
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
@@ -0,0 +1,95 @@
+/*
+ * 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 <stddef.h>
+#include <stdint.h>
+
+#include <log/log.h>
+#include <unwindstack/Memory.h>
+
+#include "gwp_asan/common.h"
+#include "types.h"
+#include "utility.h"
+
+class GwpAsanCrashData {
+ public:
+  GwpAsanCrashData() = delete;
+  ~GwpAsanCrashData() = default;
+
+  // Construct the crash data object. Takes a handle to the object that can
+  // supply the memory of the dead process, and pointers to the GWP-ASan state
+  // and metadata regions within that process. Also takes the thread information
+  // of the crashed process. If the process didn't crash via SEGV, GWP-ASan may
+  // still be responsible, as it terminates when it detects an internal error
+  // (double free, invalid free). In these cases, we will retrieve the fault
+  // address from the GWP-ASan allocator's state.
+  GwpAsanCrashData(unwindstack::Memory* process_memory, uintptr_t gwp_asan_state_ptr,
+                   uintptr_t gwp_asan_metadata_ptr, const ThreadInfo& thread_info);
+
+  // Is GWP-ASan responsible for this crash.
+  bool CrashIsMine() const;
+
+  // Returns the fault address. The fault address may be the same as provided
+  // during construction, or it may have been retrieved from GWP-ASan's internal
+  // allocator crash state.
+  uintptr_t GetFaultAddress() const;
+
+  // Dump the GWP-ASan stringified cause of this crash. May only be called if
+  // CrashIsMine() returns true.
+  void DumpCause(log_t* log) const;
+
+  // Returns whether this crash has a deallocation trace. May only be called if
+  // CrashIsMine() returns true.
+  bool HasDeallocationTrace() const;
+
+  // Dump the GWP-ASan deallocation trace for this crash. May only be called if
+  // HasDeallocationTrace() returns true.
+  void DumpDeallocationTrace(log_t* log, unwindstack::Unwinder* unwinder) const;
+
+  // Returns whether this crash has a allocation trace. May only be called if
+  // CrashIsMine() returns true.
+  bool HasAllocationTrace() const;
+
+  // Dump the GWP-ASan allocation trace for this crash. May only be called if
+  // HasAllocationTrace() returns true.
+  void DumpAllocationTrace(log_t* log, unwindstack::Unwinder* unwinder) const;
+
+ protected:
+  // Is GWP-ASan responsible for this crash.
+  bool is_gwp_asan_responsible_ = false;
+
+  // Thread ID of the crash.
+  size_t thread_id_;
+
+  // The type of error that GWP-ASan caused (and the stringified version),
+  // Undefined if GWP-ASan isn't responsible for the crash.
+  gwp_asan::Error error_;
+  const char* error_string_;
+
+  // Pointer to the crash address. Holds the internal crash address if it
+  // exists, otherwise the address provided at construction.
+  uintptr_t crash_address_ = 0u;
+
+  // Pointer to the metadata for the responsible allocation, nullptr if it
+  // doesn't exist.
+  const gwp_asan::AllocationMetadata* responsible_allocation_ = nullptr;
+
+  // Internal state.
+  gwp_asan::AllocatorState state_;
+  std::unique_ptr<const gwp_asan::AllocationMetadata> metadata_;
+};
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
index 7133f77..291d994 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
@@ -55,6 +55,7 @@
 void engrave_tombstone(android::base::unique_fd output_fd, unwindstack::Unwinder* unwinder,
                        const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread,
                        uint64_t abort_msg_address, OpenFilesList* open_files,
-                       std::string* amfd_data);
+                       std::string* amfd_data, uintptr_t gwp_asan_state,
+                       uintptr_t gwp_asan_metadata);
 
 #endif  // _DEBUGGERD_TOMBSTONE_H
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index 9dea7ac..eed95bc 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -23,6 +23,7 @@
 
 #include <android-base/file.h>
 #include <android-base/properties.h>
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 #include "libdebuggerd/utility.h"
@@ -31,8 +32,13 @@
 #include "host_signal_fixup.h"
 #include "log_fake.h"
 
+// Include tombstone.cpp to define log_tag before GWP-ASan includes log.
 #include "tombstone.cpp"
 
+#include "gwp_asan.cpp"
+
+using ::testing::MatchesRegex;
+
 class TombstoneTest : public ::testing::Test {
  protected:
   virtual void SetUp() {
@@ -360,466 +366,130 @@
   ASSERT_STREQ("Timestamp: 1970-01-01 00:00:00+0000\n", amfd_data_.c_str());
 }
 
-class MemoryPattern : public unwindstack::Memory {
- public:
-  MemoryPattern() = default;
-  virtual ~MemoryPattern() = default;
+class GwpAsanCrashDataTest : public GwpAsanCrashData {
+public:
+  GwpAsanCrashDataTest(
+      gwp_asan::Error error,
+      const gwp_asan::AllocationMetadata *responsible_allocation) :
+      GwpAsanCrashData(nullptr, 0u, 0u, ThreadInfo{}) {
+    is_gwp_asan_responsible_ = true;
+    error_ = error;
+    responsible_allocation_ = responsible_allocation;
+    error_string_ = gwp_asan::ErrorToString(error_);
+  }
 
-  size_t Read(uint64_t, void* dst, size_t size) override {
-    uint8_t* data = reinterpret_cast<uint8_t*>(dst);
-    for (size_t i = 0; i < size; i++) {
-      data[i] = (i % 0xff);
-    }
-    return size;
+  void SetCrashAddress(uintptr_t crash_address) {
+    crash_address_ = crash_address;
   }
 };
 
-TEST_F(TombstoneTest, dump_stack_single_frame) {
-  std::vector<unwindstack::FrameData> frames;
-  unwindstack::Maps maps;
-  MemoryPattern memory;
+TEST_F(TombstoneTest, gwp_asan_cause_uaf_exact) {
+  gwp_asan::AllocationMetadata meta;
+  meta.Addr = 0x1000;
+  meta.Size = 32;
 
-  frames.push_back(
-      unwindstack::FrameData{.num = 0, .rel_pc = 0x1000, .pc = 0x301000, .sp = 0x2000});
-  dump_stack(&log_, frames, &maps, &memory);
+  GwpAsanCrashDataTest crash_data(gwp_asan::Error::USE_AFTER_FREE, &meta);
+  crash_data.SetCrashAddress(0x1000);
 
-  std::string contents;
+  crash_data.DumpCause(&log_);
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
-  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &contents));
-
-  std::string expected =
-#if defined(__LP64__)
-      "         0000000000001f80  0706050403020100\n"
-      "         0000000000001f88  0f0e0d0c0b0a0908\n"
-      "         0000000000001f90  1716151413121110\n"
-      "         0000000000001f98  1f1e1d1c1b1a1918\n"
-      "         0000000000001fa0  2726252423222120\n"
-      "         0000000000001fa8  2f2e2d2c2b2a2928\n"
-      "         0000000000001fb0  3736353433323130\n"
-      "         0000000000001fb8  3f3e3d3c3b3a3938\n"
-      "         0000000000001fc0  4746454443424140\n"
-      "         0000000000001fc8  4f4e4d4c4b4a4948\n"
-      "         0000000000001fd0  5756555453525150\n"
-      "         0000000000001fd8  5f5e5d5c5b5a5958\n"
-      "         0000000000001fe0  6766656463626160\n"
-      "         0000000000001fe8  6f6e6d6c6b6a6968\n"
-      "         0000000000001ff0  7776757473727170\n"
-      "         0000000000001ff8  7f7e7d7c7b7a7978\n"
-      "    #00  0000000000002000  0706050403020100\n"
-      "         0000000000002008  0f0e0d0c0b0a0908\n"
-      "         0000000000002010  1716151413121110\n"
-      "         0000000000002018  1f1e1d1c1b1a1918\n"
-      "         0000000000002020  2726252423222120\n"
-      "         0000000000002028  2f2e2d2c2b2a2928\n"
-      "         0000000000002030  3736353433323130\n"
-      "         0000000000002038  3f3e3d3c3b3a3938\n"
-      "         0000000000002040  4746454443424140\n"
-      "         0000000000002048  4f4e4d4c4b4a4948\n"
-      "         0000000000002050  5756555453525150\n"
-      "         0000000000002058  5f5e5d5c5b5a5958\n"
-      "         0000000000002060  6766656463626160\n"
-      "         0000000000002068  6f6e6d6c6b6a6968\n"
-      "         0000000000002070  7776757473727170\n"
-      "         0000000000002078  7f7e7d7c7b7a7978\n";
-#else
-      "         00001fc0  03020100\n"
-      "         00001fc4  07060504\n"
-      "         00001fc8  0b0a0908\n"
-      "         00001fcc  0f0e0d0c\n"
-      "         00001fd0  13121110\n"
-      "         00001fd4  17161514\n"
-      "         00001fd8  1b1a1918\n"
-      "         00001fdc  1f1e1d1c\n"
-      "         00001fe0  23222120\n"
-      "         00001fe4  27262524\n"
-      "         00001fe8  2b2a2928\n"
-      "         00001fec  2f2e2d2c\n"
-      "         00001ff0  33323130\n"
-      "         00001ff4  37363534\n"
-      "         00001ff8  3b3a3938\n"
-      "         00001ffc  3f3e3d3c\n"
-      "    #00  00002000  03020100\n"
-      "         00002004  07060504\n"
-      "         00002008  0b0a0908\n"
-      "         0000200c  0f0e0d0c\n"
-      "         00002010  13121110\n"
-      "         00002014  17161514\n"
-      "         00002018  1b1a1918\n"
-      "         0000201c  1f1e1d1c\n"
-      "         00002020  23222120\n"
-      "         00002024  27262524\n"
-      "         00002028  2b2a2928\n"
-      "         0000202c  2f2e2d2c\n"
-      "         00002030  33323130\n"
-      "         00002034  37363534\n"
-      "         00002038  3b3a3938\n"
-      "         0000203c  3f3e3d3c\n";
-#endif
-  EXPECT_EQ(expected, contents);
+  std::string tombstone_contents;
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_THAT(tombstone_contents,
+              MatchesRegex("Cause: \\[GWP-ASan\\]: Use After Free on a 32-byte "
+                           "allocation at 0x[a-fA-F0-9]+\n"));
 }
 
-TEST_F(TombstoneTest, dump_stack_multiple_frames_same_sp) {
-  std::vector<unwindstack::FrameData> frames;
-  unwindstack::Maps maps;
-  MemoryPattern memory;
+TEST_F(TombstoneTest, gwp_asan_cause_double_free) {
+  gwp_asan::AllocationMetadata meta;
+  meta.Addr = 0x1000;
+  meta.Size = 32;
 
-  frames.push_back(
-      unwindstack::FrameData{.num = 0, .rel_pc = 0x1000, .pc = 0x301000, .sp = 0x2000});
-  frames.push_back(
-      unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x2000});
-  dump_stack(&log_, frames, &maps, &memory);
+  GwpAsanCrashDataTest crash_data(gwp_asan::Error::DOUBLE_FREE, &meta);
+  crash_data.SetCrashAddress(0x1000);
 
-  std::string contents;
+  crash_data.DumpCause(&log_);
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
-  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &contents));
-
-  std::string expected =
-#if defined(__LP64__)
-      "         0000000000001f80  0706050403020100\n"
-      "         0000000000001f88  0f0e0d0c0b0a0908\n"
-      "         0000000000001f90  1716151413121110\n"
-      "         0000000000001f98  1f1e1d1c1b1a1918\n"
-      "         0000000000001fa0  2726252423222120\n"
-      "         0000000000001fa8  2f2e2d2c2b2a2928\n"
-      "         0000000000001fb0  3736353433323130\n"
-      "         0000000000001fb8  3f3e3d3c3b3a3938\n"
-      "         0000000000001fc0  4746454443424140\n"
-      "         0000000000001fc8  4f4e4d4c4b4a4948\n"
-      "         0000000000001fd0  5756555453525150\n"
-      "         0000000000001fd8  5f5e5d5c5b5a5958\n"
-      "         0000000000001fe0  6766656463626160\n"
-      "         0000000000001fe8  6f6e6d6c6b6a6968\n"
-      "         0000000000001ff0  7776757473727170\n"
-      "         0000000000001ff8  7f7e7d7c7b7a7978\n"
-      "    #00  0000000000002000  0706050403020100\n"
-      "         ................  ................\n"
-      "    #01  0000000000002000  0706050403020100\n"
-      "         0000000000002008  0f0e0d0c0b0a0908\n"
-      "         0000000000002010  1716151413121110\n"
-      "         0000000000002018  1f1e1d1c1b1a1918\n"
-      "         0000000000002020  2726252423222120\n"
-      "         0000000000002028  2f2e2d2c2b2a2928\n"
-      "         0000000000002030  3736353433323130\n"
-      "         0000000000002038  3f3e3d3c3b3a3938\n"
-      "         0000000000002040  4746454443424140\n"
-      "         0000000000002048  4f4e4d4c4b4a4948\n"
-      "         0000000000002050  5756555453525150\n"
-      "         0000000000002058  5f5e5d5c5b5a5958\n"
-      "         0000000000002060  6766656463626160\n"
-      "         0000000000002068  6f6e6d6c6b6a6968\n"
-      "         0000000000002070  7776757473727170\n"
-      "         0000000000002078  7f7e7d7c7b7a7978\n";
-#else
-      "         00001fc0  03020100\n"
-      "         00001fc4  07060504\n"
-      "         00001fc8  0b0a0908\n"
-      "         00001fcc  0f0e0d0c\n"
-      "         00001fd0  13121110\n"
-      "         00001fd4  17161514\n"
-      "         00001fd8  1b1a1918\n"
-      "         00001fdc  1f1e1d1c\n"
-      "         00001fe0  23222120\n"
-      "         00001fe4  27262524\n"
-      "         00001fe8  2b2a2928\n"
-      "         00001fec  2f2e2d2c\n"
-      "         00001ff0  33323130\n"
-      "         00001ff4  37363534\n"
-      "         00001ff8  3b3a3938\n"
-      "         00001ffc  3f3e3d3c\n"
-      "    #00  00002000  03020100\n"
-      "         ........  ........\n"
-      "    #01  00002000  03020100\n"
-      "         00002004  07060504\n"
-      "         00002008  0b0a0908\n"
-      "         0000200c  0f0e0d0c\n"
-      "         00002010  13121110\n"
-      "         00002014  17161514\n"
-      "         00002018  1b1a1918\n"
-      "         0000201c  1f1e1d1c\n"
-      "         00002020  23222120\n"
-      "         00002024  27262524\n"
-      "         00002028  2b2a2928\n"
-      "         0000202c  2f2e2d2c\n"
-      "         00002030  33323130\n"
-      "         00002034  37363534\n"
-      "         00002038  3b3a3938\n"
-      "         0000203c  3f3e3d3c\n";
-#endif
-  EXPECT_EQ(expected, contents);
+  std::string tombstone_contents;
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_THAT(tombstone_contents,
+              MatchesRegex("Cause: \\[GWP-ASan\\]: Double Free on a 32-byte "
+                           "allocation at 0x[a-fA-F0-9]+\n"));
 }
 
-TEST_F(TombstoneTest, dump_stack_multiple_frames) {
-  std::vector<unwindstack::FrameData> frames;
-  unwindstack::Maps maps;
-  MemoryPattern memory;
+TEST_F(TombstoneTest, gwp_asan_cause_overflow) {
+  gwp_asan::AllocationMetadata meta;
+  meta.Addr = 0x1000;
+  meta.Size = 32;
 
-  frames.push_back(
-      unwindstack::FrameData{.num = 0, .rel_pc = 0x1000, .pc = 0x301000, .sp = 0x2000});
-  frames.push_back(
-      unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x2010});
-  frames.push_back(
-      unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x2100});
-  dump_stack(&log_, frames, &maps, &memory);
+  GwpAsanCrashDataTest crash_data(gwp_asan::Error::BUFFER_OVERFLOW, &meta);
+  crash_data.SetCrashAddress(0x1025);
 
-  std::string contents;
+  crash_data.DumpCause(&log_);
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
-  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &contents));
-
-  std::string expected =
-#if defined(__LP64__)
-      "         0000000000001f80  0706050403020100\n"
-      "         0000000000001f88  0f0e0d0c0b0a0908\n"
-      "         0000000000001f90  1716151413121110\n"
-      "         0000000000001f98  1f1e1d1c1b1a1918\n"
-      "         0000000000001fa0  2726252423222120\n"
-      "         0000000000001fa8  2f2e2d2c2b2a2928\n"
-      "         0000000000001fb0  3736353433323130\n"
-      "         0000000000001fb8  3f3e3d3c3b3a3938\n"
-      "         0000000000001fc0  4746454443424140\n"
-      "         0000000000001fc8  4f4e4d4c4b4a4948\n"
-      "         0000000000001fd0  5756555453525150\n"
-      "         0000000000001fd8  5f5e5d5c5b5a5958\n"
-      "         0000000000001fe0  6766656463626160\n"
-      "         0000000000001fe8  6f6e6d6c6b6a6968\n"
-      "         0000000000001ff0  7776757473727170\n"
-      "         0000000000001ff8  7f7e7d7c7b7a7978\n"
-      "    #00  0000000000002000  0706050403020100\n"
-      "         0000000000002008  0f0e0d0c0b0a0908\n"
-      "    #01  0000000000002010  0706050403020100\n"
-      "         0000000000002018  0f0e0d0c0b0a0908\n"
-      "         0000000000002020  1716151413121110\n"
-      "         0000000000002028  1f1e1d1c1b1a1918\n"
-      "         0000000000002030  2726252423222120\n"
-      "         0000000000002038  2f2e2d2c2b2a2928\n"
-      "         0000000000002040  3736353433323130\n"
-      "         0000000000002048  3f3e3d3c3b3a3938\n"
-      "         0000000000002050  4746454443424140\n"
-      "         0000000000002058  4f4e4d4c4b4a4948\n"
-      "         0000000000002060  5756555453525150\n"
-      "         0000000000002068  5f5e5d5c5b5a5958\n"
-      "         0000000000002070  6766656463626160\n"
-      "         0000000000002078  6f6e6d6c6b6a6968\n"
-      "         0000000000002080  7776757473727170\n"
-      "         0000000000002088  7f7e7d7c7b7a7978\n"
-      "         ................  ................\n"
-      "    #02  0000000000002100  0706050403020100\n"
-      "         0000000000002108  0f0e0d0c0b0a0908\n"
-      "         0000000000002110  1716151413121110\n"
-      "         0000000000002118  1f1e1d1c1b1a1918\n"
-      "         0000000000002120  2726252423222120\n"
-      "         0000000000002128  2f2e2d2c2b2a2928\n"
-      "         0000000000002130  3736353433323130\n"
-      "         0000000000002138  3f3e3d3c3b3a3938\n"
-      "         0000000000002140  4746454443424140\n"
-      "         0000000000002148  4f4e4d4c4b4a4948\n"
-      "         0000000000002150  5756555453525150\n"
-      "         0000000000002158  5f5e5d5c5b5a5958\n"
-      "         0000000000002160  6766656463626160\n"
-      "         0000000000002168  6f6e6d6c6b6a6968\n"
-      "         0000000000002170  7776757473727170\n"
-      "         0000000000002178  7f7e7d7c7b7a7978\n";
-#else
-      "         00001fc0  03020100\n"
-      "         00001fc4  07060504\n"
-      "         00001fc8  0b0a0908\n"
-      "         00001fcc  0f0e0d0c\n"
-      "         00001fd0  13121110\n"
-      "         00001fd4  17161514\n"
-      "         00001fd8  1b1a1918\n"
-      "         00001fdc  1f1e1d1c\n"
-      "         00001fe0  23222120\n"
-      "         00001fe4  27262524\n"
-      "         00001fe8  2b2a2928\n"
-      "         00001fec  2f2e2d2c\n"
-      "         00001ff0  33323130\n"
-      "         00001ff4  37363534\n"
-      "         00001ff8  3b3a3938\n"
-      "         00001ffc  3f3e3d3c\n"
-      "    #00  00002000  03020100\n"
-      "         00002004  07060504\n"
-      "         00002008  0b0a0908\n"
-      "         0000200c  0f0e0d0c\n"
-      "    #01  00002010  03020100\n"
-      "         00002014  07060504\n"
-      "         00002018  0b0a0908\n"
-      "         0000201c  0f0e0d0c\n"
-      "         00002020  13121110\n"
-      "         00002024  17161514\n"
-      "         00002028  1b1a1918\n"
-      "         0000202c  1f1e1d1c\n"
-      "         00002030  23222120\n"
-      "         00002034  27262524\n"
-      "         00002038  2b2a2928\n"
-      "         0000203c  2f2e2d2c\n"
-      "         00002040  33323130\n"
-      "         00002044  37363534\n"
-      "         00002048  3b3a3938\n"
-      "         0000204c  3f3e3d3c\n"
-      "         ........  ........\n"
-      "    #02  00002100  03020100\n"
-      "         00002104  07060504\n"
-      "         00002108  0b0a0908\n"
-      "         0000210c  0f0e0d0c\n"
-      "         00002110  13121110\n"
-      "         00002114  17161514\n"
-      "         00002118  1b1a1918\n"
-      "         0000211c  1f1e1d1c\n"
-      "         00002120  23222120\n"
-      "         00002124  27262524\n"
-      "         00002128  2b2a2928\n"
-      "         0000212c  2f2e2d2c\n"
-      "         00002130  33323130\n"
-      "         00002134  37363534\n"
-      "         00002138  3b3a3938\n"
-      "         0000213c  3f3e3d3c\n";
-#endif
-  EXPECT_EQ(expected, contents);
+  std::string tombstone_contents;
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_THAT(
+      tombstone_contents,
+      MatchesRegex(
+          "Cause: \\[GWP-ASan\\]: Buffer Overflow, 5 bytes right of a 32-byte "
+          "allocation at 0x[a-fA-F0-9]+\n"));
 }
 
-TEST_F(TombstoneTest, dump_stack_multiple_frames_disjoint_frames) {
-  std::vector<unwindstack::FrameData> frames;
-  unwindstack::Maps maps;
-  MemoryPattern memory;
+TEST_F(TombstoneTest, gwp_asan_cause_underflow) {
+  gwp_asan::AllocationMetadata meta;
+  meta.Addr = 0x1000;
+  meta.Size = 32;
 
-  frames.push_back(
-      unwindstack::FrameData{.num = 0, .rel_pc = 0x1000, .pc = 0x301000, .sp = 0x2000});
-  frames.push_back(
-      unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x2010});
-  frames.push_back(
-      unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x1000});
-  frames.push_back(
-      unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x1030});
-  dump_stack(&log_, frames, &maps, &memory);
+  GwpAsanCrashDataTest crash_data(gwp_asan::Error::BUFFER_UNDERFLOW, &meta);
+  crash_data.SetCrashAddress(0xffe);
 
-  std::string contents;
+  crash_data.DumpCause(&log_);
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
-  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &contents));
-
-  std::string expected =
-#if defined(__LP64__)
-      "         0000000000001f80  0706050403020100\n"
-      "         0000000000001f88  0f0e0d0c0b0a0908\n"
-      "         0000000000001f90  1716151413121110\n"
-      "         0000000000001f98  1f1e1d1c1b1a1918\n"
-      "         0000000000001fa0  2726252423222120\n"
-      "         0000000000001fa8  2f2e2d2c2b2a2928\n"
-      "         0000000000001fb0  3736353433323130\n"
-      "         0000000000001fb8  3f3e3d3c3b3a3938\n"
-      "         0000000000001fc0  4746454443424140\n"
-      "         0000000000001fc8  4f4e4d4c4b4a4948\n"
-      "         0000000000001fd0  5756555453525150\n"
-      "         0000000000001fd8  5f5e5d5c5b5a5958\n"
-      "         0000000000001fe0  6766656463626160\n"
-      "         0000000000001fe8  6f6e6d6c6b6a6968\n"
-      "         0000000000001ff0  7776757473727170\n"
-      "         0000000000001ff8  7f7e7d7c7b7a7978\n"
-      "    #00  0000000000002000  0706050403020100\n"
-      "         0000000000002008  0f0e0d0c0b0a0908\n"
-      "    #01  0000000000002010  0706050403020100\n"
-      "         0000000000002018  0f0e0d0c0b0a0908\n"
-      "         0000000000002020  1716151413121110\n"
-      "         0000000000002028  1f1e1d1c1b1a1918\n"
-      "         0000000000002030  2726252423222120\n"
-      "         0000000000002038  2f2e2d2c2b2a2928\n"
-      "         0000000000002040  3736353433323130\n"
-      "         0000000000002048  3f3e3d3c3b3a3938\n"
-      "         0000000000002050  4746454443424140\n"
-      "         0000000000002058  4f4e4d4c4b4a4948\n"
-      "         0000000000002060  5756555453525150\n"
-      "         0000000000002068  5f5e5d5c5b5a5958\n"
-      "         0000000000002070  6766656463626160\n"
-      "         0000000000002078  6f6e6d6c6b6a6968\n"
-      "         0000000000002080  7776757473727170\n"
-      "         0000000000002088  7f7e7d7c7b7a7978\n"
-      "         ................  ................\n"
-      "    #02  0000000000001000  0706050403020100\n"
-      "         0000000000001008  0f0e0d0c0b0a0908\n"
-      "         0000000000001010  1716151413121110\n"
-      "         0000000000001018  1f1e1d1c1b1a1918\n"
-      "         0000000000001020  2726252423222120\n"
-      "         0000000000001028  2f2e2d2c2b2a2928\n"
-      "    #03  0000000000001030  0706050403020100\n"
-      "         0000000000001038  0f0e0d0c0b0a0908\n"
-      "         0000000000001040  1716151413121110\n"
-      "         0000000000001048  1f1e1d1c1b1a1918\n"
-      "         0000000000001050  2726252423222120\n"
-      "         0000000000001058  2f2e2d2c2b2a2928\n"
-      "         0000000000001060  3736353433323130\n"
-      "         0000000000001068  3f3e3d3c3b3a3938\n"
-      "         0000000000001070  4746454443424140\n"
-      "         0000000000001078  4f4e4d4c4b4a4948\n"
-      "         0000000000001080  5756555453525150\n"
-      "         0000000000001088  5f5e5d5c5b5a5958\n"
-      "         0000000000001090  6766656463626160\n"
-      "         0000000000001098  6f6e6d6c6b6a6968\n"
-      "         00000000000010a0  7776757473727170\n"
-      "         00000000000010a8  7f7e7d7c7b7a7978\n";
-#else
-      "         00001fc0  03020100\n"
-      "         00001fc4  07060504\n"
-      "         00001fc8  0b0a0908\n"
-      "         00001fcc  0f0e0d0c\n"
-      "         00001fd0  13121110\n"
-      "         00001fd4  17161514\n"
-      "         00001fd8  1b1a1918\n"
-      "         00001fdc  1f1e1d1c\n"
-      "         00001fe0  23222120\n"
-      "         00001fe4  27262524\n"
-      "         00001fe8  2b2a2928\n"
-      "         00001fec  2f2e2d2c\n"
-      "         00001ff0  33323130\n"
-      "         00001ff4  37363534\n"
-      "         00001ff8  3b3a3938\n"
-      "         00001ffc  3f3e3d3c\n"
-      "    #00  00002000  03020100\n"
-      "         00002004  07060504\n"
-      "         00002008  0b0a0908\n"
-      "         0000200c  0f0e0d0c\n"
-      "    #01  00002010  03020100\n"
-      "         00002014  07060504\n"
-      "         00002018  0b0a0908\n"
-      "         0000201c  0f0e0d0c\n"
-      "         00002020  13121110\n"
-      "         00002024  17161514\n"
-      "         00002028  1b1a1918\n"
-      "         0000202c  1f1e1d1c\n"
-      "         00002030  23222120\n"
-      "         00002034  27262524\n"
-      "         00002038  2b2a2928\n"
-      "         0000203c  2f2e2d2c\n"
-      "         00002040  33323130\n"
-      "         00002044  37363534\n"
-      "         00002048  3b3a3938\n"
-      "         0000204c  3f3e3d3c\n"
-      "         ........  ........\n"
-      "    #02  00001000  03020100\n"
-      "         00001004  07060504\n"
-      "         00001008  0b0a0908\n"
-      "         0000100c  0f0e0d0c\n"
-      "         00001010  13121110\n"
-      "         00001014  17161514\n"
-      "         00001018  1b1a1918\n"
-      "         0000101c  1f1e1d1c\n"
-      "         00001020  23222120\n"
-      "         00001024  27262524\n"
-      "         00001028  2b2a2928\n"
-      "         0000102c  2f2e2d2c\n"
-      "    #03  00001030  03020100\n"
-      "         00001034  07060504\n"
-      "         00001038  0b0a0908\n"
-      "         0000103c  0f0e0d0c\n"
-      "         00001040  13121110\n"
-      "         00001044  17161514\n"
-      "         00001048  1b1a1918\n"
-      "         0000104c  1f1e1d1c\n"
-      "         00001050  23222120\n"
-      "         00001054  27262524\n"
-      "         00001058  2b2a2928\n"
-      "         0000105c  2f2e2d2c\n"
-      "         00001060  33323130\n"
-      "         00001064  37363534\n"
-      "         00001068  3b3a3938\n"
-      "         0000106c  3f3e3d3c\n";
-#endif
-  EXPECT_EQ(expected, contents);
+  std::string tombstone_contents;
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_THAT(
+      tombstone_contents,
+      MatchesRegex(
+          "Cause: \\[GWP-ASan\\]: Buffer Underflow, 2 bytes left of a 32-byte "
+          "allocation at 0x[a-fA-F0-9]+\n"));
 }
+
+TEST_F(TombstoneTest, gwp_asan_cause_invalid_free_inside) {
+  gwp_asan::AllocationMetadata meta;
+  meta.Addr = 0x1000;
+  meta.Size = 32;
+
+  GwpAsanCrashDataTest crash_data(gwp_asan::Error::INVALID_FREE, &meta);
+  crash_data.SetCrashAddress(0x1001);
+
+  crash_data.DumpCause(&log_);
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  std::string tombstone_contents;
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_THAT(
+      tombstone_contents,
+      MatchesRegex(
+          "Cause: \\[GWP-ASan\\]: Invalid \\(Wild\\) Free, 1 byte into a 32-byte "
+          "allocation at 0x[a-fA-F0-9]+\n"));
+}
+
+TEST_F(TombstoneTest, gwp_asan_cause_invalid_free_outside) {
+  gwp_asan::AllocationMetadata meta;
+  meta.Addr = 0x1000;
+  meta.Size = 32;
+
+  GwpAsanCrashDataTest crash_data(gwp_asan::Error::INVALID_FREE, &meta);
+  crash_data.SetCrashAddress(0x1021);
+
+  crash_data.DumpCause(&log_);
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  std::string tombstone_contents;
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_THAT(
+      tombstone_contents,
+      MatchesRegex(
+          "Cause: \\[GWP-ASan\\]: Invalid \\(Wild\\) Free, 33 bytes right of a 32-byte "
+          "allocation at 0x[a-fA-F0-9]+\n"));
+}
+
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 236fcf7..fd52e81 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -52,13 +52,14 @@
 #include <unwindstack/Regs.h>
 #include <unwindstack/Unwinder.h>
 
-// Needed to get DEBUGGER_SIGNAL.
-#include "debuggerd/handler.h"
-
 #include "libdebuggerd/backtrace.h"
+#include "libdebuggerd/gwp_asan.h"
 #include "libdebuggerd/open_files_list.h"
 #include "libdebuggerd/utility.h"
 
+#include "gwp_asan/common.h"
+#include "gwp_asan/crash_handler.h"
+
 using android::base::GetBoolProperty;
 using android::base::GetProperty;
 using android::base::StringPrintf;
@@ -191,106 +192,6 @@
   _LOG(log, logtype::HEADER, "uid: %d\n", thread_info.uid);
 }
 
-static void dump_stack_segment(log_t* log, unwindstack::Maps* maps, unwindstack::Memory* memory,
-                               uint64_t* sp, size_t words, int label) {
-  // Read the data all at once.
-  word_t stack_data[words];
-
-  // TODO: Do we need to word align this for crashes caused by a misaligned sp?
-  //       The process_vm_readv implementation of Memory should handle this appropriately?
-  size_t bytes_read = memory->Read(*sp, stack_data, sizeof(word_t) * words);
-  words = bytes_read / sizeof(word_t);
-  std::string line;
-  for (size_t i = 0; i < words; i++) {
-    line = "    ";
-    if (i == 0 && label >= 0) {
-      // Print the label once.
-      line += StringPrintf("#%02d  ", label);
-    } else {
-      line += "     ";
-    }
-    line += StringPrintf("%" PRIPTR "  %" PRIPTR, *sp, static_cast<uint64_t>(stack_data[i]));
-
-    unwindstack::MapInfo* map_info = maps->Find(stack_data[i]);
-    if (map_info != nullptr && !map_info->name.empty()) {
-      line += "  " + map_info->name;
-      std::string func_name;
-      uint64_t func_offset = 0;
-      if (map_info->GetFunctionName(stack_data[i], &func_name, &func_offset)) {
-        line += " (" + func_name;
-        if (func_offset) {
-          line += StringPrintf("+%" PRIu64, func_offset);
-        }
-        line += ')';
-      }
-    }
-    _LOG(log, logtype::STACK, "%s\n", line.c_str());
-
-    *sp += sizeof(word_t);
-  }
-}
-
-static void dump_stack(log_t* log, const std::vector<unwindstack::FrameData>& frames,
-                       unwindstack::Maps* maps, unwindstack::Memory* memory) {
-  size_t first = 0, last;
-  for (size_t i = 0; i < frames.size(); i++) {
-    if (frames[i].sp) {
-      if (!first) {
-        first = i+1;
-      }
-      last = i;
-    }
-  }
-
-  if (!first) {
-    return;
-  }
-  first--;
-
-  // Dump a few words before the first frame.
-  uint64_t sp = frames[first].sp - STACK_WORDS * sizeof(word_t);
-  dump_stack_segment(log, maps, memory, &sp, STACK_WORDS, -1);
-
-#if defined(__LP64__)
-  static constexpr const char delimiter[] = "         ................  ................\n";
-#else
-  static constexpr const char delimiter[] = "         ........  ........\n";
-#endif
-
-  // Dump a few words from all successive frames.
-  for (size_t i = first; i <= last; i++) {
-    auto* frame = &frames[i];
-    if (sp != frame->sp) {
-      _LOG(log, logtype::STACK, delimiter);
-      sp = frame->sp;
-    }
-    if (i != last) {
-      // Print stack data up to the stack from the next frame.
-      size_t words;
-      uint64_t next_sp = frames[i + 1].sp;
-      if (next_sp < sp) {
-        // The next frame is probably using a completely different stack,
-        // so dump the max from this stack.
-        words = STACK_WORDS;
-      } else {
-        words = (next_sp - sp) / sizeof(word_t);
-        if (words == 0) {
-          // The sp is the same as the next frame, print at least
-          // one line for this frame.
-          words = 1;
-        } else if (words > STACK_WORDS) {
-          words = STACK_WORDS;
-        }
-      }
-      dump_stack_segment(log, maps, memory, &sp, words, i);
-    } else {
-      // Print some number of words past the last stack frame since we
-      // don't know how large the stack is.
-      dump_stack_segment(log, maps, memory, &sp, STACK_WORDS, i);
-    }
-  }
-}
-
 static std::string get_addr_string(uint64_t addr) {
   std::string addr_str;
 #if defined(__LP64__)
@@ -475,7 +376,8 @@
 }
 
 static bool dump_thread(log_t* log, unwindstack::Unwinder* unwinder, const ThreadInfo& thread_info,
-                        uint64_t abort_msg_address, bool primary_thread) {
+                        uint64_t abort_msg_address, bool primary_thread,
+                        const GwpAsanCrashData& gwp_asan_crash_data) {
   log->current_tid = thread_info.tid;
   if (!primary_thread) {
     _LOG(log, logtype::THREAD, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
@@ -484,7 +386,13 @@
 
   if (thread_info.siginfo) {
     dump_signal_info(log, thread_info, unwinder->GetProcessMemory().get());
-    dump_probable_cause(log, thread_info.siginfo, unwinder->GetMaps(), thread_info.registers.get());
+  }
+
+  if (primary_thread && gwp_asan_crash_data.CrashIsMine()) {
+    gwp_asan_crash_data.DumpCause(log);
+  } else if (thread_info.siginfo) {
+    dump_probable_cause(log, thread_info.siginfo, unwinder->GetMaps(),
+                        thread_info.registers.get());
   }
 
   if (primary_thread) {
@@ -502,12 +410,17 @@
   } else {
     _LOG(log, logtype::BACKTRACE, "\nbacktrace:\n");
     log_backtrace(log, unwinder, "    ");
-
-    _LOG(log, logtype::STACK, "\nstack:\n");
-    dump_stack(log, unwinder->frames(), unwinder->GetMaps(), unwinder->GetProcessMemory().get());
   }
 
   if (primary_thread) {
+    if (gwp_asan_crash_data.HasDeallocationTrace()) {
+      gwp_asan_crash_data.DumpDeallocationTrace(log, unwinder);
+    }
+
+    if (gwp_asan_crash_data.HasAllocationTrace()) {
+      gwp_asan_crash_data.DumpAllocationTrace(log, unwinder);
+    }
+
     unwindstack::Maps* maps = unwinder->GetMaps();
     dump_memory_and_code(log, maps, unwinder->GetProcessMemory().get(),
                          thread_info.registers.get());
@@ -689,13 +602,14 @@
   }
 
   engrave_tombstone(unique_fd(dup(tombstone_fd)), &unwinder, threads, tid, abort_msg_address,
-                    nullptr, nullptr);
+                    nullptr, nullptr, 0u, 0u);
 }
 
 void engrave_tombstone(unique_fd output_fd, unwindstack::Unwinder* unwinder,
                        const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
                        uint64_t abort_msg_address, OpenFilesList* open_files,
-                       std::string* amfd_data) {
+                       std::string* amfd_data, uintptr_t gwp_asan_state_ptr,
+                       uintptr_t gwp_asan_metadata_ptr) {
   // don't copy log messages to tombstone unless this is a dev device
   bool want_logs = android::base::GetBoolProperty("ro.debuggable", false);
 
@@ -713,7 +627,13 @@
   if (it == threads.end()) {
     LOG(FATAL) << "failed to find target thread";
   }
-  dump_thread(&log, unwinder, it->second, abort_msg_address, true);
+
+  GwpAsanCrashData gwp_asan_crash_data(unwinder->GetProcessMemory().get(),
+                                       gwp_asan_state_ptr,
+                                       gwp_asan_metadata_ptr, it->second);
+
+  dump_thread(&log, unwinder, it->second, abort_msg_address, true,
+              gwp_asan_crash_data);
 
   if (want_logs) {
     dump_logs(&log, it->second.pid, 50);
@@ -724,7 +644,7 @@
       continue;
     }
 
-    dump_thread(&log, unwinder, thread_info, 0, false);
+    dump_thread(&log, unwinder, thread_info, 0, false, gwp_asan_crash_data);
   }
 
   if (open_files) {
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 5ce26fc..0a1d2a4 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -35,6 +35,7 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
+#include <bionic/reserved_signals.h>
 #include <debuggerd/handler.h>
 #include <log/log.h>
 #include <unwindstack/Memory.h>
@@ -296,7 +297,8 @@
     case SIGSTOP: return "SIGSTOP";
     case SIGSYS: return "SIGSYS";
     case SIGTRAP: return "SIGTRAP";
-    case DEBUGGER_SIGNAL: return "<debuggerd signal>";
+    case BIONIC_SIGNAL_DEBUGGER:
+      return "<debuggerd signal>";
     default: return "?";
   }
 }
diff --git a/debuggerd/protocol.h b/debuggerd/protocol.h
index bfd0fbb..bf53864 100644
--- a/debuggerd/protocol.h
+++ b/debuggerd/protocol.h
@@ -95,10 +95,16 @@
   uintptr_t fdsan_table_address;
 };
 
+struct __attribute__((__packed__)) CrashInfoDataV3 : public CrashInfoDataV2 {
+  uintptr_t gwp_asan_state;
+  uintptr_t gwp_asan_metadata;
+};
+
 struct __attribute__((__packed__)) CrashInfo {
   CrashInfoHeader header;
   union {
     CrashInfoDataV1 v1;
     CrashInfoDataV2 v2;
+    CrashInfoDataV3 v3;
   } data;
 };
diff --git a/deprecated-adf/Android.bp b/deprecated-adf/Android.bp
deleted file mode 100644
index b44c296..0000000
--- a/deprecated-adf/Android.bp
+++ /dev/null
@@ -1 +0,0 @@
-subdirs = ["*"]
diff --git a/deprecated-adf/libadf/Android.bp b/deprecated-adf/libadf/Android.bp
index 49e3721..70f0a3b 100644
--- a/deprecated-adf/libadf/Android.bp
+++ b/deprecated-adf/libadf/Android.bp
@@ -24,5 +24,3 @@
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
 }
-
-subdirs = ["tests"]
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 17ec392..fd009e7 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -18,9 +18,9 @@
 # Package fastboot-related executables.
 #
 
-my_dist_files := $(HOST_OUT_EXECUTABLES)/mke2fs
-my_dist_files += $(HOST_OUT_EXECUTABLES)/e2fsdroid
-my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs
-my_dist_files += $(HOST_OUT_EXECUTABLES)/sload_f2fs
+my_dist_files := $(SOONG_HOST_OUT_EXECUTABLES)/mke2fs
+my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/e2fsdroid
+my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/make_f2fs
+my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/sload_f2fs
 $(call dist-for-goals,dist_files sdk win_sdk,$(my_dist_files))
 my_dist_files :=
diff --git a/fastboot/constants.h b/fastboot/constants.h
index 5a554a0..ba43ca5 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -42,11 +42,13 @@
 #define RESPONSE_INFO "INFO"
 
 #define FB_COMMAND_SZ 64
-#define FB_RESPONSE_SZ 64
+#define FB_RESPONSE_SZ 256
 
 #define FB_VAR_VERSION "version"
 #define FB_VAR_VERSION_BOOTLOADER "version-bootloader"
 #define FB_VAR_VERSION_BASEBAND "version-baseband"
+#define FB_VAR_VERSION_OS "version-os"
+#define FB_VAR_VERSION_VNDK "version-vndk"
 #define FB_VAR_PRODUCT "product"
 #define FB_VAR_SERIALNO "serialno"
 #define FB_VAR_SECURE "secure"
@@ -69,3 +71,9 @@
 #define FB_VAR_SUPER_PARTITION_NAME "super-partition-name"
 #define FB_VAR_SNAPSHOT_UPDATE_STATUS "snapshot-update-status"
 #define FB_VAR_CPU_ABI "cpu-abi"
+#define FB_VAR_SYSTEM_FINGERPRINT "system-fingerprint"
+#define FB_VAR_VENDOR_FINGERPRINT "vendor-fingerprint"
+#define FB_VAR_DYNAMIC_PARTITION "dynamic-partition"
+#define FB_VAR_FIRST_API_LEVEL "first-api-level"
+#define FB_VAR_SECURITY_PATCH_LEVEL "security-patch-level"
+#define FB_VAR_TREBLE_ENABLED "treble-enabled"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index 1a745ab..2c9dec9 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -31,7 +31,6 @@
 #include <cutils/android_reboot.h>
 #include <ext4_utils/wipe.h>
 #include <fs_mgr.h>
-#include <fs_mgr/roots.h>
 #include <libgsi/libgsi.h>
 #include <liblp/builder.h>
 #include <liblp/liblp.h>
@@ -107,6 +106,8 @@
             {FB_VAR_VERSION, {GetVersion, nullptr}},
             {FB_VAR_VERSION_BOOTLOADER, {GetBootloaderVersion, nullptr}},
             {FB_VAR_VERSION_BASEBAND, {GetBasebandVersion, nullptr}},
+            {FB_VAR_VERSION_OS, {GetOsVersion, nullptr}},
+            {FB_VAR_VERSION_VNDK, {GetVndkVersion, nullptr}},
             {FB_VAR_PRODUCT, {GetProduct, nullptr}},
             {FB_VAR_SERIALNO, {GetSerial, nullptr}},
             {FB_VAR_VARIANT, {GetVariant, nullptr}},
@@ -128,7 +129,13 @@
             {FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}},
             {FB_VAR_SUPER_PARTITION_NAME, {GetSuperPartitionName, nullptr}},
             {FB_VAR_SNAPSHOT_UPDATE_STATUS, {GetSnapshotUpdateStatus, nullptr}},
-            {FB_VAR_CPU_ABI, {GetCpuAbi, nullptr}}};
+            {FB_VAR_CPU_ABI, {GetCpuAbi, nullptr}},
+            {FB_VAR_SYSTEM_FINGERPRINT, {GetSystemFingerprint, nullptr}},
+            {FB_VAR_VENDOR_FINGERPRINT, {GetVendorFingerprint, nullptr}},
+            {FB_VAR_DYNAMIC_PARTITION, {GetDynamicPartition, nullptr}},
+            {FB_VAR_FIRST_API_LEVEL, {GetFirstApiLevel, nullptr}},
+            {FB_VAR_SECURITY_PATCH_LEVEL, {GetSecurityPatchLevel, nullptr}},
+            {FB_VAR_TREBLE_ENABLED, {GetTrebleEnabled, nullptr}}};
 
     if (args.size() < 2) {
         return device->WriteFail("Missing argument");
@@ -550,42 +557,6 @@
     return UpdateSuper(device, args[1], wipe);
 }
 
-class AutoMountMetadata {
-  public:
-    AutoMountMetadata() {
-        android::fs_mgr::Fstab proc_mounts;
-        if (!ReadFstabFromFile("/proc/mounts", &proc_mounts)) {
-            LOG(ERROR) << "Could not read /proc/mounts";
-            return;
-        }
-
-        auto iter = std::find_if(proc_mounts.begin(), proc_mounts.end(),
-                [](const auto& entry) { return entry.mount_point == "/metadata"; });
-        if (iter != proc_mounts.end()) {
-            mounted_ = true;
-            return;
-        }
-
-        if (!ReadDefaultFstab(&fstab_)) {
-            LOG(ERROR) << "Could not read default fstab";
-            return;
-        }
-        mounted_ = EnsurePathMounted(&fstab_, "/metadata");
-        should_unmount_ = true;
-    }
-    ~AutoMountMetadata() {
-        if (mounted_ && should_unmount_) {
-            EnsurePathUnmounted(&fstab_, "/metadata");
-        }
-    }
-    explicit operator bool() const { return mounted_; }
-
-  private:
-    android::fs_mgr::Fstab fstab_;
-    bool mounted_ = false;
-    bool should_unmount_ = false;
-};
-
 bool GsiHandler(FastbootDevice* device, const std::vector<std::string>& args) {
     if (args.size() != 2) {
         return device->WriteFail("Invalid arguments");
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index 102ebdb..7e7e507 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -21,6 +21,7 @@
 
 #include <algorithm>
 #include <memory>
+#include <optional>
 #include <set>
 #include <string>
 
@@ -56,6 +57,7 @@
     Fstab fstab;
     ReadDefaultFstab(&fstab);
 
+    std::optional<AutoMountMetadata> mount_metadata;
     for (const auto& entry : fstab) {
         auto partition = android::base::Basename(entry.mount_point);
         if ("/" == entry.mount_point) {
@@ -63,6 +65,7 @@
         }
 
         if ((partition + device->GetCurrentSlot()) == partition_name) {
+            mount_metadata.emplace();
             fs_mgr_overlayfs_teardown(entry.mount_point.c_str());
         }
     }
diff --git a/fastboot/device/usb_client.cpp b/fastboot/device/usb_client.cpp
index 5066046..9c80765 100644
--- a/fastboot/device/usb_client.cpp
+++ b/fastboot/device/usb_client.cpp
@@ -297,3 +297,7 @@
     CloseFunctionFs(handle_.get());
     return 0;
 }
+
+int ClientUsbTransport::Reset() {
+    return 0;
+}
diff --git a/fastboot/device/usb_client.h b/fastboot/device/usb_client.h
index 3694f9a..e6a1a8b 100644
--- a/fastboot/device/usb_client.h
+++ b/fastboot/device/usb_client.h
@@ -29,6 +29,7 @@
     ssize_t Read(void* data, size_t len) override;
     ssize_t Write(const void* data, size_t len) override;
     int Close() override;
+    int Reset() override;
 
   private:
     std::unique_ptr<usb_handle> handle_;
diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp
index b3f2d5f..7c6ac89 100644
--- a/fastboot/device/utility.cpp
+++ b/fastboot/device/utility.cpp
@@ -26,6 +26,7 @@
 #include <android-base/properties.h>
 #include <android-base/strings.h>
 #include <fs_mgr.h>
+#include <fs_mgr/roots.h>
 #include <fs_mgr_dm_linear.h>
 #include <liblp/builder.h>
 #include <liblp/liblp.h>
@@ -240,3 +241,29 @@
     }
     return current_slot_suffix;
 }
+
+AutoMountMetadata::AutoMountMetadata() {
+    android::fs_mgr::Fstab proc_mounts;
+    if (!ReadFstabFromFile("/proc/mounts", &proc_mounts)) {
+        LOG(ERROR) << "Could not read /proc/mounts";
+        return;
+    }
+
+    if (GetEntryForMountPoint(&proc_mounts, "/metadata")) {
+        mounted_ = true;
+        return;
+    }
+
+    if (!ReadDefaultFstab(&fstab_)) {
+        LOG(ERROR) << "Could not read default fstab";
+        return;
+    }
+    mounted_ = EnsurePathMounted(&fstab_, "/metadata");
+    should_unmount_ = true;
+}
+
+AutoMountMetadata::~AutoMountMetadata() {
+    if (mounted_ && should_unmount_) {
+        EnsurePathUnmounted(&fstab_, "/metadata");
+    }
+}
diff --git a/fastboot/device/utility.h b/fastboot/device/utility.h
index bfeeb74..3b71ef0 100644
--- a/fastboot/device/utility.h
+++ b/fastboot/device/utility.h
@@ -20,6 +20,7 @@
 
 #include <android-base/unique_fd.h>
 #include <android/hardware/boot/1.0/IBootControl.h>
+#include <fstab/fstab.h>
 #include <liblp/liblp.h>
 
 // Logical partitions are only mapped to a block device as needed, and
@@ -51,6 +52,18 @@
     std::function<void()> closer_;
 };
 
+class AutoMountMetadata {
+  public:
+    AutoMountMetadata();
+    ~AutoMountMetadata();
+    explicit operator bool() const { return mounted_; }
+
+  private:
+    android::fs_mgr::Fstab fstab_;
+    bool mounted_ = false;
+    bool should_unmount_ = false;
+};
+
 class FastbootDevice;
 
 // On normal devices, the super partition is always named "super". On retrofit
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index 10eac01..e7d8bc3 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -62,6 +62,18 @@
     return true;
 }
 
+bool GetOsVersion(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                  std::string* message) {
+    *message = android::base::GetProperty("ro.build.version.release", "");
+    return true;
+}
+
+bool GetVndkVersion(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                    std::string* message) {
+    *message = android::base::GetProperty("ro.vndk.version", "");
+    return true;
+}
+
 bool GetProduct(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
                 std::string* message) {
     *message = android::base::GetProperty("ro.product.device", "");
@@ -458,3 +470,42 @@
     *message = android::base::GetProperty("ro.product.cpu.abi", "");
     return true;
 }
+
+bool GetSystemFingerprint(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                          std::string* message) {
+    *message = android::base::GetProperty("ro.system.build.fingerprint", "");
+    if (message->empty()) {
+        *message = android::base::GetProperty("ro.build.fingerprint", "");
+    }
+    return true;
+}
+
+bool GetVendorFingerprint(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                          std::string* message) {
+    *message = android::base::GetProperty("ro.vendor.build.fingerprint", "");
+    return true;
+}
+
+bool GetDynamicPartition(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                         std::string* message) {
+    *message = android::base::GetProperty("ro.boot.dynamic_partitions", "");
+    return true;
+}
+
+bool GetFirstApiLevel(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                      std::string* message) {
+    *message = android::base::GetProperty("ro.product.first_api_level", "");
+    return true;
+}
+
+bool GetSecurityPatchLevel(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                           std::string* message) {
+    *message = android::base::GetProperty("ro.build.version.security_patch", "");
+    return true;
+}
+
+bool GetTrebleEnabled(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                      std::string* message) {
+    *message = android::base::GetProperty("ro.treble.enabled", "");
+    return true;
+}
diff --git a/fastboot/device/variables.h b/fastboot/device/variables.h
index 90840d6..c11e472 100644
--- a/fastboot/device/variables.h
+++ b/fastboot/device/variables.h
@@ -26,6 +26,10 @@
                           std::string* message);
 bool GetBasebandVersion(FastbootDevice* device, const std::vector<std::string>& args,
                         std::string* message);
+bool GetOsVersion(FastbootDevice* device, const std::vector<std::string>& args,
+                  std::string* message);
+bool GetVndkVersion(FastbootDevice* device, const std::vector<std::string>& args,
+                    std::string* message);
 bool GetProduct(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
 bool GetSerial(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
 bool GetSecure(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
@@ -64,6 +68,18 @@
 bool GetSnapshotUpdateStatus(FastbootDevice* device, const std::vector<std::string>& args,
                              std::string* message);
 bool GetCpuAbi(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
+bool GetSystemFingerprint(FastbootDevice* device, const std::vector<std::string>& args,
+                          std::string* message);
+bool GetVendorFingerprint(FastbootDevice* device, const std::vector<std::string>& args,
+                          std::string* message);
+bool GetDynamicPartition(FastbootDevice* device, const std::vector<std::string>& args,
+                         std::string* message);
+bool GetFirstApiLevel(FastbootDevice* device, const std::vector<std::string>& args,
+                      std::string* message);
+bool GetSecurityPatchLevel(FastbootDevice* device, const std::vector<std::string>& args,
+                           std::string* message);
+bool GetTrebleEnabled(FastbootDevice* device, const std::vector<std::string>& args,
+                      std::string* message);
 
 // Helpers for getvar all.
 std::vector<std::vector<std::string>> GetAllPartitionArgsWithSlot(FastbootDevice* device);
diff --git a/fastboot/fuzzy_fastboot/Android.bp b/fastboot/fuzzy_fastboot/Android.bp
index 277cc3a..bb54fd9 100644
--- a/fastboot/fuzzy_fastboot/Android.bp
+++ b/fastboot/fuzzy_fastboot/Android.bp
@@ -5,7 +5,7 @@
   srcs: [
     "main.cpp",
     "extensions.cpp",
-    "usb_transport_sniffer.cpp",
+    "transport_sniffer.cpp",
     "fixtures.cpp",
     "test_utils.cpp",
   ],
@@ -31,6 +31,8 @@
     "libext4_utils",
   ],
 
+  stl: "libc++_static",
+
   // Static libs (libfastboot2) shared library dependencies are not transitively included
   // This is needed to avoid link time errors when building for mac
   target: {
@@ -40,5 +42,13 @@
           "-framework IOKit",
       ],
     },
-  }
+  },
+
+  // Disable auto-generation of test config as this binary itself is not a test in the test suites,
+  // rather it is used by other tests.
+  auto_gen_config: false,
+  test_suites: [
+    "general-tests",
+    "vts-core",
+  ],
 }
diff --git a/fastboot/fuzzy_fastboot/fixtures.cpp b/fastboot/fuzzy_fastboot/fixtures.cpp
index bc13a8c..bd76ff4 100644
--- a/fastboot/fuzzy_fastboot/fixtures.cpp
+++ b/fastboot/fuzzy_fastboot/fixtures.cpp
@@ -48,12 +48,13 @@
 #include <gtest/gtest.h>
 
 #include "fastboot_driver.h"
+#include "tcp.h"
 #include "usb.h"
 
 #include "extensions.h"
 #include "fixtures.h"
 #include "test_utils.h"
-#include "usb_transport_sniffer.h"
+#include "transport_sniffer.h"
 
 using namespace std::literals::chrono_literals;
 
@@ -74,7 +75,14 @@
     return 0;
 }
 
+bool FastBootTest::IsFastbootOverTcp() {
+    // serial contains ":" is treated as host ip and port number
+    return (device_serial.find(":") != std::string::npos);
+}
+
 bool FastBootTest::UsbStillAvailible() {
+    if (IsFastbootOverTcp()) return true;
+
     // For some reason someone decided to prefix the path with "usb:"
     std::string prefix("usb:");
     if (std::equal(prefix.begin(), prefix.end(), device_path.begin())) {
@@ -113,15 +121,19 @@
         ASSERT_TRUE(UsbStillAvailible());  // The device disconnected
     }
 
-    const auto matcher = [](usb_ifc_info* info) -> int {
-        return MatchFastboot(info, device_serial);
-    };
-    for (int i = 0; i < MAX_USB_TRIES && !transport; i++) {
-        std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
-        if (usb)
-            transport = std::unique_ptr<UsbTransportSniffer>(
-                    new UsbTransportSniffer(std::move(usb), serial_port));
-        std::this_thread::sleep_for(std::chrono::milliseconds(10));
+    if (IsFastbootOverTcp()) {
+        ConnectTcpFastbootDevice();
+    } else {
+        const auto matcher = [](usb_ifc_info* info) -> int {
+            return MatchFastboot(info, device_serial);
+        };
+        for (int i = 0; i < MAX_USB_TRIES && !transport; i++) {
+            std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
+            if (usb)
+                transport = std::unique_ptr<TransportSniffer>(
+                        new TransportSniffer(std::move(usb), serial_port));
+            std::this_thread::sleep_for(std::chrono::milliseconds(10));
+        }
     }
 
     ASSERT_TRUE(transport);  // no nullptr
@@ -154,6 +166,8 @@
 
 // TODO, this should eventually be piped to a file instead of stdout
 void FastBootTest::TearDownSerial() {
+    if (IsFastbootOverTcp()) return;
+
     if (!transport) return;
     // One last read from serial
     transport->ProcessSerial();
@@ -167,9 +181,34 @@
     }
 }
 
+void FastBootTest::ConnectTcpFastbootDevice() {
+    std::size_t found = device_serial.find(":");
+    if (found != std::string::npos) {
+        for (int i = 0; i < MAX_TCP_TRIES && !transport; i++) {
+            std::string error;
+            std::unique_ptr<Transport> tcp(
+                    tcp::Connect(device_serial.substr(0, found), tcp::kDefaultPort, &error)
+                            .release());
+            if (tcp)
+                transport =
+                        std::unique_ptr<TransportSniffer>(new TransportSniffer(std::move(tcp), 0));
+            if (transport != nullptr) break;
+            std::this_thread::sleep_for(std::chrono::milliseconds(10));
+        }
+    }
+}
+
 void FastBootTest::ReconnectFastbootDevice() {
     fb.reset();
     transport.reset();
+
+    if (IsFastbootOverTcp()) {
+        ConnectTcpFastbootDevice();
+        device_path = cb_scratch;
+        fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
+        return;
+    }
+
     while (UsbStillAvailible())
         ;
     printf("WAITING FOR DEVICE\n");
@@ -180,8 +219,8 @@
     while (!transport) {
         std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
         if (usb) {
-            transport = std::unique_ptr<UsbTransportSniffer>(
-                    new UsbTransportSniffer(std::move(usb), serial_port));
+            transport = std::unique_ptr<TransportSniffer>(
+                    new TransportSniffer(std::move(usb), serial_port));
         }
         std::this_thread::sleep_for(1s);
     }
diff --git a/fastboot/fuzzy_fastboot/fixtures.h b/fastboot/fuzzy_fastboot/fixtures.h
index c71c897..2468868 100644
--- a/fastboot/fuzzy_fastboot/fixtures.h
+++ b/fastboot/fuzzy_fastboot/fixtures.h
@@ -31,7 +31,7 @@
 #include "fastboot_driver.h"
 
 #include "extensions.h"
-#include "usb_transport_sniffer.h"
+#include "transport_sniffer.h"
 
 namespace fastboot {
 
@@ -45,11 +45,14 @@
     static int serial_port;
     static std::string device_serial;
     static constexpr int MAX_USB_TRIES = 10;
+    static constexpr int MAX_TCP_TRIES = 6000;
 
     static int MatchFastboot(usb_ifc_info* info, const std::string& local_serial = "");
+    static bool IsFastbootOverTcp();
     bool UsbStillAvailible();
     bool UserSpaceFastboot();
     void ReconnectFastbootDevice();
+    void ConnectTcpFastbootDevice();
 
   protected:
     RetCode DownloadCommand(uint32_t size, std::string* response = nullptr,
@@ -64,7 +67,7 @@
     void TearDownSerial();
     void SetLockState(bool unlock, bool assert_change = true);
 
-    std::unique_ptr<UsbTransportSniffer> transport;
+    std::unique_ptr<TransportSniffer> transport;
     std::unique_ptr<FastBootDriver> fb;
 
   private:
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index a1d69d2..d9167e7 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -54,7 +54,7 @@
 #include "extensions.h"
 #include "fixtures.h"
 #include "test_utils.h"
-#include "usb_transport_sniffer.h"
+#include "transport_sniffer.h"
 
 namespace fastboot {
 
@@ -227,13 +227,6 @@
 
 TEST_F(LogicalPartitionCompliance, FastbootRebootTest) {
     ASSERT_TRUE(UserSpaceFastboot());
-    GTEST_LOG_(INFO) << "Rebooting to bootloader mode";
-    // Test 'fastboot reboot bootloader' from fastbootd
-    fb->RebootTo("bootloader");
-
-    // Test fastboot reboot fastboot from bootloader
-    ReconnectFastbootDevice();
-    ASSERT_FALSE(UserSpaceFastboot());
     GTEST_LOG_(INFO) << "Rebooting back to fastbootd mode";
     fb->RebootTo("fastboot");
 
@@ -268,23 +261,6 @@
     GTEST_LOG_(INFO) << "Flashing a logical partition..";
     EXPECT_EQ(fb->FlashPartition(test_partition_name, buf), SUCCESS)
             << "flash logical -partition failed";
-    GTEST_LOG_(INFO) << "Rebooting to bootloader mode";
-    // Reboot to bootloader mode and attempt to flash the logical partitions
-    fb->RebootTo("bootloader");
-
-    ReconnectFastbootDevice();
-    ASSERT_FALSE(UserSpaceFastboot());
-    GTEST_LOG_(INFO) << "Attempt to flash a logical partition..";
-    EXPECT_EQ(fb->FlashPartition(test_partition_name, buf), DEVICE_FAIL)
-            << "flash logical partition must fail in bootloader";
-    GTEST_LOG_(INFO) << "Rebooting back to fastbootd mode";
-    fb->RebootTo("fastboot");
-
-    ReconnectFastbootDevice();
-    ASSERT_TRUE(UserSpaceFastboot());
-    GTEST_LOG_(INFO) << "Testing 'fastboot delete-logical-partition' command";
-    EXPECT_EQ(fb->DeletePartition(test_partition_name), SUCCESS)
-            << "delete logical-partition failed";
 }
 
 // Conformance tests
@@ -1756,16 +1732,19 @@
     }
 
     setbuf(stdout, NULL);  // no buffering
-    printf("<Waiting for Device>\n");
-    const auto matcher = [](usb_ifc_info* info) -> int {
-        return fastboot::FastBootTest::MatchFastboot(info, fastboot::FastBootTest::device_serial);
-    };
-    Transport* transport = nullptr;
-    while (!transport) {
-        transport = usb_open(matcher);
-        std::this_thread::sleep_for(std::chrono::milliseconds(10));
+
+    if (!fastboot::FastBootTest::IsFastbootOverTcp()) {
+        printf("<Waiting for Device>\n");
+        const auto matcher = [](usb_ifc_info* info) -> int {
+            return fastboot::FastBootTest::MatchFastboot(info, fastboot::FastBootTest::device_serial);
+        };
+        Transport* transport = nullptr;
+        while (!transport) {
+            transport = usb_open(matcher);
+            std::this_thread::sleep_for(std::chrono::milliseconds(10));
+        }
+        transport->Close();
     }
-    transport->Close();
 
     if (args.find("serial_port") != args.end()) {
         fastboot::FastBootTest::serial_port = fastboot::ConfigureSerial(args.at("serial_port"));
diff --git a/fastboot/fuzzy_fastboot/usb_transport_sniffer.cpp b/fastboot/fuzzy_fastboot/transport_sniffer.cpp
similarity index 91%
rename from fastboot/fuzzy_fastboot/usb_transport_sniffer.cpp
rename to fastboot/fuzzy_fastboot/transport_sniffer.cpp
index 7c595f4..b55ffd3 100644
--- a/fastboot/fuzzy_fastboot/usb_transport_sniffer.cpp
+++ b/fastboot/fuzzy_fastboot/transport_sniffer.cpp
@@ -1,4 +1,4 @@
-#include "usb_transport_sniffer.h"
+#include "transport_sniffer.h"
 #include <android-base/stringprintf.h>
 #include <sys/select.h>
 #include <sys/time.h>
@@ -8,15 +8,15 @@
 
 namespace fastboot {
 
-UsbTransportSniffer::UsbTransportSniffer(std::unique_ptr<UsbTransport> transport,
+TransportSniffer::TransportSniffer(std::unique_ptr<Transport> transport,
                                          const int serial_fd)
     : transport_(std::move(transport)), serial_fd_(serial_fd) {}
 
-UsbTransportSniffer::~UsbTransportSniffer() {
+TransportSniffer::~TransportSniffer() {
     Close();
 }
 
-ssize_t UsbTransportSniffer::Read(void* data, size_t len) {
+ssize_t TransportSniffer::Read(void* data, size_t len) {
     ProcessSerial();
 
     ssize_t ret = transport_->Read(data, len);
@@ -37,7 +37,7 @@
     return ret;
 }
 
-ssize_t UsbTransportSniffer::Write(const void* data, size_t len) {
+ssize_t TransportSniffer::Write(const void* data, size_t len) {
     ProcessSerial();
 
     size_t ret = transport_->Write(data, len);
@@ -58,11 +58,11 @@
     return ret;
 }
 
-int UsbTransportSniffer::Close() {
+int TransportSniffer::Close() {
     return transport_->Close();
 }
 
-int UsbTransportSniffer::Reset() {
+int TransportSniffer::Reset() {
     ProcessSerial();
     int ret = transport_->Reset();
     std::vector<char> buf;
@@ -72,7 +72,7 @@
     return ret;
 }
 
-const std::vector<UsbTransportSniffer::Event> UsbTransportSniffer::Transfers() {
+const std::vector<TransportSniffer::Event> TransportSniffer::Transfers() {
     return transfers_;
 }
 
@@ -81,7 +81,7 @@
  * the failure. This method will look through its log of captured events, and
  * create a clean printable string of everything that happened.
  */
-std::string UsbTransportSniffer::CreateTrace() {
+std::string TransportSniffer::CreateTrace() {
     std::string ret;
 
     const auto no_print = [](char c) -> bool { return !isprint(c); };
@@ -158,7 +158,7 @@
 
 // This is a quick call to flush any UART logs the device might have sent
 // to our internal event log. It will wait up to 10ms for data to appear
-void UsbTransportSniffer::ProcessSerial() {
+void TransportSniffer::ProcessSerial() {
     if (serial_fd_ <= 0) return;
 
     fd_set set;
diff --git a/fastboot/fuzzy_fastboot/usb_transport_sniffer.h b/fastboot/fuzzy_fastboot/transport_sniffer.h
similarity index 90%
rename from fastboot/fuzzy_fastboot/usb_transport_sniffer.h
rename to fastboot/fuzzy_fastboot/transport_sniffer.h
index 8119aea..2cbb9fe 100644
--- a/fastboot/fuzzy_fastboot/usb_transport_sniffer.h
+++ b/fastboot/fuzzy_fastboot/transport_sniffer.h
@@ -42,12 +42,12 @@
 /* A special class for sniffing reads and writes
  *
  * A useful debugging tool is to see the raw fastboot transactions going between
- * the host and device. This class wraps the UsbTransport class, and snoops and saves
+ * the host and device. This class is a special subclass of Transport that snoops and saves
  * all the transactions going on. Additionally, if there is a console serial port
  * from the device, this class can monitor it as well and capture the interleaving of
  * transport transactions and UART log messages.
  */
-class UsbTransportSniffer : public UsbTransport {
+class TransportSniffer : public Transport {
   public:
     enum EventType {
         READ,
@@ -67,8 +67,8 @@
         const std::vector<char> buf;
     };
 
-    UsbTransportSniffer(std::unique_ptr<UsbTransport> transport, const int serial_fd = 0);
-    ~UsbTransportSniffer() override;
+    TransportSniffer(std::unique_ptr<Transport> transport, const int serial_fd = 0);
+    ~TransportSniffer() override;
 
     virtual ssize_t Read(void* data, size_t len) override;
     virtual ssize_t Write(const void* data, size_t len) override;
@@ -81,7 +81,7 @@
 
   private:
     std::vector<Event> transfers_;
-    std::unique_ptr<UsbTransport> transport_;
+    std::unique_ptr<Transport> transport_;
     const int serial_fd_;
 };
 
diff --git a/fastboot/tcp.cpp b/fastboot/tcp.cpp
index dd6fbf8..dca306f 100644
--- a/fastboot/tcp.cpp
+++ b/fastboot/tcp.cpp
@@ -64,6 +64,7 @@
     ssize_t Read(void* data, size_t length) override;
     ssize_t Write(const void* data, size_t length) override;
     int Close() override;
+    int Reset() override;
 
   private:
     explicit TcpTransport(std::unique_ptr<Socket> sock) : socket_(std::move(sock)) {}
@@ -178,6 +179,10 @@
     return result;
 }
 
+int TcpTransport::Reset() {
+    return 0;
+}
+
 std::unique_ptr<Transport> Connect(const std::string& hostname, int port, std::string* error) {
     return internal::Connect(Socket::NewClient(Socket::Protocol::kTcp, hostname, port, error),
                              error);
diff --git a/fastboot/transport.h b/fastboot/transport.h
index 96b90d2..de0cc92 100644
--- a/fastboot/transport.h
+++ b/fastboot/transport.h
@@ -36,6 +36,8 @@
     // Closes the underlying transport. Returns 0 on success.
     virtual int Close() = 0;
 
+    virtual int Reset() = 0;
+
     // Blocks until the transport disconnects. Transports that don't support
     // this will return immediately. Returns 0 on success.
     virtual int WaitForDisconnect() { return 0; }
diff --git a/fastboot/udp.cpp b/fastboot/udp.cpp
index 53fb347..308c96c 100644
--- a/fastboot/udp.cpp
+++ b/fastboot/udp.cpp
@@ -109,6 +109,7 @@
     ssize_t Read(void* data, size_t length) override;
     ssize_t Write(const void* data, size_t length) override;
     int Close() override;
+    int Reset() override;
 
   private:
     explicit UdpTransport(std::unique_ptr<Socket> socket) : socket_(std::move(socket)) {}
@@ -370,6 +371,10 @@
     return result;
 }
 
+int UdpTransport::Reset() {
+    return 0;
+}
+
 std::unique_ptr<Transport> Connect(const std::string& hostname, int port, std::string* error) {
     return internal::Connect(Socket::NewClient(Socket::Protocol::kUdp, hostname, port, error),
                              error);
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 34c64d2..f5daf91 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -91,10 +91,15 @@
     },
     header_libs: [
         "libfiemap_headers",
+        "libstorage_literals_headers",
     ],
     export_header_lib_headers: [
         "libfiemap_headers",
     ],
+    required: [
+        "e2freefrag",
+        "e2fsdroid",
+    ],
 }
 
 // Two variants of libfs_mgr are provided: libfs_mgr and libfs_mgr_binder.
@@ -165,7 +170,7 @@
         "libcrypto",
         "libext4_utils",
         "libfec",
-        "libfs_mgr",
+        "libfs_mgr_binder",
         "liblog",
         "liblp",
         "libselinux",
@@ -187,4 +192,26 @@
             ],
         },
     },
+    required: [
+        "clean_scratch_files",
+    ],
+}
+
+cc_binary {
+    name: "clean_scratch_files",
+    defaults: ["fs_mgr_defaults"],
+    shared_libs: [
+        "libbase",
+        "libfs_mgr_binder",
+    ],
+    srcs: [
+        "clean_scratch_files.cpp",
+    ],
+    product_variables: {
+        debuggable: {
+            init_rc: [
+                "clean_scratch_files.rc",
+            ],
+        },
+    },
 }
diff --git a/fs_mgr/TEST_MAPPING b/fs_mgr/TEST_MAPPING
new file mode 100644
index 0000000..705d4e3
--- /dev/null
+++ b/fs_mgr/TEST_MAPPING
@@ -0,0 +1,19 @@
+{
+  "presubmit": [
+    {
+      "name": "libdm_test"
+    },
+    {
+      "name": "liblp_test"
+    },
+    {
+      "name": "fiemap_image_test_presubmit"
+    },
+    {
+      "name": "fiemap_writer_test"
+    },
+    {
+      "name": "vts_libsnapshot_test_presubmit"
+    }
+  ]
+}
diff --git a/fs_mgr/clean_scratch_files.cpp b/fs_mgr/clean_scratch_files.cpp
new file mode 100644
index 0000000..42fe35a
--- /dev/null
+++ b/fs_mgr/clean_scratch_files.cpp
@@ -0,0 +1,22 @@
+/*
+ * 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 <fs_mgr_overlayfs.h>
+
+int main() {
+    android::fs_mgr::CleanupOldScratchFiles();
+    return 0;
+}
diff --git a/fs_mgr/clean_scratch_files.rc b/fs_mgr/clean_scratch_files.rc
new file mode 100644
index 0000000..738d1aa
--- /dev/null
+++ b/fs_mgr/clean_scratch_files.rc
@@ -0,0 +1,2 @@
+on post-fs-data && property:ro.debuggable=1
+    exec_background - root root -- clean_scratch_files
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 15c9dfb..aad9f77 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -78,6 +78,7 @@
 #define F2FS_FSCK_BIN   "/system/bin/fsck.f2fs"
 #define MKSWAP_BIN      "/system/bin/mkswap"
 #define TUNE2FS_BIN     "/system/bin/tune2fs"
+#define RESIZE2FS_BIN "/system/bin/resize2fs"
 
 #define FSCK_LOG_FILE   "/dev/fscklogs/log"
 
@@ -86,6 +87,7 @@
 #define ZRAM_BACK_DEV   "/sys/block/zram0/backing_dev"
 
 #define SYSFS_EXT4_VERITY "/sys/fs/ext4/features/verity"
+#define SYSFS_EXT4_CASEFOLD "/sys/fs/ext4/features/casefold"
 
 // FIXME: this should be in system/extras
 #define EXT4_FEATURE_COMPAT_STABLE_INODES 0x0800
@@ -100,6 +102,7 @@
 using android::base::unique_fd;
 using android::dm::DeviceMapper;
 using android::dm::DmDeviceState;
+using android::dm::DmTargetLinear;
 
 // Realistically, this file should be part of the android::fs_mgr namespace;
 using namespace android::fs_mgr;
@@ -123,6 +126,8 @@
     FS_STAT_SET_RESERVED_BLOCKS_FAILED = 0x20000,
     FS_STAT_ENABLE_ENCRYPTION_FAILED = 0x40000,
     FS_STAT_ENABLE_VERITY_FAILED = 0x80000,
+    FS_STAT_ENABLE_CASEFOLD_FAILED = 0x100000,
+    FS_STAT_ENABLE_METADATA_CSUM_FAILED = 0x200000,
 };
 
 static void log_fs_stat(const std::string& blk_device, int fs_stat) {
@@ -231,10 +236,11 @@
             LINFO << "Running " << E2FSCK_BIN << " on " << realpath(blk_device);
             if (should_force_check(*fs_stat)) {
                 ret = logwrap_fork_execvp(ARRAY_SIZE(e2fsck_forced_argv), e2fsck_forced_argv,
-                                          &status, false, LOG_KLOG | LOG_FILE, true, FSCK_LOG_FILE);
+                                          &status, false, LOG_KLOG | LOG_FILE, false,
+                                          FSCK_LOG_FILE);
             } else {
                 ret = logwrap_fork_execvp(ARRAY_SIZE(e2fsck_argv), e2fsck_argv, &status, false,
-                                          LOG_KLOG | LOG_FILE, true, FSCK_LOG_FILE);
+                                          LOG_KLOG | LOG_FILE, false, FSCK_LOG_FILE);
             }
 
             if (ret < 0) {
@@ -253,11 +259,11 @@
         if (should_force_check(*fs_stat)) {
             LINFO << "Running " << F2FS_FSCK_BIN << " -f " << realpath(blk_device);
             ret = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_forced_argv), f2fs_fsck_forced_argv,
-                                      &status, false, LOG_KLOG | LOG_FILE, true, FSCK_LOG_FILE);
+                                      &status, false, LOG_KLOG | LOG_FILE, false, FSCK_LOG_FILE);
         } else {
             LINFO << "Running " << F2FS_FSCK_BIN << " -a " << realpath(blk_device);
             ret = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_argv), f2fs_fsck_argv, &status, false,
-                                      LOG_KLOG | LOG_FILE, true, FSCK_LOG_FILE);
+                                      LOG_KLOG | LOG_FILE, false, FSCK_LOG_FILE);
         }
         if (ret < 0) {
             /* No need to check for error in fork, we can't really handle it now */
@@ -332,10 +338,10 @@
     return access(TUNE2FS_BIN, X_OK) == 0;
 }
 
-static bool run_tune2fs(const char* argv[], int argc) {
+static bool run_command(const char* argv[], int argc) {
     int ret;
 
-    ret = logwrap_fork_execvp(argc, argv, nullptr, false, LOG_KLOG, true, nullptr);
+    ret = logwrap_fork_execvp(argc, argv, nullptr, false, LOG_KLOG, false, nullptr);
     return ret == 0;
 }
 
@@ -344,6 +350,7 @@
                        const struct ext4_super_block* sb, int* fs_stat) {
     bool has_quota = (sb->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_QUOTA)) != 0;
     bool want_quota = entry.fs_mgr_flags.quota;
+    bool want_projid = android::base::GetBoolProperty("ro.emulated_storage.projid", false);
 
     if (has_quota == want_quota) {
         return;
@@ -360,15 +367,19 @@
     if (want_quota) {
         LINFO << "Enabling quotas on " << blk_device;
         argv[1] = "-Oquota";
-        argv[2] = "-Qusrquota,grpquota";
+        // Once usr/grp unneeded, make just prjquota to save overhead
+        if (want_projid)
+            argv[2] = "-Qusrquota,grpquota,prjquota";
+        else
+            argv[2] = "-Qusrquota,grpquota";
         *fs_stat |= FS_STAT_QUOTA_ENABLED;
     } else {
         LINFO << "Disabling quotas on " << blk_device;
         argv[1] = "-O^quota";
-        argv[2] = "-Q^usrquota,^grpquota";
+        argv[2] = "-Q^usrquota,^grpquota,^prjquota";
     }
 
-    if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
+    if (!run_command(argv, ARRAY_SIZE(argv))) {
         LERROR << "Failed to run " TUNE2FS_BIN " to " << (want_quota ? "enable" : "disable")
                << " quotas on " << blk_device;
         *fs_stat |= FS_STAT_TOGGLE_QUOTAS_FAILED;
@@ -410,7 +421,7 @@
     const char* argv[] = {
             TUNE2FS_BIN,       "-r", reserved_blocks_str.c_str(), "-g", reserved_gid_str.c_str(),
             blk_device.c_str()};
-    if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
+    if (!run_command(argv, ARRAY_SIZE(argv))) {
         LERROR << "Failed to run " TUNE2FS_BIN " to set the number of reserved blocks on "
                << blk_device;
         *fs_stat |= FS_STAT_SET_RESERVED_BLOCKS_FAILED;
@@ -454,7 +465,7 @@
     const char* argv[] = {TUNE2FS_BIN, flag_arg.c_str(), blk_device.c_str()};
 
     LINFO << "Enabling ext4 flags " << flags << " on " << blk_device;
-    if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
+    if (!run_command(argv, ARRAY_SIZE(argv))) {
         LERROR << "Failed to run " TUNE2FS_BIN " to enable "
                << "ext4 flags " << flags << " on " << blk_device;
         *fs_stat |= FS_STAT_ENABLE_ENCRYPTION_FAILED;
@@ -491,13 +502,93 @@
     LINFO << "Enabling ext4 verity on " << blk_device;
 
     const char* argv[] = {TUNE2FS_BIN, "-O", "verity", blk_device.c_str()};
-    if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
+    if (!run_command(argv, ARRAY_SIZE(argv))) {
         LERROR << "Failed to run " TUNE2FS_BIN " to enable "
                << "ext4 verity on " << blk_device;
         *fs_stat |= FS_STAT_ENABLE_VERITY_FAILED;
     }
 }
 
+// Enable casefold if needed.
+static void tune_casefold(const std::string& blk_device, const struct ext4_super_block* sb,
+                          int* fs_stat) {
+    bool has_casefold =
+            (sb->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_INCOMPAT_CASEFOLD)) != 0;
+    bool wants_casefold = android::base::GetBoolProperty("ro.emulated_storage.casefold", false);
+
+    if (!wants_casefold || has_casefold) return;
+
+    std::string casefold_support;
+    if (!android::base::ReadFileToString(SYSFS_EXT4_CASEFOLD, &casefold_support)) {
+        LERROR << "Failed to open " << SYSFS_EXT4_CASEFOLD;
+        return;
+    }
+
+    if (!(android::base::Trim(casefold_support) == "supported")) {
+        LERROR << "Current ext4 casefolding not supported by kernel";
+        return;
+    }
+
+    if (!tune2fs_available()) {
+        LERROR << "Unable to enable ext4 casefold on " << blk_device
+               << " because " TUNE2FS_BIN " is missing";
+        return;
+    }
+
+    LINFO << "Enabling ext4 casefold on " << blk_device;
+
+    const char* argv[] = {TUNE2FS_BIN, "-O", "casefold", "-E", "encoding=utf8", blk_device.c_str()};
+    if (!run_command(argv, ARRAY_SIZE(argv))) {
+        LERROR << "Failed to run " TUNE2FS_BIN " to enable "
+               << "ext4 casefold on " << blk_device;
+        *fs_stat |= FS_STAT_ENABLE_CASEFOLD_FAILED;
+    }
+}
+
+static bool resize2fs_available(void) {
+    return access(RESIZE2FS_BIN, X_OK) == 0;
+}
+
+// Enable metadata_csum
+static void tune_metadata_csum(const std::string& blk_device, const FstabEntry& entry,
+                               const struct ext4_super_block* sb, int* fs_stat) {
+    bool has_meta_csum =
+            (sb->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) != 0;
+    bool want_meta_csum = entry.fs_mgr_flags.ext_meta_csum;
+
+    if (has_meta_csum || !want_meta_csum) return;
+
+    if (!tune2fs_available()) {
+        LERROR << "Unable to enable metadata_csum on " << blk_device
+               << " because " TUNE2FS_BIN " is missing";
+        return;
+    }
+    if (!resize2fs_available()) {
+        LERROR << "Unable to enable metadata_csum on " << blk_device
+               << " because " RESIZE2FS_BIN " is missing";
+        return;
+    }
+
+    LINFO << "Enabling ext4 metadata_csum on " << blk_device;
+
+    // requires to give last_fsck_time to current to avoid insane time.
+    // otherwise, tune2fs won't enable metadata_csum.
+    std::string now = std::to_string(time(0));
+    const char* tune2fs_args[] = {TUNE2FS_BIN, "-O",        "metadata_csum,64bit,extent",
+                                  "-T",        now.c_str(), blk_device.c_str()};
+    const char* resize2fs_args[] = {RESIZE2FS_BIN, "-b", blk_device.c_str()};
+
+    if (!run_command(tune2fs_args, ARRAY_SIZE(tune2fs_args))) {
+        LERROR << "Failed to run " TUNE2FS_BIN " to enable "
+               << "ext4 metadata_csum on " << blk_device;
+        *fs_stat |= FS_STAT_ENABLE_METADATA_CSUM_FAILED;
+    } else if (!run_command(resize2fs_args, ARRAY_SIZE(resize2fs_args))) {
+        LERROR << "Failed to run " RESIZE2FS_BIN " to enable "
+               << "ext4 metadata_csum on " << blk_device;
+        *fs_stat |= FS_STAT_ENABLE_METADATA_CSUM_FAILED;
+    }
+}
+
 // Read the primary superblock from an f2fs filesystem.  On failure return
 // false.  If it's not an f2fs filesystem, also set FS_STAT_INVALID_MAGIC.
 #define F2FS_BLKSIZE 4096
@@ -588,13 +679,15 @@
 
     if (is_extfs(entry.fs_type) &&
         (entry.reserved_size != 0 || entry.fs_mgr_flags.file_encryption ||
-         entry.fs_mgr_flags.fs_verity)) {
+         entry.fs_mgr_flags.fs_verity || entry.fs_mgr_flags.ext_meta_csum)) {
         struct ext4_super_block sb;
 
         if (read_ext4_superblock(blk_device, &sb, &fs_stat)) {
             tune_reserved_size(blk_device, entry, &sb, &fs_stat);
             tune_encrypt(blk_device, entry, &sb, &fs_stat);
             tune_verity(blk_device, entry, &sb, &fs_stat);
+            tune_casefold(blk_device, &sb, &fs_stat);
+            tune_metadata_csum(blk_device, entry, &sb, &fs_stat);
         }
     }
 
@@ -842,7 +935,7 @@
 }
 
 static bool should_use_metadata_encryption(const FstabEntry& entry) {
-    return !entry.key_dir.empty() &&
+    return !entry.metadata_key_dir.empty() &&
            (entry.fs_mgr_flags.file_encryption || entry.fs_mgr_flags.force_fde_or_fbe);
 }
 
@@ -910,6 +1003,10 @@
     return true;
 }
 
+static bool SupportsCheckpoint(FstabEntry* entry) {
+    return entry->fs_mgr_flags.checkpoint_blk || entry->fs_mgr_flags.checkpoint_fs;
+}
+
 class CheckpointManager {
   public:
     CheckpointManager(int needs_checkpoint = -1) : needs_checkpoint_(needs_checkpoint) {}
@@ -926,7 +1023,7 @@
     }
 
     bool Update(FstabEntry* entry, const std::string& block_device = std::string()) {
-        if (!entry->fs_mgr_flags.checkpoint_blk && !entry->fs_mgr_flags.checkpoint_fs) {
+        if (!SupportsCheckpoint(entry)) {
             return true;
         }
 
@@ -947,7 +1044,7 @@
     }
 
     bool Revert(FstabEntry* entry) {
-        if (!entry->fs_mgr_flags.checkpoint_blk && !entry->fs_mgr_flags.checkpoint_fs) {
+        if (!SupportsCheckpoint(entry)) {
             return true;
         }
 
@@ -1066,6 +1163,83 @@
     }
 }
 
+static constexpr const char* kUserdataWrapperName = "userdata-wrapper";
+
+static void WrapUserdata(FstabEntry* entry, dev_t dev, const std::string& block_device) {
+    DeviceMapper& dm = DeviceMapper::Instance();
+    if (dm.GetState(kUserdataWrapperName) != DmDeviceState::INVALID) {
+        // This will report failure for us. If we do fail to get the path,
+        // we leave the device unwrapped.
+        dm.GetDmDevicePathByName(kUserdataWrapperName, &entry->blk_device);
+        return;
+    }
+
+    unique_fd fd(open(block_device.c_str(), O_RDONLY | O_CLOEXEC));
+    if (fd < 0) {
+        PLOG(ERROR) << "open failed: " << entry->blk_device;
+        return;
+    }
+
+    auto dev_str = android::base::StringPrintf("%u:%u", major(dev), minor(dev));
+    uint64_t sectors = get_block_device_size(fd) / 512;
+
+    android::dm::DmTable table;
+    table.Emplace<DmTargetLinear>(0, sectors, dev_str, 0);
+
+    std::string dm_path;
+    if (!dm.CreateDevice(kUserdataWrapperName, table, &dm_path, 20s)) {
+        LOG(ERROR) << "Failed to create userdata wrapper device";
+        return;
+    }
+    entry->blk_device = dm_path;
+}
+
+// When using Virtual A/B, partitions can be backed by /data and mapped with
+// device-mapper in first-stage init. This can happen when merging an OTA or
+// when using adb remount to house "scratch". In this case, /data cannot be
+// mounted directly off the userdata block device, and e2fsck will refuse to
+// scan it, because the kernel reports the block device as in-use.
+//
+// As a workaround, when mounting /data, we create a trivial dm-linear wrapper
+// if the underlying block device already has dependencies. Note that we make
+// an exception for metadata-encrypted devices, since dm-default-key is already
+// a wrapper.
+static void WrapUserdataIfNeeded(FstabEntry* entry, const std::string& actual_block_device = {}) {
+    const auto& block_device =
+            actual_block_device.empty() ? entry->blk_device : actual_block_device;
+    if (entry->mount_point != "/data" || !entry->metadata_key_dir.empty() ||
+        android::base::StartsWith(block_device, "/dev/block/dm-")) {
+        return;
+    }
+
+    struct stat st;
+    if (stat(block_device.c_str(), &st) < 0) {
+        PLOG(ERROR) << "stat failed: " << block_device;
+        return;
+    }
+
+    std::string path = android::base::StringPrintf("/sys/dev/block/%u:%u/holders",
+                                                   major(st.st_rdev), minor(st.st_rdev));
+    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir);
+    if (!dir) {
+        PLOG(ERROR) << "opendir failed: " << path;
+        return;
+    }
+
+    struct dirent* d;
+    bool has_holders = false;
+    while ((d = readdir(dir.get())) != nullptr) {
+        if (strcmp(d->d_name, ".") != 0 && strcmp(d->d_name, "..") != 0) {
+            has_holders = true;
+            break;
+        }
+    }
+
+    if (has_holders) {
+        WrapUserdata(entry, st.st_rdev, block_device);
+    }
+}
+
 static bool IsMountPointMounted(const std::string& mount_point) {
     // Check if this is already mounted.
     Fstab fstab;
@@ -1088,7 +1262,9 @@
         return FS_MGR_MNTALL_FAIL;
     }
 
-    for (size_t i = 0; i < fstab->size(); i++) {
+    // Keep i int to prevent unsigned integer overflow from (i = top_idx - 1),
+    // where top_idx is 0. It will give SIGABRT
+    for (int i = 0; i < static_cast<int>(fstab->size()); i++) {
         auto& current_entry = (*fstab)[i];
 
         // If a filesystem should have been mounted in the first stage, we
@@ -1143,6 +1319,8 @@
             }
         }
 
+        WrapUserdataIfNeeded(&current_entry);
+
         if (!checkpoint_manager.Update(&current_entry)) {
             continue;
         }
@@ -1362,6 +1540,7 @@
 }
 
 static bool fs_mgr_unmount_all_data_mounts(const std::string& block_device) {
+    LINFO << __FUNCTION__ << "(): about to umount everything on top of " << block_device;
     Timer t;
     // TODO(b/135984674): should be configured via a read-only property.
     std::chrono::milliseconds timeout = 5s;
@@ -1369,22 +1548,34 @@
         bool umount_done = true;
         Fstab proc_mounts;
         if (!ReadFstabFromFile("/proc/mounts", &proc_mounts)) {
-            LERROR << "Can't read /proc/mounts";
+            LERROR << __FUNCTION__ << "(): Can't read /proc/mounts";
             return false;
         }
         // Now proceed with other bind mounts on top of /data.
         for (const auto& entry : proc_mounts) {
             if (entry.blk_device == block_device) {
                 if (umount2(entry.mount_point.c_str(), 0) != 0) {
+                    PERROR << __FUNCTION__ << "(): Failed to umount " << entry.mount_point;
                     umount_done = false;
                 }
             }
         }
         if (umount_done) {
-            LINFO << "Unmounting /data took " << t;
+            LINFO << __FUNCTION__ << "(): Unmounting /data took " << t;
             return true;
         }
         if (t.duration() > timeout) {
+            LERROR << __FUNCTION__ << "(): Timed out unmounting all mounts on " << block_device;
+            Fstab remaining_mounts;
+            if (!ReadFstabFromFile("/proc/mounts", &remaining_mounts)) {
+                LERROR << __FUNCTION__ << "(): Can't read /proc/mounts";
+            } else {
+                LERROR << __FUNCTION__ << "(): Following mounts remaining";
+                for (const auto& e : remaining_mounts) {
+                    LERROR << __FUNCTION__ << "(): mount point: " << e.mount_point
+                           << " block device: " << e.blk_device;
+                }
+            }
             return false;
         }
         std::this_thread::sleep_for(50ms);
@@ -1400,6 +1591,9 @@
     }
     std::string block_device;
     if (auto entry = GetEntryForMountPoint(&proc_mounts, "/data"); entry != nullptr) {
+        // Note: we don't care about a userdata wrapper here, since it's safe
+        // to remount on top of the bow device instead, there will be no
+        // conflicts.
         block_device = entry->blk_device;
     } else {
         LERROR << "/data is not mounted";
@@ -1410,18 +1604,20 @@
         LERROR << "Can't find /data in fstab";
         return -1;
     }
-    if (!fstab_entry->fs_mgr_flags.checkpoint_blk && !fstab_entry->fs_mgr_flags.checkpoint_fs) {
+    bool force_umount = GetBoolProperty("sys.init.userdata_remount.force_umount", false);
+    if (force_umount) {
+        LINFO << "Will force an umount of userdata even if it's not required";
+    }
+    if (!force_umount && !SupportsCheckpoint(fstab_entry)) {
         LINFO << "Userdata doesn't support checkpointing. Nothing to do";
         return 0;
     }
     CheckpointManager checkpoint_manager;
-    if (!checkpoint_manager.NeedsCheckpoint()) {
+    if (!force_umount && !checkpoint_manager.NeedsCheckpoint()) {
         LINFO << "Checkpointing not needed. Don't remount";
         return 0;
     }
-    bool force_umount_for_f2fs =
-            GetBoolProperty("sys.init.userdata_remount.force_umount_f2fs", false);
-    if (fstab_entry->fs_mgr_flags.checkpoint_fs && !force_umount_for_f2fs) {
+    if (!force_umount && fstab_entry->fs_mgr_flags.checkpoint_fs) {
         // Userdata is f2fs, simply remount it.
         if (!checkpoint_manager.Update(fstab_entry)) {
             LERROR << "Failed to remount userdata in checkpointing mode";
@@ -1516,6 +1712,8 @@
             }
         }
 
+        WrapUserdataIfNeeded(&fstab_entry, n_blk_device);
+
         if (!checkpoint_manager.Update(&fstab_entry, n_blk_device)) {
             LERROR << "Could not set up checkpoint partition, skipping!";
             continue;
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index 0dcb9fe..ea9c957 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -151,6 +151,10 @@
             LINFO << "Skipping zero-length logical partition: " << GetPartitionName(partition);
             continue;
         }
+        if (partition.attributes & LP_PARTITION_ATTR_DISABLED) {
+            LINFO << "Skipping disabled partition: " << GetPartitionName(partition);
+            continue;
+        }
 
         params.partition = &partition;
 
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index acf4d7b..7be024f 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -24,6 +24,7 @@
 #include <cutils/partition_utils.h>
 #include <sys/mount.h>
 
+#include <android-base/properties.h>
 #include <android-base/unique_fd.h>
 #include <ext4_utils/ext4.h>
 #include <ext4_utils/ext4_utils.h>
@@ -57,7 +58,7 @@
 }
 
 static int format_ext4(const std::string& fs_blkdev, const std::string& fs_mnt_point,
-                       bool crypt_footer) {
+                       bool crypt_footer, bool needs_projid, bool needs_metadata_csum) {
     uint64_t dev_sz;
     int rc = 0;
 
@@ -72,12 +73,36 @@
     }
 
     std::string size_str = std::to_string(dev_sz / 4096);
-    const char* const mke2fs_args[] = {
-            "/system/bin/mke2fs", "-t",   "ext4", "-b", "4096", fs_blkdev.c_str(),
-            size_str.c_str(),     nullptr};
 
-    rc = logwrap_fork_execvp(arraysize(mke2fs_args), mke2fs_args, nullptr, false, LOG_KLOG, true,
-                             nullptr);
+    std::vector<const char*> mke2fs_args = {"/system/bin/mke2fs", "-t", "ext4", "-b", "4096"};
+
+    // Project ID's require wider inodes. The Quotas themselves are enabled by tune2fs during boot.
+    if (needs_projid) {
+        mke2fs_args.push_back("-I");
+        mke2fs_args.push_back("512");
+    }
+    // casefolding is enabled via tune2fs during boot.
+
+    if (needs_metadata_csum) {
+        mke2fs_args.push_back("-O");
+        mke2fs_args.push_back("metadata_csum");
+        // tune2fs recommends to enable 64bit and extent:
+        //  Extents are not enabled.  The file extent tree can be checksummed,
+        //  whereas block maps cannot. Not enabling extents reduces the coverage
+        //  of metadata checksumming.  Re-run with -O extent to rectify.
+        //  64-bit filesystem support is not enabled.  The larger fields afforded
+        //  by this feature enable full-strength checksumming.  Run resize2fs -b to rectify.
+        mke2fs_args.push_back("-O");
+        mke2fs_args.push_back("64bit");
+        mke2fs_args.push_back("-O");
+        mke2fs_args.push_back("extent");
+    }
+
+    mke2fs_args.push_back(fs_blkdev.c_str());
+    mke2fs_args.push_back(size_str.c_str());
+
+    rc = logwrap_fork_execvp(mke2fs_args.size(), mke2fs_args.data(), nullptr, false, LOG_KLOG,
+                             false, nullptr);
     if (rc) {
         LERROR << "mke2fs returned " << rc;
         return rc;
@@ -87,7 +112,7 @@
             "/system/bin/e2fsdroid", "-e", "-a", fs_mnt_point.c_str(), fs_blkdev.c_str(), nullptr};
 
     rc = logwrap_fork_execvp(arraysize(e2fsdroid_args), e2fsdroid_args, nullptr, false, LOG_KLOG,
-                             true, nullptr);
+                             false, nullptr);
     if (rc) {
         LERROR << "e2fsdroid returned " << rc;
     }
@@ -95,7 +120,8 @@
     return rc;
 }
 
-static int format_f2fs(const std::string& fs_blkdev, uint64_t dev_sz, bool crypt_footer) {
+static int format_f2fs(const std::string& fs_blkdev, uint64_t dev_sz, bool crypt_footer,
+                       bool needs_projid, bool needs_casefold) {
     if (!dev_sz) {
         int rc = get_dev_sz(fs_blkdev, &dev_sz);
         if (rc) {
@@ -109,26 +135,41 @@
     }
 
     std::string size_str = std::to_string(dev_sz / 4096);
-    // clang-format off
-    const char* const args[] = {
-        "/system/bin/make_f2fs",
-        "-g", "android",
-        fs_blkdev.c_str(),
-        size_str.c_str(),
-        nullptr
-    };
-    // clang-format on
 
-    return logwrap_fork_execvp(arraysize(args), args, nullptr, false, LOG_KLOG, true, nullptr);
+    std::vector<const char*> args = {"/system/bin/make_f2fs", "-g", "android"};
+    if (needs_projid) {
+        args.push_back("-O");
+        args.push_back("project_quota,extra_attr");
+    }
+    if (needs_casefold) {
+        args.push_back("-O");
+        args.push_back("casefold");
+        args.push_back("-C");
+        args.push_back("utf8");
+    }
+    args.push_back(fs_blkdev.c_str());
+    args.push_back(size_str.c_str());
+
+    return logwrap_fork_execvp(args.size(), args.data(), nullptr, false, LOG_KLOG, false, nullptr);
 }
 
 int fs_mgr_do_format(const FstabEntry& entry, bool crypt_footer) {
     LERROR << __FUNCTION__ << ": Format " << entry.blk_device << " as '" << entry.fs_type << "'";
 
+    bool needs_casefold = false;
+    bool needs_projid = false;
+
+    if (entry.mount_point == "/data") {
+        needs_casefold = android::base::GetBoolProperty("ro.emulated_storage.casefold", false);
+        needs_projid = android::base::GetBoolProperty("ro.emulated_storage.projid", false);
+    }
+
     if (entry.fs_type == "f2fs") {
-        return format_f2fs(entry.blk_device, entry.length, crypt_footer);
+        return format_f2fs(entry.blk_device, entry.length, crypt_footer, needs_projid,
+                           needs_casefold);
     } else if (entry.fs_type == "ext4") {
-        return format_ext4(entry.blk_device, entry.mount_point, crypt_footer);
+        return format_ext4(entry.blk_device, entry.mount_point, crypt_footer, needs_projid,
+                           entry.fs_mgr_flags.ext_meta_csum);
     } else {
         LERROR << "File system type '" << entry.fs_type << "' is not supported";
         return -EINVAL;
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 9697a4c..65f710a 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -176,6 +176,7 @@
         CheckFlag("first_stage_mount", first_stage_mount);
         CheckFlag("slotselect_other", slot_select_other);
         CheckFlag("fsverity", fs_verity);
+        CheckFlag("metadata_csum", ext_meta_csum);
 
 #undef CheckFlag
 
@@ -211,7 +212,7 @@
             }
         } else if (StartsWith(flag, "swapprio=")) {
             if (!ParseInt(arg, &entry->swap_prio)) {
-                LWARNING << "Warning: length= flag malformed: " << arg;
+                LWARNING << "Warning: swapprio= flag malformed: " << arg;
             }
         } else if (StartsWith(flag, "zramsize=")) {
             if (!arg.empty() && arg.back() == '%') {
@@ -276,7 +277,10 @@
             entry->vbmeta_partition = arg;
         } else if (StartsWith(flag, "keydirectory=")) {
             // The metadata flag is followed by an = and the directory for the keys.
-            entry->key_dir = arg;
+            entry->metadata_key_dir = arg;
+        } else if (StartsWith(flag, "metadata_encryption=")) {
+            // Specify the cipher and flags to use for metadata encryption
+            entry->metadata_encryption = arg;
         } else if (StartsWith(flag, "sysfs_path=")) {
             // The path to trigger device gc by idle-maint of vold.
             entry->sysfs_path = arg;
@@ -591,8 +595,8 @@
         userdata.blk_device = "userdata_gsi";
         userdata.fs_mgr_flags.logical = true;
         userdata.fs_mgr_flags.formattable = true;
-        if (!userdata.key_dir.empty()) {
-            userdata.key_dir += "/gsi";
+        if (!userdata.metadata_key_dir.empty()) {
+            userdata.metadata_key_dir += "/gsi";
         }
     } else {
         userdata = BuildDsuUserdataFstabEntry();
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 27971da..1fa1aa1 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -48,15 +48,21 @@
 #include <fs_mgr_overlayfs.h>
 #include <fstab/fstab.h>
 #include <libdm/dm.h>
+#include <libfiemap/image_manager.h>
 #include <libgsi/libgsi.h>
 #include <liblp/builder.h>
 #include <liblp/liblp.h>
+#include <storage_literals/storage_literals.h>
 
 #include "fs_mgr_priv.h"
+#include "libfiemap/utility.h"
 
 using namespace std::literals;
 using namespace android::dm;
 using namespace android::fs_mgr;
+using namespace android::storage_literals;
+using android::fiemap::FilesystemHasReliablePinning;
+using android::fiemap::IImageManager;
 
 namespace {
 
@@ -104,6 +110,14 @@
     return false;
 }
 
+namespace android {
+namespace fs_mgr {
+
+void MapScratchPartitionIfNeeded(Fstab*,
+                                 const std::function<bool(const std::set<std::string>&)>&) {}
+}  // namespace fs_mgr
+}  // namespace android
+
 #else  // ALLOW_ADBD_DISABLE_VERITY == 0
 
 namespace {
@@ -153,6 +167,12 @@
 }
 
 const auto kPhysicalDevice = "/dev/block/by-name/"s;
+constexpr char kScratchImageMetadata[] = "/metadata/gsi/remount/lp_metadata";
+
+// Note: this is meant only for recovery/first-stage init.
+bool ScratchIsOnData() {
+    return fs_mgr_access(kScratchImageMetadata);
+}
 
 bool fs_mgr_update_blk_device(FstabEntry* entry) {
     if (entry->fs_mgr_flags.logical) {
@@ -443,20 +463,37 @@
 bool fs_mgr_overlayfs_teardown_scratch(const std::string& overlay, bool* change) {
     // umount and delete kScratchMountPoint storage if we have logical partitions
     if (overlay != kScratchMountPoint) return true;
-    auto slot_number = fs_mgr_overlayfs_slot_number();
-    auto super_device = fs_mgr_overlayfs_super_device(slot_number);
-    if (!fs_mgr_rw_access(super_device)) return true;
 
     auto save_errno = errno;
     if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
         fs_mgr_overlayfs_umount_scratch();
     }
+
+    const auto partition_name = android::base::Basename(kScratchMountPoint);
+
+    auto images = IImageManager::Open("remount", 10s);
+    if (images && images->BackingImageExists(partition_name)) {
+#if defined __ANDROID_RECOVERY__
+        if (!images->DisableImage(partition_name)) {
+            return false;
+        }
+#else
+        if (!images->UnmapImageIfExists(partition_name) ||
+            !images->DeleteBackingImage(partition_name)) {
+            return false;
+        }
+#endif
+    }
+
+    auto slot_number = fs_mgr_overlayfs_slot_number();
+    auto super_device = fs_mgr_overlayfs_super_device(slot_number);
+    if (!fs_mgr_rw_access(super_device)) return true;
+
     auto builder = MetadataBuilder::New(super_device, slot_number);
     if (!builder) {
         errno = save_errno;
         return true;
     }
-    const auto partition_name = android::base::Basename(kScratchMountPoint);
     if (builder->FindPartition(partition_name) == nullptr) {
         errno = save_errno;
         return true;
@@ -811,66 +848,49 @@
     return "auto";
 }
 
-enum class ScratchStrategy {
-    kNone,
-    // DAP device, use logical partitions.
-    kDynamicPartition,
-    // Retrofit DAP device, use super_<other>.
-    kSuperOther,
-    // Pre-DAP device, uses the other slot.
-    kSystemOther
-};
-
-// Return the strategy this device must use for creating a scratch partition.
-static ScratchStrategy GetScratchStrategy(std::string* backing_device = nullptr) {
+// Note: we do not check access() here except for the super partition, since
+// in first-stage init we wouldn't have registed by-name symlinks for "other"
+// partitions that won't be mounted.
+static std::string GetPhysicalScratchDevice() {
     auto slot_number = fs_mgr_overlayfs_slot_number();
     auto super_device = fs_mgr_overlayfs_super_device(slot_number);
     auto path = fs_mgr_overlayfs_super_device(slot_number == 0);
     if (super_device != path) {
-        // Note: we do not check access() here, since in first-stage init we
-        // wouldn't have registed by-name symlinks for the device as it's
-        // normally not needed. The access checks elsewhere in this function
-        // are safe because system/super are always required.
-        if (backing_device) *backing_device = path;
-        return ScratchStrategy::kSuperOther;
+        return path;
     }
     if (fs_mgr_access(super_device)) {
-        if (backing_device) *backing_device = super_device;
-        return ScratchStrategy::kDynamicPartition;
+        // Do not try to use system_other on a DAP device.
+        return "";
     }
 
     auto other_slot = fs_mgr_get_other_slot_suffix();
     if (!other_slot.empty()) {
-        path = kPhysicalDevice + "system" + other_slot;
-        if (fs_mgr_access(path)) {
-            if (backing_device) *backing_device = path;
-            return ScratchStrategy::kSystemOther;
-        }
+        return kPhysicalDevice + "system" + other_slot;
     }
-    return ScratchStrategy::kNone;
+    return "";
 }
 
-// Return the scratch device if it exists.
-static std::string GetScratchDevice() {
-    std::string device;
-    ScratchStrategy strategy = GetScratchStrategy(&device);
+// This returns the scratch device that was detected during early boot (first-
+// stage init). If the device was created later, for example during setup for
+// the adb remount command, it can return an empty string since it does not
+// query ImageManager. (Note that ImageManager in first-stage init will always
+// use device-mapper, since /data is not available to use loop devices.)
+static std::string GetBootScratchDevice() {
+    auto& dm = DeviceMapper::Instance();
 
-    switch (strategy) {
-        case ScratchStrategy::kSuperOther:
-        case ScratchStrategy::kSystemOther:
-            return device;
-        case ScratchStrategy::kDynamicPartition: {
-            auto& dm = DeviceMapper::Instance();
-            auto partition_name = android::base::Basename(kScratchMountPoint);
-            if (dm.GetState(partition_name) != DmDeviceState::INVALID &&
-                dm.GetDmDevicePathByName(partition_name, &device)) {
-                return device;
-            }
-            return "";
-        }
-        default:
-            return "";
+    // If there is a scratch partition allocated in /data or on super, we
+    // automatically prioritize that over super_other or system_other.
+    // Some devices, for example, have a write-protected eMMC and the
+    // super partition cannot be used even if it exists.
+    std::string device;
+    auto partition_name = android::base::Basename(kScratchMountPoint);
+    if (dm.GetState(partition_name) != DmDeviceState::INVALID &&
+        dm.GetDmDevicePathByName(partition_name, &device)) {
+        return device;
     }
+
+    // There is no dynamic scratch, so try and find a physical one.
+    return GetPhysicalScratchDevice();
 }
 
 bool fs_mgr_overlayfs_make_scratch(const std::string& scratch_device, const std::string& mnt_type) {
@@ -915,8 +935,8 @@
 }
 
 // Create or update a scratch partition within super.
-static bool CreateDynamicScratch(const Fstab& fstab, std::string* scratch_device,
-                                 bool* partition_exists, bool* change) {
+static bool CreateDynamicScratch(std::string* scratch_device, bool* partition_exists,
+                                 bool* change) {
     const auto partition_name = android::base::Basename(kScratchMountPoint);
 
     auto& dm = DeviceMapper::Instance();
@@ -925,8 +945,6 @@
     auto partition_create = !*partition_exists;
     auto slot_number = fs_mgr_overlayfs_slot_number();
     auto super_device = fs_mgr_overlayfs_super_device(slot_number);
-    if (!fs_mgr_rw_access(super_device)) return false;
-    if (!fs_mgr_overlayfs_has_logical(fstab)) return false;
     auto builder = MetadataBuilder::New(super_device, slot_number);
     if (!builder) {
         LERROR << "open " << super_device << " metadata";
@@ -1008,27 +1026,96 @@
         }
 
         if (change) *change = true;
+    } else if (scratch_device->empty()) {
+        *scratch_device = GetBootScratchDevice();
     }
     return true;
 }
 
+static bool CreateScratchOnData(std::string* scratch_device, bool* partition_exists, bool* change) {
+    *partition_exists = false;
+    *change = false;
+
+    auto images = IImageManager::Open("remount", 10s);
+    if (!images) {
+        return false;
+    }
+
+    auto partition_name = android::base::Basename(kScratchMountPoint);
+    if (images->GetMappedImageDevice(partition_name, scratch_device)) {
+        *partition_exists = true;
+        return true;
+    }
+
+    BlockDeviceInfo info;
+    PartitionOpener opener;
+    if (!opener.GetInfo(fs_mgr_get_super_partition_name(), &info)) {
+        LERROR << "could not get block device info for super";
+        return false;
+    }
+
+    *change = true;
+
+    // Note: calling RemoveDisabledImages here ensures that we do not race with
+    // clean_scratch_files and accidentally try to map an image that will be
+    // deleted.
+    if (!images->RemoveDisabledImages()) {
+        return false;
+    }
+    if (!images->BackingImageExists(partition_name)) {
+        static constexpr uint64_t kMinimumSize = 16_MiB;
+        static constexpr uint64_t kMaximumSize = 2_GiB;
+
+        uint64_t size = std::clamp(info.size / 2, kMinimumSize, kMaximumSize);
+        auto flags = IImageManager::CREATE_IMAGE_DEFAULT;
+
+        if (!images->CreateBackingImage(partition_name, size, flags)) {
+            LERROR << "could not create scratch image of " << size << " bytes";
+            return false;
+        }
+    }
+    if (!images->MapImageDevice(partition_name, 10s, scratch_device)) {
+        LERROR << "could not map scratch image";
+        return false;
+    }
+    return true;
+}
+
+static bool CanUseSuperPartition(const Fstab& fstab, bool* is_virtual_ab) {
+    auto slot_number = fs_mgr_overlayfs_slot_number();
+    auto super_device = fs_mgr_overlayfs_super_device(slot_number);
+    if (!fs_mgr_rw_access(super_device) || !fs_mgr_overlayfs_has_logical(fstab)) {
+        return false;
+    }
+    auto metadata = ReadMetadata(super_device, slot_number);
+    if (!metadata) {
+        return false;
+    }
+    *is_virtual_ab = !!(metadata->header.flags & LP_HEADER_FLAG_VIRTUAL_AB_DEVICE);
+    return true;
+}
+
 bool fs_mgr_overlayfs_create_scratch(const Fstab& fstab, std::string* scratch_device,
                                      bool* partition_exists, bool* change) {
-    auto strategy = GetScratchStrategy();
-    if (strategy == ScratchStrategy::kDynamicPartition) {
-        return CreateDynamicScratch(fstab, scratch_device, partition_exists, change);
+    // Try a physical partition first.
+    *scratch_device = GetPhysicalScratchDevice();
+    if (!scratch_device->empty() && fs_mgr_rw_access(*scratch_device)) {
+        *partition_exists = true;
+        return true;
     }
 
-    // The scratch partition can only be landed on a physical partition if we
-    // get here. If there are no viable candidates that are R/W, just return
-    // that there is no device.
-    *scratch_device = GetScratchDevice();
-    if (scratch_device->empty()) {
-        errno = ENXIO;
-        return false;
+    // If that fails, see if we can land on super.
+    bool is_virtual_ab;
+    if (CanUseSuperPartition(fstab, &is_virtual_ab)) {
+        bool can_use_data = false;
+        if (is_virtual_ab && FilesystemHasReliablePinning("/data", &can_use_data) && can_use_data) {
+            return CreateScratchOnData(scratch_device, partition_exists, change);
+        }
+        return CreateDynamicScratch(scratch_device, partition_exists, change);
     }
-    *partition_exists = true;
-    return true;
+
+    errno = ENXIO;
+    return false;
 }
 
 // Create and mount kScratchMountPoint storage if we have logical partitions
@@ -1065,19 +1152,6 @@
     return fs_mgr_overlayfs_mount_scratch(scratch_device, mnt_type);
 }
 
-bool fs_mgr_overlayfs_scratch_can_be_mounted(const std::string& scratch_device) {
-    if (scratch_device.empty()) return false;
-    if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) return false;
-    if (android::base::StartsWith(scratch_device, kPhysicalDevice)) return true;
-    if (fs_mgr_rw_access(scratch_device)) return true;
-    auto slot_number = fs_mgr_overlayfs_slot_number();
-    auto super_device = fs_mgr_overlayfs_super_device(slot_number);
-    if (!fs_mgr_rw_access(super_device)) return false;
-    auto builder = MetadataBuilder::New(super_device, slot_number);
-    if (!builder) return false;
-    return builder->FindPartition(android::base::Basename(kScratchMountPoint)) != nullptr;
-}
-
 bool fs_mgr_overlayfs_invalid() {
     if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) return true;
 
@@ -1120,8 +1194,13 @@
 }
 
 static void TryMountScratch() {
-    auto scratch_device = GetScratchDevice();
-    if (!fs_mgr_overlayfs_scratch_can_be_mounted(scratch_device)) {
+    // Note we get the boot scratch device here, which means if scratch was
+    // just created through ImageManager, this could fail. In practice this
+    // should not happen because "remount" detects this scenario (by checking
+    // if verity is still disabled, i.e. no reboot occurred), and skips calling
+    // fs_mgr_overlayfs_mount_all().
+    auto scratch_device = GetBootScratchDevice();
+    if (!fs_mgr_rw_access(scratch_device)) {
         return;
     }
     if (!WaitForFile(scratch_device, 10s)) {
@@ -1159,23 +1238,6 @@
     return ret;
 }
 
-std::vector<std::string> fs_mgr_overlayfs_required_devices(Fstab* fstab) {
-    if (fs_mgr_overlayfs_invalid()) return {};
-
-    if (GetEntryForMountPoint(fstab, kScratchMountPoint) != nullptr) {
-        return {};
-    }
-
-    for (const auto& entry : fs_mgr_overlayfs_candidate_list(*fstab)) {
-        if (fs_mgr_is_verity_enabled(entry)) continue;
-        if (fs_mgr_overlayfs_already_mounted(fs_mgr_mount_point(entry.mount_point))) continue;
-        auto device = GetScratchDevice();
-        if (!fs_mgr_overlayfs_scratch_can_be_mounted(device)) break;
-        return {device};
-    }
-    return {};
-}
-
 // Returns false if setup not permitted, errno set to last error.
 // If something is altered, set *change.
 bool fs_mgr_overlayfs_setup(const char* backing, const char* mount_point, bool* change,
@@ -1241,25 +1303,58 @@
     return ret;
 }
 
-static bool GetAndMapScratchDeviceIfNeeded(std::string* device) {
-    *device = GetScratchDevice();
+static bool EnsureScratchMapped(std::string* device, bool* mapped) {
+    *mapped = false;
+    *device = GetBootScratchDevice();
     if (!device->empty()) {
         return true;
     }
 
-    auto strategy = GetScratchStrategy();
-    if (strategy == ScratchStrategy::kDynamicPartition) {
-        auto metadata_slot = fs_mgr_overlayfs_slot_number();
-        CreateLogicalPartitionParams params = {
-                .block_device = fs_mgr_overlayfs_super_device(metadata_slot),
-                .metadata_slot = metadata_slot,
-                .partition_name = android::base::Basename(kScratchMountPoint),
-                .force_writable = true,
-                .timeout_ms = 10s,
-        };
-        return CreateLogicalPartition(params, device);
+    auto partition_name = android::base::Basename(kScratchMountPoint);
+
+    // Check for scratch on /data first, before looking for a modified super
+    // partition. We should only reach this code in recovery, because scratch
+    // would otherwise always be mapped.
+    auto images = IImageManager::Open("remount", 10s);
+    if (images && images->BackingImageExists(partition_name)) {
+        if (!images->MapImageDevice(partition_name, 10s, device)) {
+            return false;
+        }
+        *mapped = true;
+        return true;
     }
-    return false;
+
+    // Avoid uart spam by first checking for a scratch partition.
+    auto metadata_slot = fs_mgr_overlayfs_slot_number();
+    auto super_device = fs_mgr_overlayfs_super_device(metadata_slot);
+    auto metadata = ReadCurrentMetadata(super_device);
+    if (!metadata) {
+        return false;
+    }
+
+    auto partition = FindPartition(*metadata.get(), partition_name);
+    if (!partition) {
+        return false;
+    }
+
+    CreateLogicalPartitionParams params = {
+            .block_device = super_device,
+            .metadata = metadata.get(),
+            .partition = partition,
+            .force_writable = true,
+            .timeout_ms = 10s,
+    };
+    if (!CreateLogicalPartition(params, device)) {
+        return false;
+    }
+    *mapped = true;
+    return true;
+}
+
+static void UnmapScratchDevice() {
+    // This should only be reachable in recovery, where scratch is not
+    // automatically mapped and therefore can be unmapped.
+    DestroyLogicalPartition(android::base::Basename(kScratchMountPoint));
 }
 
 // Returns false if teardown not permitted, errno set to last error.
@@ -1267,12 +1362,14 @@
 bool fs_mgr_overlayfs_teardown(const char* mount_point, bool* change) {
     if (change) *change = false;
     auto ret = true;
+
     // If scratch exists, but is not mounted, lets gain access to clean
     // specific override entries.
     auto mount_scratch = false;
+    bool unmap = false;
     if ((mount_point != nullptr) && !fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
         std::string scratch_device;
-        if (GetAndMapScratchDeviceIfNeeded(&scratch_device)) {
+        if (EnsureScratchMapped(&scratch_device, &unmap)) {
             mount_scratch = fs_mgr_overlayfs_mount_scratch(scratch_device,
                                                            fs_mgr_overlayfs_scratch_mount_type());
         }
@@ -1294,8 +1391,12 @@
         PERROR << "teardown";
         ret = false;
     }
-    if (mount_scratch) fs_mgr_overlayfs_umount_scratch();
-
+    if (mount_scratch) {
+        fs_mgr_overlayfs_umount_scratch();
+    }
+    if (unmap) {
+        UnmapScratchDevice();
+    }
     return ret;
 }
 
@@ -1313,6 +1414,59 @@
     return false;
 }
 
+namespace android {
+namespace fs_mgr {
+
+void MapScratchPartitionIfNeeded(Fstab* fstab,
+                                 const std::function<bool(const std::set<std::string>&)>& init) {
+    if (fs_mgr_overlayfs_invalid()) {
+        return;
+    }
+    if (GetEntryForMountPoint(fstab, kScratchMountPoint) != nullptr) {
+        return;
+    }
+
+    bool want_scratch = false;
+    for (const auto& entry : fs_mgr_overlayfs_candidate_list(*fstab)) {
+        if (fs_mgr_is_verity_enabled(entry)) {
+            continue;
+        }
+        if (fs_mgr_overlayfs_already_mounted(fs_mgr_mount_point(entry.mount_point))) {
+            continue;
+        }
+        want_scratch = true;
+        break;
+    }
+    if (!want_scratch) {
+        return;
+    }
+
+    if (ScratchIsOnData()) {
+        if (auto images = IImageManager::Open("remount", 0ms)) {
+            images->MapAllImages(init);
+        }
+    }
+
+    // Physical or logical partitions will have already been mapped here,
+    // so just ensure /dev/block symlinks exist.
+    auto device = GetBootScratchDevice();
+    if (!device.empty()) {
+        init({android::base::Basename(device)});
+    }
+}
+
+void CleanupOldScratchFiles() {
+    if (!ScratchIsOnData()) {
+        return;
+    }
+    if (auto images = IImageManager::Open("remount", 0ms)) {
+        images->RemoveDisabledImages();
+    }
+}
+
+}  // namespace fs_mgr
+}  // namespace android
+
 #endif  // ALLOW_ADBD_DISABLE_VERITY != 0
 
 bool fs_mgr_has_shared_blocks(const std::string& mount_point, const std::string& dev) {
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index 93bba68..24cbad7 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -80,9 +80,13 @@
     return &(*it);
 }
 
+auto verbose = false;
+
 void MyLogger(android::base::LogId id, android::base::LogSeverity severity, const char* tag,
               const char* file, unsigned int line, const char* message) {
-    fprintf(stderr, "%s\n", message);
+    if (verbose || severity == android::base::ERROR || message[0] != '[') {
+        fprintf(stderr, "%s\n", message);
+    }
     static auto logd = android::base::LogdLogger();
     logd(id, severity, tag, file, line, message);
 }
@@ -131,10 +135,14 @@
             {"fstab", required_argument, nullptr, 'T'},
             {"help", no_argument, nullptr, 'h'},
             {"reboot", no_argument, nullptr, 'R'},
+            {"verbose", no_argument, nullptr, 'v'},
             {0, 0, nullptr, 0},
     };
-    for (int opt; (opt = ::getopt_long(argc, argv, "hRT:", longopts, nullptr)) != -1;) {
+    for (int opt; (opt = ::getopt_long(argc, argv, "hRT:v", longopts, nullptr)) != -1;) {
         switch (opt) {
+            case 'h':
+                usage(SUCCESS);
+                break;
             case 'R':
                 can_reboot = true;
                 break;
@@ -145,13 +153,13 @@
                 }
                 fstab_file = optarg;
                 break;
+            case 'v':
+                verbose = true;
+                break;
             default:
                 LOG(ERROR) << "Bad Argument -" << char(opt);
                 usage(BADARG);
                 break;
-            case 'h':
-                usage(SUCCESS);
-                break;
         }
     }
 
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index 9a7381f..34aded9 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -16,8 +16,11 @@
 
 #pragma once
 
+#include <functional>
+
 #include <fstab/fstab.h>
 
+#include <set>
 #include <string>
 #include <vector>
 
@@ -38,3 +41,13 @@
     kOverrideCredsRequired,
 };
 OverlayfsValidResult fs_mgr_overlayfs_valid();
+
+namespace android {
+namespace fs_mgr {
+
+void MapScratchPartitionIfNeeded(Fstab* fstab,
+                                 const std::function<bool(const std::set<std::string>&)>& init);
+void CleanupOldScratchFiles();
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 80deaef..05e18fa 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -37,7 +37,8 @@
     unsigned long flags = 0;
     std::string fs_options;
     std::string key_loc;
-    std::string key_dir;
+    std::string metadata_key_dir;
+    std::string metadata_encryption;
     off64_t length = 0;
     std::string label;
     int partnum = -1;
@@ -82,6 +83,7 @@
         bool first_stage_mount : 1;
         bool slot_select_other : 1;
         bool fs_verity : 1;
+        bool ext_meta_csum : 1;
     } fs_mgr_flags = {};
 
     bool is_encryptable() const {
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index 8a924d5..1c3427f 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -54,29 +54,32 @@
 }
 
 cc_defaults {
-    name: "libdm_defaults",
+    name: "libdm_test_defaults",
     defaults: ["fs_mgr_defaults"],
     static_libs: [
         "libdm",
+    ],
+    shared_libs: [
         "libbase",
         "libext2_uuid",
         "libfs_mgr",
         "liblog",
     ],
     srcs: [":libdm_test_srcs"],
+    auto_gen_config: true,
+    require_root: true,
 }
 
 cc_test {
     name: "libdm_test",
-    defaults: ["libdm_defaults"],
+    defaults: ["libdm_test_defaults"],
+    test_suites: ["device-tests"],
 }
 
 cc_test {
     name: "vts_libdm_test",
-    defaults: ["libdm_defaults"],
+    defaults: ["libdm_test_defaults"],
     test_suites: ["vts-core"],
-    auto_gen_config: true,
-    require_root: true,
     test_min_api_level: 29,
 }
 
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
index 65f6e12..6461788 100644
--- a/fs_mgr/libdm/dm_target.cpp
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -243,9 +243,50 @@
     return android::base::Join(argv, " ");
 }
 
+const std::string DmTargetDefaultKey::name_ = "default-key";
+
+bool DmTargetDefaultKey::IsLegacy(bool* result) {
+    DeviceMapper& dm = DeviceMapper::Instance();
+    DmTargetTypeInfo info;
+    if (!dm.GetTargetByName(name_, &info)) return false;
+    // dm-default-key was modified to be like dm-crypt with version 2
+    *result = !info.IsAtLeast(2, 0, 0);
+    return true;
+}
+
+bool DmTargetDefaultKey::Valid() const {
+    bool real_is_legacy;
+    if (!DmTargetDefaultKey::IsLegacy(&real_is_legacy)) return false;
+    if (real_is_legacy != is_legacy_) return false;
+    if (!is_legacy_ && !set_dun_) return false;
+    return true;
+}
+
 std::string DmTargetDefaultKey::GetParameterString() const {
-    return cipher_ + " " + key_ + " " + blockdev_ + " " + std::to_string(start_sector_) +
-           (set_dun_ ? " 1 set_dun" : "");
+    std::vector<std::string> argv;
+    argv.emplace_back(cipher_);
+    argv.emplace_back(key_);
+    if (!is_legacy_) {
+        argv.emplace_back("0");  // iv_offset
+    }
+    argv.emplace_back(blockdev_);
+    argv.push_back(std::to_string(start_sector_));
+    std::vector<std::string> extra_argv;
+    if (is_legacy_) {
+        if (set_dun_) {  // v2 always sets the DUN.
+            extra_argv.emplace_back("set_dun");
+        }
+    } else {
+        extra_argv.emplace_back("allow_discards");
+        extra_argv.emplace_back("sector_size:4096");
+        extra_argv.emplace_back("iv_large_sectors");
+        if (is_hw_wrapped_) extra_argv.emplace_back("wrappedkey_v0");
+    }
+    if (!extra_argv.empty()) {
+        argv.emplace_back(std::to_string(extra_argv.size()));
+        argv.insert(argv.end(), extra_argv.begin(), extra_argv.end());
+    }
+    return android::base::Join(argv, " ");
 }
 
 }  // namespace dm
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index b7f31bc..67af59a 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -516,10 +516,32 @@
 }
 
 TEST(libdm, DefaultKeyArgs) {
+    DmTargetTypeInfo info;
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    if (!dm.GetTargetByName("default-key", &info)) {
+        cout << "default-key module not enabled; skipping test" << std::endl;
+        return;
+    }
+    bool is_legacy;
+    ASSERT_TRUE(DmTargetDefaultKey::IsLegacy(&is_legacy));
+    // set_dun only in the non-is_legacy case
     DmTargetDefaultKey target(0, 4096, "AES-256-XTS", "abcdef0123456789", "/dev/loop0", 0);
+    if (is_legacy) {
+        target.SetIsLegacy();
+    } else {
+        target.SetSetDun();
+    }
     ASSERT_EQ(target.name(), "default-key");
     ASSERT_TRUE(target.Valid());
-    ASSERT_EQ(target.GetParameterString(), "AES-256-XTS abcdef0123456789 /dev/loop0 0");
+    if (is_legacy) {
+        ASSERT_EQ(target.GetParameterString(), "AES-256-XTS abcdef0123456789 /dev/loop0 0");
+    } else {
+        // TODO: Add case for wrapped key enabled
+        ASSERT_EQ(target.GetParameterString(),
+                  "AES-256-XTS abcdef0123456789 0 /dev/loop0 0 3 allow_discards sector_size:4096 "
+                  "iv_large_sectors");
+    }
 }
 
 TEST(libdm, DeleteDeviceWithTimeout) {
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index a78bc71..d2e50d3 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -280,25 +280,30 @@
 class DmTargetDefaultKey final : public DmTarget {
   public:
     DmTargetDefaultKey(uint64_t start, uint64_t length, const std::string& cipher,
-                       const std::string& key, const std::string& blockdev, uint64_t start_sector,
-                       bool set_dun = false)
+                       const std::string& key, const std::string& blockdev, uint64_t start_sector)
         : DmTarget(start, length),
           cipher_(cipher),
           key_(key),
           blockdev_(blockdev),
-          start_sector_(start_sector),
-          set_dun_(set_dun) {}
+          start_sector_(start_sector) {}
 
-    std::string name() const override { return "default-key"; }
-    bool Valid() const override { return true; }
+    std::string name() const override { return name_; }
+    bool Valid() const override;
     std::string GetParameterString() const override;
+    static bool IsLegacy(bool* result);
+    void SetIsLegacy() { is_legacy_ = true; }
+    void SetSetDun() { set_dun_ = true; }
+    void SetWrappedKeyV0() { is_hw_wrapped_ = true; }
 
   private:
+    static const std::string name_;
     std::string cipher_;
     std::string key_;
     std::string blockdev_;
     uint64_t start_sector_;
-    bool set_dun_;
+    bool is_legacy_ = false;
+    bool set_dun_ = false;
+    bool is_hw_wrapped_ = false;
 };
 
 }  // namespace dm
diff --git a/fs_mgr/libfiemap/Android.bp b/fs_mgr/libfiemap/Android.bp
index fdc1583..2fd463c 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -24,6 +24,7 @@
     name: "libfiemap_srcs",
     srcs: [
         "fiemap_writer.cpp",
+        "fiemap_status.cpp",
         "image_manager.cpp",
         "metadata.cpp",
         "split_fiemap_writer.cpp",
@@ -78,6 +79,11 @@
     srcs: [
         "fiemap_writer_test.cpp",
     ],
+
+    test_suites: ["vts-core", "device-tests"],
+    auto_gen_config: true,
+    test_min_api_level: 29,
+    require_root: true,
 }
 
 cc_test {
@@ -98,8 +104,34 @@
     srcs: [
         "image_test.cpp",
     ],
+    test_suites: ["device-tests"],
+    auto_gen_config: true,
+    require_root: true,
 }
 
-vts_config {
-    name: "VtsFiemapWriterTest",
+/* BUG(148874852) temporary test */
+cc_test {
+    name: "fiemap_image_test_presubmit",
+    cppflags: [
+        "-DSKIP_TEST_IN_PRESUBMIT",
+    ],
+    static_libs: [
+        "libdm",
+        "libext4_utils",
+        "libfs_mgr",
+        "liblp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcrypto",
+        "libcrypto_utils",
+        "libcutils",
+        "liblog",
+    ],
+    srcs: [
+        "image_test.cpp",
+    ],
+    test_suites: ["device-tests"],
+    auto_gen_config: true,
+    require_root: true,
 }
diff --git a/fs_mgr/libfiemap/AndroidTest.xml b/fs_mgr/libfiemap/AndroidTest.xml
deleted file mode 100644
index 44c80fc..0000000
--- a/fs_mgr/libfiemap/AndroidTest.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<configuration description="Config for VTS VtsFiemapWriterTest">
-    <option name="config-descriptor:metadata" key="plan" value="vts-kernel" />
-    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
-        <option name="abort-on-push-failure" value="false"/>
-        <option name="push-group" value="HostDrivenTest.push"/>
-    </target_preparer>
-    <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
-      <option name="test-module-name" value="VtsFiemapWriterTest"/>
-        <option name="binary-test-source" value="_32bit::DATA/nativetest/fiemap_writer_test/fiemap_writer_test" />
-        <option name="binary-test-source" value="_64bit::DATA/nativetest64/fiemap_writer_test/fiemap_writer_test" />
-        <option name="binary-test-type" value="gtest"/>
-        <option name="precondition-first-api-level" value="29" />
-        <option name="test-timeout" value="1m"/>
-    </test>
-</configuration>
diff --git a/fs_mgr/libfiemap/binder.cpp b/fs_mgr/libfiemap/binder.cpp
index 49779f4..96c36ed 100644
--- a/fs_mgr/libfiemap/binder.cpp
+++ b/fs_mgr/libfiemap/binder.cpp
@@ -17,6 +17,7 @@
 #if !defined(__ANDROID_RECOVERY__)
 #include <android-base/logging.h>
 #include <android-base/properties.h>
+#include <android/gsi/BnProgressCallback.h>
 #include <android/gsi/IGsiService.h>
 #include <android/gsi/IGsid.h>
 #include <binder/IServiceManager.h>
@@ -29,10 +30,29 @@
 using namespace android::gsi;
 using namespace std::chrono_literals;
 
+class ProgressCallback final : public BnProgressCallback {
+  public:
+    ProgressCallback(std::function<bool(uint64_t, uint64_t)>&& callback)
+        : callback_(std::move(callback)) {
+        CHECK(callback_);
+    }
+    android::binder::Status onProgress(int64_t current, int64_t total) {
+        if (callback_(static_cast<uint64_t>(current), static_cast<uint64_t>(total))) {
+            return android::binder::Status::ok();
+        }
+        return android::binder::Status::fromServiceSpecificError(UNKNOWN_ERROR,
+                                                                 "Progress callback failed");
+    }
+
+  private:
+    std::function<bool(uint64_t, uint64_t)> callback_;
+};
+
 class ImageManagerBinder final : public IImageManager {
   public:
     ImageManagerBinder(android::sp<IGsiService>&& service, android::sp<IImageService>&& manager);
-    bool CreateBackingImage(const std::string& name, uint64_t size, int flags) override;
+    FiemapStatus CreateBackingImage(const std::string& name, uint64_t size, int flags,
+                                    std::function<bool(uint64_t, uint64_t)>&& on_progress) override;
     bool DeleteBackingImage(const std::string& name) override;
     bool MapImageDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms,
                         std::string* path) override;
@@ -41,8 +61,12 @@
     bool IsImageMapped(const std::string& name) override;
     bool MapImageWithDeviceMapper(const IPartitionOpener& opener, const std::string& name,
                                   std::string* dev) override;
-    bool ZeroFillNewImage(const std::string& name, uint64_t bytes) override;
+    FiemapStatus ZeroFillNewImage(const std::string& name, uint64_t bytes) override;
     bool RemoveAllImages() override;
+    bool DisableImage(const std::string& name) override;
+    bool RemoveDisabledImages() override;
+    bool GetMappedImageDevice(const std::string& name, std::string* device) override;
+    bool MapAllImages(const std::function<bool(std::set<std::string>)>& init) override;
 
     std::vector<std::string> GetAllBackingImages() override;
 
@@ -51,18 +75,31 @@
     android::sp<IImageService> manager_;
 };
 
+static FiemapStatus ToFiemapStatus(const char* func, const binder::Status& status) {
+    if (!status.isOk()) {
+        LOG(ERROR) << func << " binder returned: " << status.toString8().string();
+        if (status.serviceSpecificErrorCode() != 0) {
+            return FiemapStatus::FromErrorCode(status.serviceSpecificErrorCode());
+        } else {
+            return FiemapStatus::Error();
+        }
+    }
+    return FiemapStatus::Ok();
+}
+
 ImageManagerBinder::ImageManagerBinder(android::sp<IGsiService>&& service,
                                        android::sp<IImageService>&& manager)
     : service_(std::move(service)), manager_(std::move(manager)) {}
 
-bool ImageManagerBinder::CreateBackingImage(const std::string& name, uint64_t size, int flags) {
-    auto status = manager_->createBackingImage(name, size, flags);
-    if (!status.isOk()) {
-        LOG(ERROR) << __PRETTY_FUNCTION__
-                   << " binder returned: " << status.exceptionMessage().string();
-        return false;
+FiemapStatus ImageManagerBinder::CreateBackingImage(
+        const std::string& name, uint64_t size, int flags,
+        std::function<bool(uint64_t, uint64_t)>&& on_progress) {
+    sp<IProgressCallback> callback = nullptr;
+    if (on_progress) {
+        callback = new ProgressCallback(std::move(on_progress));
     }
-    return true;
+    auto status = manager_->createBackingImage(name, size, flags, callback);
+    return ToFiemapStatus(__PRETTY_FUNCTION__, status);
 }
 
 bool ImageManagerBinder::DeleteBackingImage(const std::string& name) {
@@ -143,14 +180,9 @@
     return retval;
 }
 
-bool ImageManagerBinder::ZeroFillNewImage(const std::string& name, uint64_t bytes) {
+FiemapStatus ImageManagerBinder::ZeroFillNewImage(const std::string& name, uint64_t bytes) {
     auto status = manager_->zeroFillNewImage(name, bytes);
-    if (!status.isOk()) {
-        LOG(ERROR) << __PRETTY_FUNCTION__
-                   << " binder returned: " << status.exceptionMessage().string();
-        return false;
-    }
-    return true;
+    return ToFiemapStatus(__PRETTY_FUNCTION__, status);
 }
 
 bool ImageManagerBinder::RemoveAllImages() {
@@ -163,6 +195,36 @@
     return true;
 }
 
+bool ImageManagerBinder::DisableImage(const std::string&) {
+    LOG(ERROR) << __PRETTY_FUNCTION__ << " is not available over binder";
+    return false;
+}
+
+bool ImageManagerBinder::RemoveDisabledImages() {
+    auto status = manager_->removeDisabledImages();
+    if (!status.isOk()) {
+        LOG(ERROR) << __PRETTY_FUNCTION__
+                   << " binder returned: " << status.exceptionMessage().string();
+        return false;
+    }
+    return true;
+}
+
+bool ImageManagerBinder::GetMappedImageDevice(const std::string& name, std::string* device) {
+    auto status = manager_->getMappedImageDevice(name, device);
+    if (!status.isOk()) {
+        LOG(ERROR) << __PRETTY_FUNCTION__
+                   << " binder returned: " << status.exceptionMessage().string();
+        return false;
+    }
+    return !device->empty();
+}
+
+bool ImageManagerBinder::MapAllImages(const std::function<bool(std::set<std::string>)>&) {
+    LOG(ERROR) << __PRETTY_FUNCTION__ << " not available over binder";
+    return false;
+}
+
 static android::sp<IGsid> AcquireIGsid(const std::chrono::milliseconds& timeout_ms) {
     if (android::base::GetProperty("init.svc.gsid", "") != "running") {
         if (!android::base::SetProperty("ctl.start", "gsid") ||
diff --git a/fs_mgr/libfiemap/fiemap_status.cpp b/fs_mgr/libfiemap/fiemap_status.cpp
new file mode 100644
index 0000000..92ac935
--- /dev/null
+++ b/fs_mgr/libfiemap/fiemap_status.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <libfiemap/fiemap_status.h>
+
+namespace android::fiemap {
+
+// FiemapStatus -> string
+std::string FiemapStatus::string() const {
+    if (error_code() == ErrorCode::ERROR) {
+        return "Error";
+    }
+    return strerror(-static_cast<int>(error_code()));
+}
+
+// -errno -> known ErrorCode
+// unknown ErrorCode -> known ErrorCode
+FiemapStatus::ErrorCode FiemapStatus::CastErrorCode(int error_code) {
+    switch (error_code) {
+        case static_cast<int32_t>(ErrorCode::SUCCESS):
+        case static_cast<int32_t>(ErrorCode::NO_SPACE):
+            return static_cast<ErrorCode>(error_code);
+        case static_cast<int32_t>(ErrorCode::ERROR):
+        default:
+            return ErrorCode::ERROR;
+    }
+}
+
+}  // namespace android::fiemap
diff --git a/fs_mgr/libfiemap/fiemap_writer.cpp b/fs_mgr/libfiemap/fiemap_writer.cpp
index b5794d3..4dd4bcc 100644
--- a/fs_mgr/libfiemap/fiemap_writer.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer.cpp
@@ -233,8 +233,7 @@
     return sb.st_size;
 }
 
-static bool PerformFileChecks(const std::string& file_path, uint64_t file_size, uint64_t* blocksz,
-                              uint32_t* fs_type) {
+static bool PerformFileChecks(const std::string& file_path, uint64_t* blocksz, uint32_t* fs_type) {
     struct statfs64 sfs;
     if (statfs64(file_path.c_str(), &sfs)) {
         PLOG(ERROR) << "Failed to read file system status at: " << file_path;
@@ -258,20 +257,14 @@
             return false;
     }
 
-    uint64_t available_bytes = sfs.f_bsize * sfs.f_bavail;
-    if (access(file_path.c_str(), F_OK) != 0 && available_bytes <= file_size) {
-        LOG(ERROR) << "Not enough free space in file system to create file of size : " << file_size;
-        return false;
-    }
-
     *blocksz = sfs.f_bsize;
     *fs_type = sfs.f_type;
     return true;
 }
 
-static bool FallocateFallback(int file_fd, uint64_t block_size, uint64_t file_size,
-                              const std::string& file_path,
-                              const std::function<bool(uint64_t, uint64_t)>& on_progress) {
+static FiemapStatus FallocateFallback(int file_fd, uint64_t block_size, uint64_t file_size,
+                                      const std::string& file_path,
+                                      const std::function<bool(uint64_t, uint64_t)>& on_progress) {
     // Even though this is much faster than writing zeroes, it is still slow
     // enough that we need to fire the progress callback periodically. To
     // easily achieve this, we seek in chunks. We use 1000 chunks since
@@ -287,22 +280,22 @@
         auto rv = TEMP_FAILURE_RETRY(lseek(file_fd, cursor - 1, SEEK_SET));
         if (rv < 0) {
             PLOG(ERROR) << "Failed to lseek " << file_path;
-            return false;
+            return FiemapStatus::FromErrno(errno);
         }
         if (rv != cursor - 1) {
             LOG(ERROR) << "Seek returned wrong offset " << rv << " for file " << file_path;
-            return false;
+            return FiemapStatus::Error();
         }
         char buffer[] = {0};
         if (!android::base::WriteFully(file_fd, buffer, 1)) {
             PLOG(ERROR) << "Write failed: " << file_path;
-            return false;
+            return FiemapStatus::FromErrno(errno);
         }
         if (on_progress && !on_progress(cursor, file_size)) {
-            return false;
+            return FiemapStatus::Error();
         }
     }
-    return true;
+    return FiemapStatus::Ok();
 }
 
 // F2FS-specific ioctl
@@ -389,19 +382,19 @@
 // write zeroes in 'blocksz' byte increments until we reach file_size to make sure the data
 // blocks are actually written to by the file system and thus getting rid of the holes in the
 // file.
-static bool WriteZeroes(int file_fd, const std::string& file_path, size_t blocksz,
-                        uint64_t file_size,
-                        const std::function<bool(uint64_t, uint64_t)>& on_progress) {
+static FiemapStatus WriteZeroes(int file_fd, const std::string& file_path, size_t blocksz,
+                                uint64_t file_size,
+                                const std::function<bool(uint64_t, uint64_t)>& on_progress) {
     auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, blocksz), free);
     if (buffer == nullptr) {
         LOG(ERROR) << "failed to allocate memory for writing file";
-        return false;
+        return FiemapStatus::Error();
     }
 
     off64_t offset = lseek64(file_fd, 0, SEEK_SET);
     if (offset < 0) {
         PLOG(ERROR) << "Failed to seek at the beginning of : " << file_path;
-        return false;
+        return FiemapStatus::FromErrno(errno);
     }
 
     int permille = -1;
@@ -409,7 +402,7 @@
         if (!::android::base::WriteFully(file_fd, buffer.get(), blocksz)) {
             PLOG(ERROR) << "Failed to write" << blocksz << " bytes at offset" << offset
                         << " in file " << file_path;
-            return false;
+            return FiemapStatus::FromErrno(errno);
         }
 
         offset += blocksz;
@@ -419,7 +412,7 @@
         int new_permille = (static_cast<uint64_t>(offset) * 1000) / file_size;
         if (new_permille != permille && static_cast<uint64_t>(offset) != file_size) {
             if (on_progress && !on_progress(offset, file_size)) {
-                return false;
+                return FiemapStatus::Error();
             }
             permille = new_permille;
         }
@@ -427,18 +420,18 @@
 
     if (lseek64(file_fd, 0, SEEK_SET) < 0) {
         PLOG(ERROR) << "Failed to reset offset at the beginning of : " << file_path;
-        return false;
+        return FiemapStatus::FromErrno(errno);
     }
-    return true;
+    return FiemapStatus::Ok();
 }
 
 // Reserve space for the file on the file system and write it out to make sure the extents
 // don't come back unwritten. Return from this function with the kernel file offset set to 0.
 // If the filesystem is f2fs, then we also PIN the file on disk to make sure the blocks
 // aren't moved around.
-static bool AllocateFile(int file_fd, const std::string& file_path, uint64_t blocksz,
-                         uint64_t file_size, unsigned int fs_type,
-                         std::function<bool(uint64_t, uint64_t)> on_progress) {
+static FiemapStatus AllocateFile(int file_fd, const std::string& file_path, uint64_t blocksz,
+                                 uint64_t file_size, unsigned int fs_type,
+                                 std::function<bool(uint64_t, uint64_t)> on_progress) {
     bool need_explicit_writes = true;
     switch (fs_type) {
         case EXT4_SUPER_MAGIC:
@@ -446,11 +439,11 @@
         case F2FS_SUPER_MAGIC: {
             bool supported;
             if (!F2fsPinBeforeAllocate(file_fd, &supported)) {
-                return false;
+                return FiemapStatus::Error();
             }
             if (supported) {
                 if (!PinFile(file_fd, file_path, fs_type)) {
-                    return false;
+                    return FiemapStatus::Error();
                 }
                 need_explicit_writes = false;
             }
@@ -462,29 +455,32 @@
             return FallocateFallback(file_fd, blocksz, file_size, file_path, on_progress);
         default:
             LOG(ERROR) << "Missing fallocate() support for file system " << fs_type;
-            return false;
+            return FiemapStatus::Error();
     }
 
     if (fallocate(file_fd, 0, 0, file_size)) {
         PLOG(ERROR) << "Failed to allocate space for file: " << file_path << " size: " << file_size;
-        return false;
+        return FiemapStatus::FromErrno(errno);
     }
 
-    if (need_explicit_writes && !WriteZeroes(file_fd, file_path, blocksz, file_size, on_progress)) {
-        return false;
+    if (need_explicit_writes) {
+        auto status = WriteZeroes(file_fd, file_path, blocksz, file_size, on_progress);
+        if (!status.is_ok()) {
+            return status;
+        }
     }
 
     // flush all writes here ..
     if (fsync(file_fd)) {
         PLOG(ERROR) << "Failed to synchronize written file:" << file_path;
-        return false;
+        return FiemapStatus::FromErrno(errno);
     }
 
     // Send one last progress notification.
     if (on_progress && !on_progress(file_size, file_size)) {
-        return false;
+        return FiemapStatus::Error();
     }
-    return true;
+    return FiemapStatus::Ok();
 }
 
 bool FiemapWriter::HasPinnedExtents(const std::string& file_path) {
@@ -530,11 +526,7 @@
 }
 
 static bool IsLastExtent(const fiemap_extent* extent) {
-    if (!(extent->fe_flags & FIEMAP_EXTENT_LAST)) {
-        LOG(ERROR) << "Extents are being received out-of-order";
-        return false;
-    }
-    return true;
+    return !!(extent->fe_flags & FIEMAP_EXTENT_LAST);
 }
 
 static bool FiemapToExtents(struct fiemap* fiemap, std::vector<struct fiemap_extent>* extents,
@@ -556,7 +548,10 @@
         fiemap_extent* next = &fiemap->fm_extents[i];
 
         // Make sure extents are returned in order
-        if (next != last_extent && IsLastExtent(next)) return false;
+        if (next != last_extent && IsLastExtent(next)) {
+            LOG(ERROR) << "Extents are being received out-of-order";
+            return false;
+        }
 
         // Check if extent's flags are valid
         if (!IsValidExtent(next, file_path)) return false;
@@ -596,8 +591,7 @@
         return false;
     }
 
-    uint64_t fiemap_size =
-            sizeof(struct fiemap_extent) + num_extents * sizeof(struct fiemap_extent);
+    uint64_t fiemap_size = sizeof(struct fiemap) + num_extents * sizeof(struct fiemap_extent);
     auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, fiemap_size), free);
     if (buffer == nullptr) {
         LOG(ERROR) << "Failed to allocate memory for fiemap";
@@ -678,6 +672,18 @@
 
 FiemapUniquePtr FiemapWriter::Open(const std::string& file_path, uint64_t file_size, bool create,
                                    std::function<bool(uint64_t, uint64_t)> progress) {
+    FiemapUniquePtr ret;
+    if (!Open(file_path, file_size, &ret, create, progress).is_ok()) {
+        return nullptr;
+    }
+    return ret;
+}
+
+FiemapStatus FiemapWriter::Open(const std::string& file_path, uint64_t file_size,
+                                FiemapUniquePtr* out, bool create,
+                                std::function<bool(uint64_t, uint64_t)> progress) {
+    out->reset();
+
     // if 'create' is false, open an existing file and do not truncate.
     int open_flags = O_RDWR | O_CLOEXEC;
     if (create) {
@@ -690,52 +696,55 @@
             TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags, S_IRUSR | S_IWUSR)));
     if (file_fd < 0) {
         PLOG(ERROR) << "Failed to create file at: " << file_path;
-        return nullptr;
+        return FiemapStatus::FromErrno(errno);
     }
 
     std::string abs_path;
     if (!::android::base::Realpath(file_path, &abs_path)) {
+        int saved_errno = errno;
         PLOG(ERROR) << "Invalid file path: " << file_path;
         cleanup(file_path, create);
-        return nullptr;
+        return FiemapStatus::FromErrno(saved_errno);
     }
 
     std::string bdev_path;
     if (!GetBlockDeviceForFile(abs_path, &bdev_path)) {
         LOG(ERROR) << "Failed to get block dev path for file: " << file_path;
         cleanup(abs_path, create);
-        return nullptr;
+        return FiemapStatus::Error();
     }
 
     ::android::base::unique_fd bdev_fd(
             TEMP_FAILURE_RETRY(open(bdev_path.c_str(), O_RDONLY | O_CLOEXEC)));
     if (bdev_fd < 0) {
+        int saved_errno = errno;
         PLOG(ERROR) << "Failed to open block device: " << bdev_path;
         cleanup(file_path, create);
-        return nullptr;
+        return FiemapStatus::FromErrno(saved_errno);
     }
 
     uint64_t bdevsz;
     if (!GetBlockDeviceSize(bdev_fd, bdev_path, &bdevsz)) {
+        int saved_errno = errno;
         LOG(ERROR) << "Failed to get block device size for : " << bdev_path;
         cleanup(file_path, create);
-        return nullptr;
+        return FiemapStatus::FromErrno(saved_errno);
     }
 
     if (!create) {
         file_size = GetFileSize(abs_path);
         if (file_size == 0) {
             LOG(ERROR) << "Invalid file size of zero bytes for file: " << abs_path;
-            return nullptr;
+            return FiemapStatus::FromErrno(errno);
         }
     }
 
     uint64_t blocksz;
     uint32_t fs_type;
-    if (!PerformFileChecks(abs_path, file_size, &blocksz, &fs_type)) {
+    if (!PerformFileChecks(abs_path, &blocksz, &fs_type)) {
         LOG(ERROR) << "Failed to validate file or file system for file:" << abs_path;
         cleanup(abs_path, create);
-        return nullptr;
+        return FiemapStatus::Error();
     }
 
     // Align up to the nearest block size.
@@ -744,11 +753,13 @@
     }
 
     if (create) {
-        if (!AllocateFile(file_fd, abs_path, blocksz, file_size, fs_type, std::move(progress))) {
+        auto status =
+                AllocateFile(file_fd, abs_path, blocksz, file_size, fs_type, std::move(progress));
+        if (!status.is_ok()) {
             LOG(ERROR) << "Failed to allocate file: " << abs_path << " of size: " << file_size
                        << " bytes";
             cleanup(abs_path, create);
-            return nullptr;
+            return status;
         }
     }
 
@@ -756,7 +767,7 @@
     if (!PinFile(file_fd, abs_path, fs_type)) {
         cleanup(abs_path, create);
         LOG(ERROR) << "Failed to pin the file in storage";
-        return nullptr;
+        return FiemapStatus::Error();
     }
 
     // now allocate the FiemapWriter and start setting it up
@@ -767,14 +778,14 @@
             if (!ReadFiemap(file_fd, abs_path, &fmap->extents_)) {
                 LOG(ERROR) << "Failed to read fiemap of file: " << abs_path;
                 cleanup(abs_path, create);
-                return nullptr;
+                return FiemapStatus::Error();
             }
             break;
         case MSDOS_SUPER_MAGIC:
             if (!ReadFibmap(file_fd, abs_path, &fmap->extents_)) {
                 LOG(ERROR) << "Failed to read fibmap of file: " << abs_path;
                 cleanup(abs_path, create);
-                return nullptr;
+                return FiemapStatus::Error();
             }
             break;
     }
@@ -788,7 +799,8 @@
 
     LOG(VERBOSE) << "Successfully created FiemapWriter for file " << abs_path << " on block device "
                  << bdev_path;
-    return fmap;
+    *out = std::move(fmap);
+    return FiemapStatus::Ok();
 }
 
 }  // namespace fiemap
diff --git a/fs_mgr/libfiemap/fiemap_writer_test.cpp b/fs_mgr/libfiemap/fiemap_writer_test.cpp
index 4ac7161..22a3722 100644
--- a/fs_mgr/libfiemap/fiemap_writer_test.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer_test.cpp
@@ -193,7 +193,9 @@
 }
 
 TEST_F(FiemapWriterTest, MaxBlockSize) {
-    ASSERT_GT(DetermineMaximumFileSize(testfile), 0);
+    uint64_t max_piece_size = 0;
+    ASSERT_TRUE(DetermineMaximumFileSize(testfile, &max_piece_size));
+    ASSERT_GT(max_piece_size, 0);
 }
 
 TEST_F(FiemapWriterTest, FibmapBlockAddressing) {
diff --git a/fs_mgr/libfiemap/image_manager.cpp b/fs_mgr/libfiemap/image_manager.cpp
index fe2018d..6717922 100644
--- a/fs_mgr/libfiemap/image_manager.cpp
+++ b/fs_mgr/libfiemap/image_manager.cpp
@@ -26,6 +26,7 @@
 #include <fs_mgr_dm_linear.h>
 #include <libdm/loop_control.h>
 #include <libfiemap/split_fiemap_writer.h>
+#include <libgsi/libgsi.h>
 
 #include "metadata.h"
 #include "utility.h"
@@ -34,6 +35,7 @@
 namespace fiemap {
 
 using namespace std::literals;
+using android::base::ReadFileToString;
 using android::base::unique_fd;
 using android::dm::DeviceMapper;
 using android::dm::DmDeviceState;
@@ -42,7 +44,10 @@
 using android::dm::LoopControl;
 using android::fs_mgr::CreateLogicalPartition;
 using android::fs_mgr::CreateLogicalPartitionParams;
+using android::fs_mgr::CreateLogicalPartitions;
 using android::fs_mgr::DestroyLogicalPartition;
+using android::fs_mgr::GetBlockDevicePartitionName;
+using android::fs_mgr::GetBlockDevicePartitionNames;
 using android::fs_mgr::GetPartitionName;
 
 static constexpr char kTestImageMetadataDir[] = "/metadata/gsi/test";
@@ -50,6 +55,11 @@
 std::unique_ptr<ImageManager> ImageManager::Open(const std::string& dir_prefix) {
     auto metadata_dir = "/metadata/gsi/" + dir_prefix;
     auto data_dir = "/data/gsi/" + dir_prefix;
+    auto install_dir_file = gsi::DsuInstallDirFile(gsi::GetDsuSlot(dir_prefix));
+    std::string path;
+    if (ReadFileToString(install_dir_file, &path)) {
+        data_dir = path;
+    }
     return Open(metadata_dir, data_dir);
 }
 
@@ -114,7 +124,7 @@
     return images;
 }
 
-bool ImageManager::PartitionExists(const std::string& name) {
+bool ImageManager::BackingImageExists(const std::string& name) {
     if (!MetadataExists(metadata_dir_)) {
         return false;
     }
@@ -125,32 +135,25 @@
     return !!FindPartition(*metadata.get(), name);
 }
 
-bool ImageManager::BackingImageExists(const std::string& name) {
-    auto header_file = GetImageHeaderPath(name);
-    return access(header_file.c_str(), F_OK) == 0;
-}
-
-bool ImageManager::CreateBackingImage(const std::string& name, uint64_t size, int flags) {
-    return CreateBackingImage(name, size, flags, nullptr);
-}
-
 static bool IsUnreliablePinningAllowed(const std::string& path) {
     return android::base::StartsWith(path, "/data/gsi/dsu/") ||
            android::base::StartsWith(path, "/data/gsi/test/") ||
            android::base::StartsWith(path, "/data/gsi/ota/test/");
 }
 
-bool ImageManager::CreateBackingImage(const std::string& name, uint64_t size, int flags,
-                                      std::function<bool(uint64_t, uint64_t)>&& on_progress) {
+FiemapStatus ImageManager::CreateBackingImage(
+        const std::string& name, uint64_t size, int flags,
+        std::function<bool(uint64_t, uint64_t)>&& on_progress) {
     auto data_path = GetImageHeaderPath(name);
-    auto fw = SplitFiemap::Create(data_path, size, 0, on_progress);
-    if (!fw) {
-        return false;
+    std::unique_ptr<SplitFiemap> fw;
+    auto status = SplitFiemap::Create(data_path, size, 0, &fw, on_progress);
+    if (!status.is_ok()) {
+        return status;
     }
 
     bool reliable_pinning;
     if (!FilesystemHasReliablePinning(data_path, &reliable_pinning)) {
-        return false;
+        return FiemapStatus::Error();
     }
     if (!reliable_pinning && !IsUnreliablePinningAllowed(data_path)) {
         // For historical reasons, we allow unreliable pinning for certain use
@@ -161,7 +164,7 @@
         // proper pinning.
         LOG(ERROR) << "File system does not have reliable block pinning";
         SplitFiemap::RemoveSplitFiles(data_path);
-        return false;
+        return FiemapStatus::Error();
     }
 
     // Except for testing, we do not allow persisting metadata that references
@@ -177,24 +180,25 @@
 
         fw = {};
         SplitFiemap::RemoveSplitFiles(data_path);
-        return false;
+        return FiemapStatus::Error();
     }
 
     bool readonly = !!(flags & CREATE_IMAGE_READONLY);
     if (!UpdateMetadata(metadata_dir_, name, fw.get(), size, readonly)) {
-        return false;
+        return FiemapStatus::Error();
     }
 
     if (flags & CREATE_IMAGE_ZERO_FILL) {
-        if (!ZeroFillNewImage(name, 0)) {
+        auto res = ZeroFillNewImage(name, 0);
+        if (!res.is_ok()) {
             DeleteBackingImage(name);
-            return false;
+            return res;
         }
     }
-    return true;
+    return FiemapStatus::Ok();
 }
 
-bool ImageManager::ZeroFillNewImage(const std::string& name, uint64_t bytes) {
+FiemapStatus ImageManager::ZeroFillNewImage(const std::string& name, uint64_t bytes) {
     auto data_path = GetImageHeaderPath(name);
 
     // See the comment in MapImageDevice() about how this works.
@@ -202,13 +206,13 @@
     bool can_use_devicemapper;
     if (!FiemapWriter::GetBlockDeviceForFile(data_path, &block_device, &can_use_devicemapper)) {
         LOG(ERROR) << "Could not determine block device for " << data_path;
-        return false;
+        return FiemapStatus::Error();
     }
 
     if (!can_use_devicemapper) {
         // We've backed with loop devices, and since we store files in an
         // unencrypted folder, the initial zeroes we wrote will suffice.
-        return true;
+        return FiemapStatus::Ok();
     }
 
     // data is dm-crypt, or FBE + dm-default-key. This means the zeroes written
@@ -216,7 +220,7 @@
     // this.
     auto device = MappedDevice::Open(this, 10s, name);
     if (!device) {
-        return false;
+        return FiemapStatus::Error();
     }
 
     static constexpr size_t kChunkSize = 4096;
@@ -229,7 +233,7 @@
         remaining = get_block_device_size(device->fd());
         if (!remaining) {
             PLOG(ERROR) << "Could not get block device size for " << device->path();
-            return false;
+            return FiemapStatus::FromErrno(errno);
         }
     }
     while (remaining) {
@@ -237,21 +241,25 @@
         if (!android::base::WriteFully(device->fd(), zeroes.data(),
                                        static_cast<size_t>(to_write))) {
             PLOG(ERROR) << "write failed: " << device->path();
-            return false;
+            return FiemapStatus::FromErrno(errno);
         }
         remaining -= to_write;
     }
-    return true;
+    return FiemapStatus::Ok();
 }
 
 bool ImageManager::DeleteBackingImage(const std::string& name) {
     // For dm-linear devices sitting on top of /data, we cannot risk deleting
     // the file. The underlying blocks could be reallocated by the filesystem.
     if (IsImageMapped(name)) {
-        LOG(ERROR) << "Backing image " << name << " is currently mapped to a block device";
+        LOG(ERROR) << "Cannot delete backing image " << name << " because mapped to a block device";
         return false;
     }
 
+#if defined __ANDROID_RECOVERY__
+    LOG(ERROR) << "Cannot remove images backed by /data in recovery";
+    return false;
+#else
     std::string message;
     auto header_file = GetImageHeaderPath(name);
     if (!SplitFiemap::RemoveSplitFiles(header_file, &message)) {
@@ -265,6 +273,7 @@
         LOG(ERROR) << "Error removing " << status_file << ": " << message;
     }
     return RemoveImageMetadata(metadata_dir_, name);
+#endif
 }
 
 // Create a block device for an image file, using its extents in its
@@ -498,6 +507,7 @@
 
     auto image_header = GetImageHeaderPath(name);
 
+#if !defined __ANDROID_RECOVERY__
     // If there is a device-mapper node wrapping the block device, then we're
     // able to create another node around it; the dm layer does not carry the
     // exclusion lock down the stack when a mount occurs.
@@ -521,6 +531,13 @@
     } else if (!MapWithLoopDevice(name, timeout_ms, path)) {
         return false;
     }
+#else
+    // In recovery, we can *only* use device-mapper, since partitions aren't
+    // mounted. That also means we cannot call GetBlockDeviceForFile.
+    if (!MapWithDmLinear(*partition_opener_.get(), name, timeout_ms, path)) {
+        return false;
+    }
+#endif
 
     // Set a property so we remember this is mapped.
     auto prop_name = GetStatusPropertyName(name);
@@ -632,6 +649,66 @@
     return true;
 }
 
+bool ImageManager::DisableImage(const std::string& name) {
+    return AddAttributes(metadata_dir_, name, LP_PARTITION_ATTR_DISABLED);
+}
+
+bool ImageManager::RemoveDisabledImages() {
+    if (!MetadataExists(metadata_dir_)) {
+        return true;
+    }
+
+    auto metadata = OpenMetadata(metadata_dir_);
+    if (!metadata) {
+        return false;
+    }
+
+    bool ok = true;
+    for (const auto& partition : metadata->partitions) {
+        if (partition.attributes & LP_PARTITION_ATTR_DISABLED) {
+            ok &= DeleteBackingImage(GetPartitionName(partition));
+        }
+    }
+    return ok;
+}
+
+bool ImageManager::GetMappedImageDevice(const std::string& name, std::string* device) {
+    auto prop_name = GetStatusPropertyName(name);
+    *device = android::base::GetProperty(prop_name, "");
+    if (!device->empty()) {
+        return true;
+    }
+
+    auto& dm = DeviceMapper::Instance();
+    if (dm.GetState(name) == DmDeviceState::INVALID) {
+        return false;
+    }
+    return dm.GetDmDevicePathByName(name, device);
+}
+
+bool ImageManager::MapAllImages(const std::function<bool(std::set<std::string>)>& init) {
+    if (!MetadataExists(metadata_dir_)) {
+        return true;
+    }
+
+    auto metadata = OpenMetadata(metadata_dir_);
+    if (!metadata) {
+        return false;
+    }
+
+    std::set<std::string> devices;
+    for (const auto& name : GetBlockDevicePartitionNames(*metadata.get())) {
+        devices.emplace(name);
+    }
+    if (!init(std::move(devices))) {
+        return false;
+    }
+
+    auto data_device = GetMetadataSuperBlockDevice(*metadata.get());
+    auto data_partition_name = GetBlockDevicePartitionName(*data_device);
+    return CreateLogicalPartitions(*metadata.get(), data_partition_name);
+}
+
 std::unique_ptr<MappedDevice> MappedDevice::Open(IImageManager* manager,
                                                  const std::chrono::milliseconds& timeout_ms,
                                                  const std::string& name) {
diff --git a/fs_mgr/libfiemap/image_test.cpp b/fs_mgr/libfiemap/image_test.cpp
index f05825c..5388b44 100644
--- a/fs_mgr/libfiemap/image_test.cpp
+++ b/fs_mgr/libfiemap/image_test.cpp
@@ -112,6 +112,25 @@
     ASSERT_EQ(android::base::GetProperty(PropertyName(), ""), "");
 }
 
+TEST_F(NativeTest, DisableImage) {
+    ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestImageSize, false, nullptr));
+    ASSERT_TRUE(manager_->BackingImageExists(base_name_));
+    ASSERT_TRUE(manager_->DisableImage(base_name_));
+    ASSERT_TRUE(manager_->RemoveDisabledImages());
+    ASSERT_TRUE(!manager_->BackingImageExists(base_name_));
+}
+
+TEST_F(NativeTest, GetMappedImageDevice) {
+    ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestImageSize, false, nullptr));
+
+    std::string path1, path2;
+    ASSERT_TRUE(manager_->MapImageDevice(base_name_, 5s, &path1));
+    ASSERT_TRUE(manager_->GetMappedImageDevice(base_name_, &path2));
+    EXPECT_EQ(path1, path2);
+
+    ASSERT_TRUE(manager_->UnmapImageDevice(base_name_));
+}
+
 // This fixture is for tests against a simulated device environment. Rather
 // than use /data, we create an image and then layer a new filesystem within
 // it. Each test then decides how to mount and create layered images. This
@@ -193,6 +212,9 @@
 }
 
 TEST_F(ImageTest, IndirectMount) {
+#ifdef SKIP_TEST_IN_PRESUBMIT
+    GTEST_SKIP() << "WIP failure b/148874852";
+#endif
     // Create a simple wrapper around the base device that we'll mount from
     // instead. This will simulate the code paths for dm-crypt/default-key/bow
     // and force us to use device-mapper rather than loop devices.
@@ -220,9 +242,19 @@
 
     ASSERT_TRUE(submanager_->CreateBackingImage(test_image_name_, kTestImageSize, false, nullptr));
 
+    std::set<std::string> backing_devices;
+    auto init = [&](std::set<std::string> devices) -> bool {
+        backing_devices = std::move(devices);
+        return true;
+    };
+
     std::string path;
     ASSERT_TRUE(submanager_->MapImageDevice(test_image_name_, 5s, &path));
     ASSERT_TRUE(android::base::StartsWith(path, "/dev/block/dm-"));
+    ASSERT_TRUE(submanager_->UnmapImageDevice(test_image_name_));
+    ASSERT_TRUE(submanager_->MapAllImages(init));
+    ASSERT_FALSE(backing_devices.empty());
+    ASSERT_TRUE(submanager_->UnmapImageDevice(test_image_name_));
 }
 
 bool Mkdir(const std::string& path) {
diff --git a/fs_mgr/libfiemap/include/libfiemap/fiemap_status.h b/fs_mgr/libfiemap/include/libfiemap/fiemap_status.h
new file mode 100644
index 0000000..d7b2cf1
--- /dev/null
+++ b/fs_mgr/libfiemap/include/libfiemap/fiemap_status.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <errno.h>
+#include <stdint.h>
+
+#include <string>
+
+namespace android::fiemap {
+
+// Represent error status of libfiemap classes.
+class FiemapStatus {
+  public:
+    enum class ErrorCode : int32_t {
+        SUCCESS = 0,
+        // Generic non-recoverable failure.
+        ERROR = INT32_MIN,
+        // Not enough space
+        NO_SPACE = -ENOSPC,
+    };
+
+    // Create from a given errno (specified in errno,h)
+    static FiemapStatus FromErrno(int error_num) { return FiemapStatus(CastErrorCode(-error_num)); }
+
+    // Create from an integer error code that is expected to be an ErrorCode
+    // value. If it isn't, Error() is returned.
+    static FiemapStatus FromErrorCode(int32_t error_code) {
+        return FiemapStatus(CastErrorCode(error_code));
+    }
+
+    // Generic error.
+    static FiemapStatus Error() { return FiemapStatus(ErrorCode::ERROR); }
+
+    // Success.
+    static FiemapStatus Ok() { return FiemapStatus(ErrorCode::SUCCESS); }
+
+    ErrorCode error_code() const { return error_code_; }
+    bool is_ok() const { return error_code() == ErrorCode::SUCCESS; }
+    operator bool() const { return is_ok(); }
+
+    // For logging and debugging only.
+    std::string string() const;
+
+  protected:
+    FiemapStatus(ErrorCode code) : error_code_(code) {}
+
+  private:
+    ErrorCode error_code_;
+
+    static ErrorCode CastErrorCode(int error);
+};
+
+}  // namespace android::fiemap
diff --git a/fs_mgr/libfiemap/include/libfiemap/fiemap_writer.h b/fs_mgr/libfiemap/include/libfiemap/fiemap_writer.h
index c692265..dd345f6 100644
--- a/fs_mgr/libfiemap/include/libfiemap/fiemap_writer.h
+++ b/fs_mgr/libfiemap/include/libfiemap/fiemap_writer.h
@@ -27,6 +27,8 @@
 
 #include <android-base/unique_fd.h>
 
+#include <libfiemap/fiemap_status.h>
+
 namespace android {
 namespace fiemap {
 
@@ -47,6 +49,9 @@
     static FiemapUniquePtr Open(const std::string& file_path, uint64_t file_size,
                                 bool create = true,
                                 std::function<bool(uint64_t, uint64_t)> progress = {});
+    static FiemapStatus Open(const std::string& file_path, uint64_t file_size, FiemapUniquePtr* out,
+                             bool create = true,
+                             std::function<bool(uint64_t, uint64_t)> progress = {});
 
     // Check that a file still has the same extents since it was last opened with FiemapWriter,
     // assuming the file was not resized outside of FiemapWriter. Returns false either on error
diff --git a/fs_mgr/libfiemap/include/libfiemap/image_manager.h b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
index 5ff4628..60b98dc 100644
--- a/fs_mgr/libfiemap/include/libfiemap/image_manager.h
+++ b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
@@ -21,9 +21,11 @@
 #include <chrono>
 #include <functional>
 #include <memory>
+#include <set>
 #include <string>
 
 #include <android-base/unique_fd.h>
+#include <libfiemap/fiemap_status.h>
 #include <liblp/partition_opener.h>
 
 namespace android {
@@ -51,7 +53,9 @@
     // of the image is undefined. If zero-fill is requested, and the operation
     // cannot be completed, the image will be deleted and this function will
     // return false.
-    virtual bool CreateBackingImage(const std::string& name, uint64_t size, int flags) = 0;
+    virtual FiemapStatus CreateBackingImage(
+            const std::string& name, uint64_t size, int flags,
+            std::function<bool(uint64_t, uint64_t)>&& on_progress = nullptr) = 0;
 
     // Delete an image created with CreateBackingImage. Its entry will be
     // removed from the associated lp_metadata file.
@@ -72,7 +76,9 @@
     // Unmap a block device previously mapped with mapBackingImage.
     virtual bool UnmapImageDevice(const std::string& name) = 0;
 
-    // Returns true whether the named backing image exists.
+    // Returns true whether the named backing image exists. This does not check
+    // consistency with the /data partition, so that it can return true in
+    // recovery.
     virtual bool BackingImageExists(const std::string& name) = 0;
 
     // Returns true if the specified image is mapped to a device.
@@ -84,12 +90,35 @@
     virtual bool MapImageWithDeviceMapper(const IPartitionOpener& opener, const std::string& name,
                                           std::string* dev) = 0;
 
+    // If an image was mapped, return the path to its device. Otherwise, return
+    // false. Errors are not reported in this case, calling IsImageMapped is
+    // not necessary.
+    virtual bool GetMappedImageDevice(const std::string& name, std::string* device) = 0;
+
+    // Map all images owned by this manager. This is only intended to be used
+    // during first-stage init, and as such, it does not provide a timeout
+    // (meaning libdm races can't be resolved, as ueventd is not available),
+    // and is not available over binder.
+    //
+    // The callback provided is given the list of dependent block devices.
+    virtual bool MapAllImages(const std::function<bool(std::set<std::string>)>& init) = 0;
+
+    // Mark an image as disabled. This is useful for marking an image as
+    // will-be-deleted in recovery, since recovery cannot mount /data.
+    //
+    // This is not available in binder, since it is intended for recovery.
+    // When binder is available, images can simply be removed.
+    virtual bool DisableImage(const std::string& name) = 0;
+
+    // Remove all images that been marked as disabled.
+    virtual bool RemoveDisabledImages() = 0;
+
     // Get all backing image names.
     virtual std::vector<std::string> GetAllBackingImages() = 0;
 
     // Writes |bytes| zeros to |name| file. If |bytes| is 0, then the
     // whole file if filled with zeros.
-    virtual bool ZeroFillNewImage(const std::string& name, uint64_t bytes) = 0;
+    virtual FiemapStatus ZeroFillNewImage(const std::string& name, uint64_t bytes) = 0;
 
     // Find and remove all images and metadata for this manager.
     virtual bool RemoveAllImages() = 0;
@@ -109,7 +138,8 @@
     static std::unique_ptr<ImageManager> Open(const std::string& dir_prefix);
 
     // Methods that must be implemented from IImageManager.
-    bool CreateBackingImage(const std::string& name, uint64_t size, int flags) override;
+    FiemapStatus CreateBackingImage(const std::string& name, uint64_t size, int flags,
+                                    std::function<bool(uint64_t, uint64_t)>&& on_progress) override;
     bool DeleteBackingImage(const std::string& name) override;
     bool MapImageDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms,
                         std::string* path) override;
@@ -119,15 +149,12 @@
     bool MapImageWithDeviceMapper(const IPartitionOpener& opener, const std::string& name,
                                   std::string* dev) override;
     bool RemoveAllImages() override;
+    bool DisableImage(const std::string& name) override;
+    bool RemoveDisabledImages() override;
+    bool GetMappedImageDevice(const std::string& name, std::string* device) override;
+    bool MapAllImages(const std::function<bool(std::set<std::string>)>& init) override;
 
     std::vector<std::string> GetAllBackingImages();
-    // Same as CreateBackingImage, but provides a progress notification.
-    bool CreateBackingImage(const std::string& name, uint64_t size, int flags,
-                            std::function<bool(uint64_t, uint64_t)>&& on_progress);
-
-    // Returns true if the named partition exists. This does not check the
-    // consistency of the backing image/data file.
-    bool PartitionExists(const std::string& name);
 
     // Validates that all images still have pinned extents. This will be removed
     // once b/134588268 is fixed.
@@ -136,7 +163,7 @@
     void set_partition_opener(std::unique_ptr<IPartitionOpener>&& opener);
 
     // Writes |bytes| zeros at the beginning of the passed image
-    bool ZeroFillNewImage(const std::string& name, uint64_t bytes);
+    FiemapStatus ZeroFillNewImage(const std::string& name, uint64_t bytes);
 
   private:
     ImageManager(const std::string& metadata_dir, const std::string& data_dir);
diff --git a/fs_mgr/libfiemap/include/libfiemap/split_fiemap_writer.h b/fs_mgr/libfiemap/include/libfiemap/split_fiemap_writer.h
index feffb3d..d739fcf 100644
--- a/fs_mgr/libfiemap/include/libfiemap/split_fiemap_writer.h
+++ b/fs_mgr/libfiemap/include/libfiemap/split_fiemap_writer.h
@@ -25,7 +25,8 @@
 
 #include <android-base/unique_fd.h>
 
-#include "fiemap_writer.h"
+#include <libfiemap/fiemap_status.h>
+#include <libfiemap/fiemap_writer.h>
 
 namespace android {
 namespace fiemap {
@@ -43,6 +44,9 @@
     static std::unique_ptr<SplitFiemap> Create(const std::string& file_path, uint64_t file_size,
                                                uint64_t max_piece_size,
                                                ProgressCallback progress = {});
+    static FiemapStatus Create(const std::string& file_path, uint64_t file_size,
+                               uint64_t max_piece_size, std::unique_ptr<SplitFiemap>* out_val,
+                               ProgressCallback progress = {});
 
     // Open an existing split fiemap file.
     static std::unique_ptr<SplitFiemap> Open(const std::string& file_path);
diff --git a/fs_mgr/libfiemap/metadata.cpp b/fs_mgr/libfiemap/metadata.cpp
index 597efe9..b0dfb5c 100644
--- a/fs_mgr/libfiemap/metadata.cpp
+++ b/fs_mgr/libfiemap/metadata.cpp
@@ -39,7 +39,13 @@
 
 bool MetadataExists(const std::string& metadata_dir) {
     auto metadata_file = GetMetadataFile(metadata_dir);
-    return access(metadata_file.c_str(), F_OK) == 0;
+    if (access(metadata_file.c_str(), F_OK)) {
+        if (errno != ENOENT) {
+            PLOG(ERROR) << "Access " << metadata_file << " failed:";
+        }
+        return false;
+    }
+    return true;
 }
 
 std::unique_ptr<LpMetadata> OpenMetadata(const std::string& metadata_dir) {
@@ -61,7 +67,7 @@
     std::unique_ptr<MetadataBuilder> builder;
     if (access(metadata_file.c_str(), R_OK)) {
         if (errno != ENOENT) {
-            PLOG(ERROR) << "access " << metadata_file << " failed:";
+            PLOG(ERROR) << "Access " << metadata_file << " failed:";
             return nullptr;
         }
 
@@ -112,7 +118,12 @@
 
 bool RemoveAllMetadata(const std::string& dir) {
     auto metadata_file = GetMetadataFile(dir);
-    return android::base::RemoveFileIfExists(metadata_file);
+    std::string err;
+    if (!android::base::RemoveFileIfExists(metadata_file, &err)) {
+        LOG(ERROR) << "Could not remove metadata file: " << err;
+        return false;
+    }
+    return true;
 }
 
 bool FillPartitionExtents(MetadataBuilder* builder, Partition* partition, SplitFiemap* file,
@@ -192,5 +203,23 @@
     return SaveMetadata(builder.get(), metadata_dir);
 }
 
+bool AddAttributes(const std::string& metadata_dir, const std::string& partition_name,
+                   uint32_t attributes) {
+    auto metadata = OpenMetadata(metadata_dir);
+    if (!metadata) {
+        return false;
+    }
+    auto builder = MetadataBuilder::New(*metadata.get());
+    if (!builder) {
+        return false;
+    }
+    auto partition = builder->FindPartition(partition_name);
+    if (!partition) {
+        return false;
+    }
+    partition->set_attributes(partition->attributes() | attributes);
+    return SaveMetadata(builder.get(), metadata_dir);
+}
+
 }  // namespace fiemap
 }  // namespace android
diff --git a/fs_mgr/libfiemap/metadata.h b/fs_mgr/libfiemap/metadata.h
index f0ce23e..4eb3ad5 100644
--- a/fs_mgr/libfiemap/metadata.h
+++ b/fs_mgr/libfiemap/metadata.h
@@ -29,6 +29,8 @@
 std::unique_ptr<android::fs_mgr::LpMetadata> OpenMetadata(const std::string& metadata_dir);
 bool UpdateMetadata(const std::string& metadata_dir, const std::string& partition_name,
                     SplitFiemap* file, uint64_t partition_size, bool readonly);
+bool AddAttributes(const std::string& metadata_dir, const std::string& partition_name,
+                   uint32_t attributes);
 bool RemoveImageMetadata(const std::string& metadata_dir, const std::string& partition_name);
 bool RemoveAllMetadata(const std::string& dir);
 
diff --git a/fs_mgr/libfiemap/split_fiemap_writer.cpp b/fs_mgr/libfiemap/split_fiemap_writer.cpp
index cc54f20..12c7397 100644
--- a/fs_mgr/libfiemap/split_fiemap_writer.cpp
+++ b/fs_mgr/libfiemap/split_fiemap_writer.cpp
@@ -45,16 +45,28 @@
 std::unique_ptr<SplitFiemap> SplitFiemap::Create(const std::string& file_path, uint64_t file_size,
                                                  uint64_t max_piece_size,
                                                  ProgressCallback progress) {
+    std::unique_ptr<SplitFiemap> ret;
+    if (!Create(file_path, file_size, max_piece_size, &ret, progress).is_ok()) {
+        return nullptr;
+    }
+    return ret;
+}
+
+FiemapStatus SplitFiemap::Create(const std::string& file_path, uint64_t file_size,
+                                 uint64_t max_piece_size, std::unique_ptr<SplitFiemap>* out_val,
+                                 ProgressCallback progress) {
+    out_val->reset();
+
     if (!file_size) {
         LOG(ERROR) << "Cannot create a fiemap for a 0-length file: " << file_path;
-        return nullptr;
+        return FiemapStatus::Error();
     }
 
     if (!max_piece_size) {
-        max_piece_size = DetermineMaximumFileSize(file_path);
-        if (!max_piece_size) {
+        auto status = DetermineMaximumFileSize(file_path, &max_piece_size);
+        if (!status.is_ok()) {
             LOG(ERROR) << "Could not determine maximum file size for " << file_path;
-            return nullptr;
+            return status;
         }
     }
 
@@ -75,7 +87,6 @@
         }
         return true;
     };
-
     std::unique_ptr<SplitFiemap> out(new SplitFiemap());
     out->creating_ = true;
     out->list_file_ = file_path;
@@ -85,14 +96,17 @@
     while (remaining_bytes) {
         if (out->files_.size() >= kMaxFilePieces) {
             LOG(ERROR) << "Requested size " << file_size << " created too many split files";
-            return nullptr;
+            out.reset();
+            return FiemapStatus::Error();
         }
         std::string chunk_path =
                 android::base::StringPrintf("%s.%04d", file_path.c_str(), (int)out->files_.size());
         uint64_t chunk_size = std::min(max_piece_size, remaining_bytes);
-        auto writer = FiemapWriter::Open(chunk_path, chunk_size, true, on_progress);
-        if (!writer) {
-            return nullptr;
+        FiemapUniquePtr writer;
+        auto status = FiemapWriter::Open(chunk_path, chunk_size, &writer, true, on_progress);
+        if (!status.is_ok()) {
+            out.reset();
+            return status;
         }
 
         // To make sure the alignment doesn't create too much inconsistency, we
@@ -110,20 +124,23 @@
     unique_fd fd(open(out->list_file_.c_str(), O_CREAT | O_WRONLY | O_CLOEXEC, 0660));
     if (fd < 0) {
         PLOG(ERROR) << "Failed to open " << file_path;
-        return nullptr;
+        out.reset();
+        return FiemapStatus::FromErrno(errno);
     }
 
     for (const auto& writer : out->files_) {
         std::string line = android::base::Basename(writer->file_path()) + "\n";
         if (!android::base::WriteFully(fd, line.data(), line.size())) {
             PLOG(ERROR) << "Write failed " << file_path;
-            return nullptr;
+            out.reset();
+            return FiemapStatus::FromErrno(errno);
         }
     }
 
     // Unset this bit, so we don't unlink on destruction.
     out->creating_ = false;
-    return out;
+    *out_val = std::move(out);
+    return FiemapStatus::Ok();
 }
 
 std::unique_ptr<SplitFiemap> SplitFiemap::Open(const std::string& file_path) {
diff --git a/fs_mgr/libfiemap/utility.cpp b/fs_mgr/libfiemap/utility.cpp
index 955e544..bbb0510 100644
--- a/fs_mgr/libfiemap/utility.cpp
+++ b/fs_mgr/libfiemap/utility.cpp
@@ -37,29 +37,30 @@
 
 static constexpr char kUserdataDevice[] = "/dev/block/by-name/userdata";
 
-uint64_t DetermineMaximumFileSize(const std::string& file_path) {
+FiemapStatus DetermineMaximumFileSize(const std::string& file_path, uint64_t* result) {
     // Create the smallest file possible (one block).
-    auto writer = FiemapWriter::Open(file_path, 1);
-    if (!writer) {
-        return 0;
+    FiemapUniquePtr writer;
+    auto status = FiemapWriter::Open(file_path, 1, &writer);
+    if (!status.is_ok()) {
+        return status;
     }
 
-    uint64_t result = 0;
+    *result = 0;
     switch (writer->fs_type()) {
         case EXT4_SUPER_MAGIC:
             // The minimum is 16GiB, so just report that. If we wanted we could parse the
             // superblock and figure out if 64-bit support is enabled.
-            result = 17179869184ULL;
+            *result = 17179869184ULL;
             break;
         case F2FS_SUPER_MAGIC:
             // Formula is from https://www.kernel.org/doc/Documentation/filesystems/f2fs.txt
             // 4KB * (923 + 2 * 1018 + 2 * 1018 * 1018 + 1018 * 1018 * 1018) := 3.94TB.
-            result = 4329690886144ULL;
+            *result = 4329690886144ULL;
             break;
         case MSDOS_SUPER_MAGIC:
             // 4GB-1, which we want aligned to the block size.
-            result = 4294967295;
-            result -= (result % writer->block_size());
+            *result = 4294967295;
+            *result -= (*result % writer->block_size());
             break;
         default:
             LOG(ERROR) << "Unknown file system type: " << writer->fs_type();
@@ -70,7 +71,7 @@
     writer = nullptr;
     unlink(file_path.c_str());
 
-    return result;
+    return FiemapStatus::Ok();
 }
 
 // Given a SplitFiemap, this returns a device path that will work during first-
diff --git a/fs_mgr/libfiemap/utility.h b/fs_mgr/libfiemap/utility.h
index 24ebc57..4c0bc2b 100644
--- a/fs_mgr/libfiemap/utility.h
+++ b/fs_mgr/libfiemap/utility.h
@@ -28,7 +28,7 @@
 // Given a file that will be created, determine the maximum size its containing
 // filesystem allows. Note this is a theoretical maximum size; free space is
 // ignored entirely.
-uint64_t DetermineMaximumFileSize(const std::string& file_path);
+FiemapStatus DetermineMaximumFileSize(const std::string& file_path, uint64_t* result);
 
 // Given a SplitFiemap, this returns a device path that will work during first-
 // stage init (i.e., its path can be found by InitRequiredDevices).
diff --git a/fs_mgr/libfs_avb/Android.bp b/fs_mgr/libfs_avb/Android.bp
index bf51fe7..8fb9697 100644
--- a/fs_mgr/libfs_avb/Android.bp
+++ b/fs_mgr/libfs_avb/Android.bp
@@ -31,6 +31,7 @@
     static_libs: [
         "libavb",
         "libdm",
+        "libgsi",
         "libfstab",
     ],
     export_static_lib_headers: [
diff --git a/fs_mgr/libfs_avb/fs_avb.cpp b/fs_mgr/libfs_avb/fs_avb.cpp
index 8770a6b..5d504ab 100644
--- a/fs_mgr/libfs_avb/fs_avb.cpp
+++ b/fs_mgr/libfs_avb/fs_avb.cpp
@@ -33,6 +33,7 @@
 #include <android-base/strings.h>
 #include <libavb/libavb.h>
 #include <libdm/dm.h>
+#include <libgsi/libgsi.h>
 
 #include "avb_ops.h"
 #include "avb_util.h"
@@ -266,14 +267,28 @@
     return avb_handle;
 }
 
-AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(const FstabEntry& fstab_entry) {
-    if (fstab_entry.avb_keys.empty()) {
+static bool IsAvbPermissive() {
+    if (IsDeviceUnlocked()) {
+        // Manually putting a file under metadata partition can enforce AVB verification.
+        if (!access(DSU_METADATA_PREFIX "avb_enforce", F_OK)) {
+            LINFO << "Enforcing AVB verification when the device is unlocked";
+            return false;
+        }
+        return true;
+    }
+    return false;
+}
+
+AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(const FstabEntry& fstab_entry,
+                                            const std::vector<std::string>& preload_avb_key_blobs) {
+    // At least one of the following should be provided for public key matching.
+    if (preload_avb_key_blobs.empty() && fstab_entry.avb_keys.empty()) {
         LERROR << "avb_keys=/path/to/key(s) is missing for " << fstab_entry.mount_point;
         return nullptr;
     }
 
     // Binds allow_verification_error and rollback_protection to device unlock state.
-    bool allow_verification_error = IsDeviceUnlocked();
+    bool allow_verification_error = IsAvbPermissive();
     bool rollback_protection = !allow_verification_error;
 
     std::string public_key_data;
@@ -309,18 +324,36 @@
             return nullptr;
     }
 
-    // fstab_entry.avb_keys might be either a directory containing multiple keys,
-    // or a string indicating multiple keys separated by ':'.
-    std::vector<std::string> allowed_avb_keys;
-    auto list_avb_keys_in_dir = ListFiles(fstab_entry.avb_keys);
-    if (list_avb_keys_in_dir) {
-        std::sort(list_avb_keys_in_dir->begin(), list_avb_keys_in_dir->end());
-        allowed_avb_keys = *list_avb_keys_in_dir;
-    } else {
-        allowed_avb_keys = Split(fstab_entry.avb_keys, ":");
+    bool public_key_match = false;
+    // Performs key matching for preload_avb_key_blobs first, if it is present.
+    if (!public_key_data.empty() && !preload_avb_key_blobs.empty()) {
+        if (std::find(preload_avb_key_blobs.begin(), preload_avb_key_blobs.end(),
+                      public_key_data) != preload_avb_key_blobs.end()) {
+            public_key_match = true;
+        }
+    }
+    // Performs key matching for fstab_entry.avb_keys if necessary.
+    // Note that it is intentional to match both preload_avb_key_blobs and fstab_entry.avb_keys.
+    // Some keys might only be availble before init chroots into /system, e.g., /avb/key1
+    // in the first-stage ramdisk, while other keys might only be available after the chroot,
+    // e.g., /system/etc/avb/key2.
+    if (!public_key_data.empty() && !public_key_match) {
+        // fstab_entry.avb_keys might be either a directory containing multiple keys,
+        // or a string indicating multiple keys separated by ':'.
+        std::vector<std::string> allowed_avb_keys;
+        auto list_avb_keys_in_dir = ListFiles(fstab_entry.avb_keys);
+        if (list_avb_keys_in_dir.ok()) {
+            std::sort(list_avb_keys_in_dir->begin(), list_avb_keys_in_dir->end());
+            allowed_avb_keys = *list_avb_keys_in_dir;
+        } else {
+            allowed_avb_keys = Split(fstab_entry.avb_keys, ":");
+        }
+        if (ValidatePublicKeyBlob(public_key_data, allowed_avb_keys)) {
+            public_key_match = true;
+        }
     }
 
-    if (!ValidatePublicKeyBlob(public_key_data, allowed_avb_keys)) {
+    if (!public_key_match) {
         avb_handle->status_ = AvbHandleStatus::kVerificationError;
         LWARNING << "Found unknown public key used to sign " << fstab_entry.mount_point;
         if (!allow_verification_error) {
@@ -344,15 +377,15 @@
     return LoadAndVerifyVbmeta("vbmeta", fs_mgr_get_slot_suffix(), fs_mgr_get_other_slot_suffix(),
                                {} /* expected_public_key, already checked by bootloader */,
                                HashAlgorithm::kSHA256,
-                               IsDeviceUnlocked(), /* allow_verification_error */
-                               true,               /* load_chained_vbmeta */
+                               IsAvbPermissive(), /* allow_verification_error */
+                               true,              /* load_chained_vbmeta */
                                false, /* rollback_protection, already checked by bootloader */
                                nullptr /* custom_device_path */);
 }
 
 // TODO(b/128807537): removes this function.
 AvbUniquePtr AvbHandle::Open() {
-    bool is_device_unlocked = IsDeviceUnlocked();
+    bool allow_verification_error = IsAvbPermissive();
 
     AvbUniquePtr avb_handle(new AvbHandle());
     if (!avb_handle) {
@@ -361,8 +394,9 @@
     }
 
     FsManagerAvbOps avb_ops;
-    AvbSlotVerifyFlags flags = is_device_unlocked ? AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR
-                                                  : AVB_SLOT_VERIFY_FLAGS_NONE;
+    AvbSlotVerifyFlags flags = allow_verification_error
+                                       ? AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR
+                                       : AVB_SLOT_VERIFY_FLAGS_NONE;
     AvbSlotVerifyResult verify_result =
             avb_ops.AvbSlotVerify(fs_mgr_get_slot_suffix(), flags, &avb_handle->vbmeta_images_);
 
@@ -385,9 +419,8 @@
             break;
         case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
         case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED:
-            if (!is_device_unlocked) {
-                LERROR << "ERROR_VERIFICATION / PUBLIC_KEY_REJECTED isn't allowed "
-                       << "if the device is LOCKED";
+            if (!allow_verification_error) {
+                LERROR << "ERROR_VERIFICATION / PUBLIC_KEY_REJECTED isn't allowed ";
                 return nullptr;
             }
             avb_handle->status_ = AvbHandleStatus::kVerificationError;
diff --git a/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
index 521f2d5..4702e68 100644
--- a/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
+++ b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
@@ -85,8 +85,15 @@
     // TODO(bowgotsai): remove Open() and switch to LoadAndVerifyVbmeta().
     static AvbUniquePtr Open();                 // loads inline vbmeta, via libavb.
     static AvbUniquePtr LoadAndVerifyVbmeta();  // loads inline vbmeta.
-    static AvbUniquePtr LoadAndVerifyVbmeta(
-            const FstabEntry& fstab_entry);     // loads offline vbmeta.
+
+    // The caller can specify optional preload_avb_key_blobs for public key matching.
+    // This is mostly for init to preload AVB keys before chroot into /system.
+    // Both preload_avb_key_blobs and fstab_entry.avb_keys (file paths) will be used
+    // for public key matching.
+    static AvbUniquePtr LoadAndVerifyVbmeta(  // loads offline vbmeta.
+            const FstabEntry& fstab_entry,
+            const std::vector<std::string>& preload_avb_key_blobs = {});
+
     static AvbUniquePtr LoadAndVerifyVbmeta(    // loads offline vbmeta.
             const std::string& partition_name, const std::string& ab_suffix,
             const std::string& ab_other_suffix, const std::string& expected_public_key,
diff --git a/fs_mgr/libfs_avb/tests/util_test.cpp b/fs_mgr/libfs_avb/tests/util_test.cpp
index e64282b..5c388aa 100644
--- a/fs_mgr/libfs_avb/tests/util_test.cpp
+++ b/fs_mgr/libfs_avb/tests/util_test.cpp
@@ -232,8 +232,7 @@
 
     // List files for comparison.
     auto result = ListFiles(test_dir.value());
-    ASSERT_TRUE(result);
-    ASSERT_TRUE(result.has_value());
+    ASSERT_RESULT_OK(result);
     auto files = result.value();
     EXPECT_EQ(3UL, files.size());
     // Sort them offline for comparison.
@@ -266,8 +265,7 @@
 
     // List files for comparison.
     auto result = ListFiles(test_dir.value());
-    ASSERT_TRUE(result);
-    ASSERT_TRUE(result.has_value());
+    ASSERT_RESULT_OK(result);
     auto files = result.value();
     EXPECT_EQ(2UL, files.size());  // Should not include the symlink file.
     // Sort them offline for comparison.
@@ -287,7 +285,7 @@
     base::FilePath no_such_dir = tmp_dir.Append("not_such_dir");
 
     auto fail = ListFiles(no_such_dir.value());
-    ASSERT_FALSE(fail);
+    ASSERT_FALSE(fail.ok());
     EXPECT_EQ(ENOENT, fail.error().code());
     EXPECT_TRUE(android::base::StartsWith(fail.error().message(), "Failed to opendir: "));
 }
@@ -303,8 +301,7 @@
 
     // List files without sorting.
     auto result = ListFiles(test_dir.value());
-    ASSERT_TRUE(result);
-    ASSERT_TRUE(result.has_value());
+    ASSERT_RESULT_OK(result);
     auto files = result.value();
     EXPECT_EQ(0UL, files.size());
 
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 7e7f393..d496466 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -852,7 +852,7 @@
             return nullptr;
         }
 
-        if (partition->attributes() & LP_PARTITION_ATTR_UPDATED) {
+        if (partition->attributes() & LP_PARTITION_ATTRIBUTE_MASK_V1) {
             static const uint16_t kMinVersion = LP_METADATA_VERSION_FOR_UPDATED_ATTR;
             metadata->header.minor_version = std::max(metadata->header.minor_version, kMinVersion);
         }
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index 851f041..f7738fb 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -145,6 +145,7 @@
     std::vector<std::unique_ptr<Extent>> extents_;
     uint32_t attributes_;
     uint64_t size_;
+    bool disabled_;
 };
 
 // An interval in the metadata. This is similar to a LinearExtent with one difference.
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
index d3c9874..41d8b0c 100644
--- a/fs_mgr/liblp/include/liblp/metadata_format.h
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -72,13 +72,17 @@
  */
 #define LP_PARTITION_ATTR_UPDATED (1 << 2)
 
+/* This flag marks a partition as disabled. It should not be used or mapped. */
+#define LP_PARTITION_ATTR_DISABLED (1 << 3)
+
 /* Mask that defines all valid attributes. When changing this, make sure to
  * update ParseMetadata().
  */
 #define LP_PARTITION_ATTRIBUTE_MASK_V0 \
     (LP_PARTITION_ATTR_READONLY | LP_PARTITION_ATTR_SLOT_SUFFIXED)
-#define LP_PARTITION_ATTRIBUTE_MASK_V1 (LP_PARTITION_ATTRIBUTE_MASK_V0 | LP_PARTITION_ATTR_UPDATED)
-#define LP_PARTITION_ATTRIBUTE_MASK LP_PARTITION_ATTRIBUTE_MASK_V1
+#define LP_PARTITION_ATTRIBUTE_MASK_V1 (LP_PARTITION_ATTR_UPDATED | LP_PARTITION_ATTR_DISABLED)
+#define LP_PARTITION_ATTRIBUTE_MASK \
+    (LP_PARTITION_ATTRIBUTE_MASK_V0 | LP_PARTITION_ATTRIBUTE_MASK_V1)
 
 /* Default name of the physical partition that holds logical partition entries.
  * The layout of this partition will look like:
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
index 30c17e4..e6fd9f7 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -280,11 +280,9 @@
         return nullptr;
     }
 
-    uint32_t valid_attributes = 0;
+    uint32_t valid_attributes = LP_PARTITION_ATTRIBUTE_MASK_V0;
     if (metadata->header.minor_version >= LP_METADATA_VERSION_FOR_UPDATED_ATTR) {
-        valid_attributes = LP_PARTITION_ATTRIBUTE_MASK_V1;
-    } else {
-        valid_attributes = LP_PARTITION_ATTRIBUTE_MASK_V0;
+        valid_attributes |= LP_PARTITION_ATTRIBUTE_MASK_V1;
     }
 
     // ValidateTableSize ensured that |cursor| is valid for the number of
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index eadcecc..d274ba4 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -24,14 +24,13 @@
     ],
     shared_libs: [
         "libbase",
+        "libcutils",
         "liblog",
         "liblp",
     ],
     static_libs: [
-        "libcutils",
         "libdm",
         "libfstab",
-        "liblp",
         "update_metadata-protos",
     ],
     whole_static_libs: [
@@ -73,8 +72,10 @@
         "android/snapshot/snapshot.proto",
         "device_info.cpp",
         "snapshot.cpp",
+        "snapshot_stats.cpp",
         "snapshot_metadata_updater.cpp",
         "partition_cow_creator.cpp",
+        "return.cpp",
         "utility.cpp",
     ],
 }
@@ -95,6 +96,16 @@
     static_libs: [
         "libfs_mgr_binder"
     ],
+
+    shared_libs: [
+        // TODO(b/148818798): remove when parent bug is fixed
+        "libutilscallstack",
+    ],
+    cflags: [
+        "-g",
+        "-O0",
+        "-DLIBSNAPSHOT_USE_CALLSTACK",
+    ],
 }
 
 cc_library_static {
@@ -127,6 +138,7 @@
         "include_test",
     ],
     srcs: [
+        "android/snapshot/snapshot.proto",
         "test_helpers.cpp",
     ],
     shared_libs: [
@@ -144,42 +156,64 @@
     ],
     static_libs: [
         "libfs_mgr",
-        "libgtest",
         "libgmock",
+        "libgtest",
     ],
 }
 
-cc_test {
-    name: "libsnapshot_test",
+cc_defaults {
+    name: "libsnapshot_test_defaults",
     defaults: ["libsnapshot_defaults"],
     srcs: [
-        "snapshot_test.cpp",
         "partition_cow_creator_test.cpp",
         "snapshot_metadata_updater_test.cpp",
-        "test_helpers.cpp",
+        "snapshot_test.cpp",
     ],
     shared_libs: [
+        "android.hardware.boot@1.0",
+        "android.hardware.boot@1.1",
         "libbinder",
         "libcrypto",
         "libhidlbase",
         "libprotobuf-cpp-lite",
+        "libsparse",
         "libutils",
+        "libz",
+
+        // TODO(b/148818798): remove when parent bug is fixed
+        "libutilscallstack",
     ],
     static_libs: [
-        "android.hardware.boot@1.0",
-        "android.hardware.boot@1.1",
         "libfs_mgr",
-        "libgmock",
         "libgsi",
+        "libgmock",
         "liblp",
         "libsnapshot",
         "libsnapshot_test_helpers",
-        "libsparse",
-        "libz",
     ],
     header_libs: [
         "libstorage_literals_headers",
     ],
+    test_suites: [
+        "vts-core",
+        "device-tests"
+    ],
+    test_min_api_level: 29,
+    auto_gen_config: true,
+    require_root: true,
+}
+
+cc_test {
+    name: "vts_libsnapshot_test",
+    defaults: ["libsnapshot_test_defaults"],
+}
+
+cc_test {
+    name: "vts_libsnapshot_test_presubmit",
+    defaults: ["libsnapshot_test_defaults"],
+    cppflags: [
+        "-DSKIP_TEST_IN_PRESUBMIT",
+    ],
 }
 
 cc_binary {
@@ -189,7 +223,6 @@
     ],
     static_libs: [
         "libdm",
-        "libext2_uuid",
         "libfstab",
         "libsnapshot",
     ],
@@ -198,7 +231,7 @@
         "android.hardware.boot@1.1",
         "libbase",
         "libbinder",
-        "libbinderthreadstate",
+        "libext2_uuid",
         "libext4_utils",
         "libfs_mgr_binder",
         "libhidlbase",
@@ -206,6 +239,9 @@
         "liblp",
         "libprotobuf-cpp-lite",
         "libutils",
+
+        // TODO(b/148818798): remove when parent bug is fixed.
+        "libutilscallstack",
     ],
     init_rc: [
         "snapshotctl.rc",
diff --git a/fs_mgr/libsnapshot/OWNERS b/fs_mgr/libsnapshot/OWNERS
index 0cfa7e4..801c446 100644
--- a/fs_mgr/libsnapshot/OWNERS
+++ b/fs_mgr/libsnapshot/OWNERS
@@ -1,2 +1,3 @@
+balsini@google.com
 dvander@google.com
 elsk@google.com
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index 629c3a4..2ac0c44 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -85,3 +85,59 @@
     // This is non-zero when |state| == MERGING or MERGE_COMPLETED.
     uint64 metadata_sectors = 8;
 }
+
+// Next: 8
+enum UpdateState {
+    // No update or merge is in progress.
+    None = 0;
+
+    // An update is applying; snapshots may already exist.
+    Initiated = 1;
+
+    // An update is pending, but has not been successfully booted yet.
+    Unverified = 2;
+
+    // The kernel is merging in the background.
+    Merging = 3;
+
+    // Post-merge cleanup steps could not be completed due to a transient
+    // error, but the next reboot will finish any pending operations.
+    MergeNeedsReboot = 4;
+
+    // Merging is complete, and needs to be acknowledged.
+    MergeCompleted = 5;
+
+    // Merging failed due to an unrecoverable error.
+    MergeFailed = 6;
+
+    // The update was implicitly cancelled, either by a rollback or a flash
+    // operation via fastboot. This state can only be returned by WaitForMerge.
+    Cancelled = 7;
+};
+
+// Next: 5
+message SnapshotUpdateStatus {
+    UpdateState state = 1;
+
+    // Total number of sectors allocated in the COW files before performing the
+    // merge operation.  This field is used to keep track of the total number
+    // of sectors modified to monitor and show the progress of the merge during
+    // an update.
+    uint64 sectors_allocated = 2;
+
+    // Total number of sectors of all the snapshot devices.
+    uint64 total_sectors = 3;
+
+    // Sectors allocated for metadata in all the snapshot devices.
+    uint64 metadata_sectors = 4;
+}
+
+// Next: 2
+message SnapshotMergeReport {
+    // Status of the update after the merge attempts.
+    UpdateState state = 1;
+
+    // Number of reboots that occurred after issuing and before completeing the
+    // merge of all the snapshot devices.
+    int32 resume_count = 2;
+}
diff --git a/fs_mgr/libsnapshot/device_info.cpp b/fs_mgr/libsnapshot/device_info.cpp
index bacb41c..0e90100 100644
--- a/fs_mgr/libsnapshot/device_info.cpp
+++ b/fs_mgr/libsnapshot/device_info.cpp
@@ -22,6 +22,7 @@
 namespace snapshot {
 
 #ifdef LIBSNAPSHOT_USE_HAL
+using android::hardware::boot::V1_0::BoolResult;
 using android::hardware::boot::V1_0::CommandResult;
 #endif
 
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/return.h b/fs_mgr/libsnapshot/include/libsnapshot/return.h
new file mode 100644
index 0000000..1f132fa
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/return.h
@@ -0,0 +1,61 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <stdint.h>
+#include <string.h>
+
+#include <libfiemap/fiemap_status.h>
+
+namespace android::snapshot {
+
+// SnapshotManager functions return either bool or Return objects. "Return" types provides
+// more information about the reason of the failure.
+class Return {
+    using FiemapStatus = android::fiemap::FiemapStatus;
+
+  public:
+    enum class ErrorCode : int32_t {
+        SUCCESS = static_cast<int32_t>(FiemapStatus::ErrorCode::SUCCESS),
+        ERROR = static_cast<int32_t>(FiemapStatus::ErrorCode::ERROR),
+        NEEDS_REBOOT = ERROR + 1,
+        NO_SPACE = static_cast<int32_t>(FiemapStatus::ErrorCode::NO_SPACE),
+    };
+    ErrorCode error_code() const { return error_code_; }
+    bool is_ok() const { return error_code() == ErrorCode::SUCCESS; }
+    operator bool() const { return is_ok(); }
+    // Total required size on /userdata.
+    uint64_t required_size() const { return required_size_; }
+    std::string string() const;
+
+    static Return Ok() { return Return(ErrorCode::SUCCESS); }
+    static Return Error() { return Return(ErrorCode::ERROR); }
+    static Return NoSpace(uint64_t size) { return Return(ErrorCode::NO_SPACE, size); }
+    static Return NeedsReboot() { return Return(ErrorCode::NEEDS_REBOOT); }
+    // Does not set required_size_ properly even when status.error_code() == NO_SPACE.
+    explicit Return(const FiemapStatus& status)
+        : error_code_(FromFiemapStatusErrorCode(status.error_code())), required_size_(0) {}
+
+  private:
+    ErrorCode error_code_;
+    uint64_t required_size_;
+    Return(ErrorCode error_code, uint64_t required_size = 0)
+        : error_code_(error_code), required_size_(required_size) {}
+
+    // FiemapStatus::ErrorCode -> ErrorCode
+    static ErrorCode FromFiemapStatusErrorCode(FiemapStatus::ErrorCode error_code);
+};
+
+}  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 445e6db..b440c71 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -26,6 +26,7 @@
 #include <vector>
 
 #include <android-base/unique_fd.h>
+#include <android/snapshot/snapshot.pb.h>
 #include <fs_mgr_dm_linear.h>
 #include <libdm/dm.h>
 #include <libfiemap/image_manager.h>
@@ -34,6 +35,7 @@
 #include <update_engine/update_metadata.pb.h>
 
 #include <libsnapshot/auto_device.h>
+#include <libsnapshot/return.h>
 
 #ifndef FRIEND_TEST
 #define FRIEND_TEST(test_set_name, individual_test) \
@@ -74,34 +76,11 @@
 
 bool SourceCopyOperationIsClone(const chromeos_update_engine::InstallOperation& operation);
 
-enum class UpdateState : unsigned int {
-    // No update or merge is in progress.
-    None,
-
-    // An update is applying; snapshots may already exist.
-    Initiated,
-
-    // An update is pending, but has not been successfully booted yet.
-    Unverified,
-
-    // The kernel is merging in the background.
-    Merging,
-
-    // Post-merge cleanup steps could not be completed due to a transient
-    // error, but the next reboot will finish any pending operations.
-    MergeNeedsReboot,
-
-    // Merging is complete, and needs to be acknowledged.
-    MergeCompleted,
-
-    // Merging failed due to an unrecoverable error.
-    MergeFailed,
-
-    // The update was implicitly cancelled, either by a rollback or a flash
-    // operation via fastboot. This state can only be returned by WaitForMerge.
-    Cancelled
+enum class CreateResult : unsigned int {
+    ERROR,
+    CREATED,
+    NOT_CREATED,
 };
-std::ostream& operator<<(std::ostream& os, UpdateState state);
 
 class SnapshotManager final {
     using CreateLogicalPartitionParams = android::fs_mgr::CreateLogicalPartitionParams;
@@ -110,6 +89,9 @@
     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:
     // Dependency injection for testing.
@@ -194,16 +176,19 @@
     // See InitiateMerge() and ProcessUpdateState() for details.
     // Returns:
     //   - None if no merge to initiate
+    //   - Unverified if called on the source slot
     //   - MergeCompleted if merge is completed
     //   - other states indicating an error has occurred
-    UpdateState InitiateMergeAndWait();
+    UpdateState InitiateMergeAndWait(SnapshotMergeReport* report = nullptr);
 
     // Wait for the merge if rebooted into the new slot. Does NOT initiate a
     // merge. If the merge has not been initiated (but should be), wait.
     // Returns:
-    //   - true there is no merge or merge finishes
-    //   - false indicating an error has occurred
-    bool WaitForMerge();
+    //   - Return::Ok(): there is no merge or merge finishes
+    //   - Return::NeedsReboot(): merge finishes but need a reboot before
+    //     applying the next update.
+    //   - Return::Error(): other irrecoverable errors
+    Return WaitForMerge();
 
     // Find the status of the current update, if any.
     //
@@ -216,7 +201,7 @@
     // 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.
-    bool CreateUpdateSnapshots(const DeltaArchiveManifest& manifest);
+    Return CreateUpdateSnapshots(const DeltaArchiveManifest& manifest);
 
     // Map a snapshotted partition for OTA clients to write to. Write-protected regions are
     // determined previously in CreateSnapshots.
@@ -231,7 +216,8 @@
 
     // 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);
+    bool CreateLogicalAndSnapshotPartitions(const std::string& super_device,
+                                            const std::chrono::milliseconds& timeout_ms = {});
 
     // 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
@@ -246,6 +232,17 @@
     // optional callback fires periodically to query progress via GetUpdateState.
     bool HandleImminentDataWipe(const std::function<void()>& callback = {});
 
+    // 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
+    // /system in recovery.
+    // This function returns:
+    // - CreateResult::CREATED if snapshot devices were successfully created;
+    // - CreateResult::NOT_CREATED if it was not necessary to create snapshot
+    // devices;
+    // - CreateResult::ERROR if a fatal error occurred, mounting /system should
+    // be aborted.
+    CreateResult RecoveryCreateSnapshotDevices();
+
     // Dump debug information.
     bool Dump(std::ostream& os);
 
@@ -279,6 +276,7 @@
     FRIEND_TEST(SnapshotTest, UpdateBootControlHal);
     FRIEND_TEST(SnapshotUpdateTest, DataWipeAfterRollback);
     FRIEND_TEST(SnapshotUpdateTest, DataWipeRollbackInRecovery);
+    FRIEND_TEST(SnapshotUpdateTest, FullUpdateFlow);
     FRIEND_TEST(SnapshotUpdateTest, MergeCannotRemoveCow);
     FRIEND_TEST(SnapshotUpdateTest, MergeInRecovery);
     FRIEND_TEST(SnapshotUpdateTest, SnapshotStatusFileWithoutCow);
@@ -342,7 +340,7 @@
 
     // |name| should be the base partition name (e.g. "system_a"). Create the
     // backing COW image using the size previously passed to CreateSnapshot().
-    bool CreateCowImage(LockedFile* lock, const std::string& name);
+    Return CreateCowImage(LockedFile* lock, const std::string& name);
 
     // Map a snapshot device that was previously created with CreateSnapshot.
     // If a merge was previously initiated, the device-mapper table will have a
@@ -380,7 +378,7 @@
     bool HandleCancelledUpdate(LockedFile* lock);
 
     // Helper for HandleCancelledUpdate. Assumes booting from new slot.
-    bool HandleCancelledUpdateOnNewSlot(LockedFile* lock);
+    bool AreAllSnapshotsCancelled(LockedFile* lock);
 
     // Remove artifacts created by the update process, such as snapshots, and
     // set the update state to None.
@@ -394,9 +392,15 @@
 
     // Interact with /metadata/ota/state.
     UpdateState ReadUpdateState(LockedFile* file);
+    SnapshotUpdateStatus ReadSnapshotUpdateStatus(LockedFile* file);
     bool WriteUpdateState(LockedFile* file, UpdateState state);
+    bool WriteSnapshotUpdateStatus(LockedFile* file, const SnapshotUpdateStatus& status);
     std::string GetStateFilePath() const;
 
+    // Interact with /metadata/ota/merge_state.
+    // This file contains information related to the snapshot merge process.
+    std::string GetMergeStateFilePath() const;
+
     // Helpers for merging.
     bool SwitchSnapshotToMerge(LockedFile* lock, const std::string& name);
     bool RewriteSnapshotDeviceTable(const std::string& dm_name);
@@ -443,7 +447,7 @@
     std::string GetSnapshotStatusFilePath(const std::string& name);
 
     std::string GetSnapshotBootIndicatorPath();
-    void RemoveSnapshotBootIndicator();
+    std::string GetRollbackIndicatorPath();
 
     // Return the name of the device holding the "snapshot" or "snapshot-merge"
     // target. This may not be the final device presented via MapSnapshot(), if
@@ -482,14 +486,14 @@
 
     // Helper for CreateUpdateSnapshots.
     // Creates all underlying images, COW partitions and snapshot files. Does not initialize them.
-    bool CreateUpdateSnapshotsInternal(LockedFile* lock, const DeltaArchiveManifest& manifest,
-                                       PartitionCowCreator* cow_creator,
-                                       AutoDeviceList* created_devices,
-                                       std::map<std::string, SnapshotStatus>* all_snapshot_status);
+    Return CreateUpdateSnapshotsInternal(
+            LockedFile* lock, const DeltaArchiveManifest& manifest,
+            PartitionCowCreator* cow_creator, AutoDeviceList* created_devices,
+            std::map<std::string, SnapshotStatus>* all_snapshot_status);
 
     // Initialize snapshots so that they can be mapped later.
     // Map the COW partition and zero-initialize the header.
-    bool InitializeUpdateSnapshots(
+    Return InitializeUpdateSnapshots(
             LockedFile* lock, MetadataBuilder* target_metadata,
             const LpMetadata* exported_target_metadata, const std::string& target_suffix,
             const std::map<std::string, SnapshotStatus>& all_snapshot_status);
@@ -507,6 +511,8 @@
     friend std::ostream& operator<<(std::ostream& os, SnapshotManager::Slot slot);
     Slot GetCurrentSlot();
 
+    std::string ReadUpdateSourceSlotSuffix();
+
     std::string gsid_dir_;
     std::string metadata_dir_;
     std::unique_ptr<IDeviceInfo> device_;
diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index 2bf1b57..98bf56a 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -14,10 +14,12 @@
 
 #pragma once
 
+#include <memory>
 #include <optional>
 #include <string>
 #include <unordered_set>
 
+#include <android-base/file.h>
 #include <android/hardware/boot/1.1/IBootControl.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -40,7 +42,6 @@
 using testing::_;
 using testing::AssertionResult;
 using testing::NiceMock;
-using testing::Return;
 
 using namespace android::storage_literals;
 using namespace std::string_literals;
@@ -115,6 +116,7 @@
 class SnapshotTestPropertyFetcher : public android::fs_mgr::testing::MockPropertyFetcher {
   public:
     SnapshotTestPropertyFetcher(const std::string& slot_suffix) {
+        using testing::Return;
         ON_CALL(*this, GetProperty("ro.boot.slot_suffix", _)).WillByDefault(Return(slot_suffix));
         ON_CALL(*this, GetBoolProperty("ro.boot.dynamic_partitions", _))
                 .WillByDefault(Return(true));
@@ -155,5 +157,28 @@
 // Get partition size from update package metadata.
 uint64_t GetSize(PartitionUpdate* partition_update);
 
+// Util class for test cases on low space scenario. These tests assumes image manager
+// uses /data as backup device.
+class LowSpaceUserdata {
+  public:
+    // Set the maximum free space allowed for this test. If /userdata has more space than the given
+    // number, a file is allocated to consume space.
+    AssertionResult Init(uint64_t max_free_space);
+
+    uint64_t free_space() const;
+    uint64_t available_space() const;
+    uint64_t bsize() const;
+
+  private:
+    AssertionResult ReadUserdataStats();
+
+    static constexpr const char* kUserDataDevice = "/data";
+    std::unique_ptr<TemporaryFile> big_file_;
+    bool initialized_ = false;
+    uint64_t free_space_ = 0;
+    uint64_t available_space_ = 0;
+    uint64_t bsize_ = 0;
+};
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/return.cpp b/fs_mgr/libsnapshot/return.cpp
new file mode 100644
index 0000000..6559c12
--- /dev/null
+++ b/fs_mgr/libsnapshot/return.cpp
@@ -0,0 +1,46 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <libsnapshot/return.h>
+
+#include <string.h>
+
+using android::fiemap::FiemapStatus;
+
+namespace android::snapshot {
+
+std::string Return::string() const {
+    switch (error_code()) {
+        case ErrorCode::ERROR:
+            return "Error";
+        case ErrorCode::NEEDS_REBOOT:
+            return "Retry after reboot";
+        case ErrorCode::SUCCESS:
+            [[fallthrough]];
+        case ErrorCode::NO_SPACE:
+            return strerror(-static_cast<int>(error_code()));
+    }
+}
+
+Return::ErrorCode Return::FromFiemapStatusErrorCode(FiemapStatus::ErrorCode error_code) {
+    switch (error_code) {
+        case FiemapStatus::ErrorCode::SUCCESS:
+        case FiemapStatus::ErrorCode::ERROR:
+        case FiemapStatus::ErrorCode::NO_SPACE:
+            return static_cast<ErrorCode>(error_code);
+        default:
+            return ErrorCode::ERROR;
+    }
+}
+}  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index a0ec068..2fe06fb 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -21,6 +21,7 @@
 #include <sys/unistd.h>
 
 #include <optional>
+#include <sstream>
 #include <thread>
 #include <unordered_set>
 
@@ -37,10 +38,15 @@
 #include <libfiemap/image_manager.h>
 #include <liblp/liblp.h>
 
+#ifdef LIBSNAPSHOT_USE_CALLSTACK
+#include <utils/CallStack.h>
+#endif
+
 #include <android/snapshot/snapshot.pb.h>
 #include "device_info.h"
 #include "partition_cow_creator.h"
 #include "snapshot_metadata_updater.h"
+#include "snapshot_stats.h"
 #include "utility.h"
 
 namespace android {
@@ -54,6 +60,7 @@
 using android::dm::DmTargetSnapshot;
 using android::dm::kSectorSize;
 using android::dm::SnapshotStorageMode;
+using android::fiemap::FiemapStatus;
 using android::fiemap::IImageManager;
 using android::fs_mgr::CreateDmTable;
 using android::fs_mgr::CreateLogicalPartition;
@@ -182,11 +189,19 @@
     return true;
 }
 
-SnapshotManager::Slot SnapshotManager::GetCurrentSlot() {
+std::string SnapshotManager::ReadUpdateSourceSlotSuffix() {
     auto boot_file = GetSnapshotBootIndicatorPath();
     std::string contents;
     if (!android::base::ReadFileToString(boot_file, &contents)) {
         PLOG(WARNING) << "Cannot read " << boot_file;
+        return {};
+    }
+    return contents;
+}
+
+SnapshotManager::Slot SnapshotManager::GetCurrentSlot() {
+    auto contents = ReadUpdateSourceSlotSuffix();
+    if (contents.empty()) {
         return Slot::Unknown;
     }
     if (device_->GetSlotSuffix() == contents) {
@@ -195,13 +210,49 @@
     return Slot::Target;
 }
 
+static bool RemoveFileIfExists(const std::string& path) {
+    std::string message;
+    if (!android::base::RemoveFileIfExists(path, &message)) {
+        LOG(ERROR) << "Remove failed: " << path << ": " << message;
+        return false;
+    }
+    return true;
+}
+
 bool SnapshotManager::RemoveAllUpdateState(LockedFile* lock) {
+    LOG(INFO) << "Removing all update state.";
+
+#ifdef LIBSNAPSHOT_USE_CALLSTACK
+    LOG(WARNING) << "Logging stack; see b/148818798.";
+    // Do not use CallStack's log functions because snapshotctl relies on
+    // android-base/logging to save log to files.
+    // TODO(b/148818798): remove this before we ship.
+    CallStack callstack;
+    callstack.update();
+    auto callstack_str = callstack.toString();
+    LOG(WARNING) << callstack_str.c_str();
+    std::stringstream path;
+    path << "/data/misc/snapshotctl_log/libsnapshot." << Now() << ".log";
+    std::string path_str = path.str();
+    android::base::WriteStringToFile(callstack_str.c_str(), path_str);
+    if (chmod(path_str.c_str(), 0644) == -1) {
+        PLOG(WARNING) << "Unable to chmod 0644 "
+                      << ", file maybe dropped from bugreport:" << path_str;
+    }
+#endif
+
     if (!RemoveAllSnapshots(lock)) {
         LOG(ERROR) << "Could not remove all snapshots";
         return false;
     }
 
-    RemoveSnapshotBootIndicator();
+    // It's okay if these fail - first-stage init performs a deeper check after
+    // reading the indicator file, so it's not a problem if it still exists
+    // after the update completes.
+    std::vector<std::string> files = {GetSnapshotBootIndicatorPath(), GetRollbackIndicatorPath()};
+    for (const auto& file : files) {
+        RemoveFileIfExists(file);
+    }
 
     // If this fails, we'll keep trying to remove the update state (as the
     // device reboots or starts a new update) until it finally succeeds.
@@ -228,6 +279,13 @@
         return false;
     }
 
+    // This file is written on boot to detect whether a rollback occurred. It
+    // MUST NOT exist before rebooting, otherwise, we're at risk of deleting
+    // snapshots too early.
+    if (!RemoveFileIfExists(GetRollbackIndicatorPath())) {
+        return false;
+    }
+
     // This file acts as both a quick indicator for init (it can use access(2)
     // to decide how to do first-stage mounts), and it stores the old slot, so
     // we can tell whether or not we performed a rollback.
@@ -289,14 +347,14 @@
     return true;
 }
 
-bool SnapshotManager::CreateCowImage(LockedFile* lock, const std::string& name) {
+Return SnapshotManager::CreateCowImage(LockedFile* lock, const std::string& name) {
     CHECK(lock);
     CHECK(lock->lock_mode() == LOCK_EX);
-    if (!EnsureImageManager()) return false;
+    if (!EnsureImageManager()) return Return::Error();
 
     SnapshotStatus status;
     if (!ReadSnapshotStatus(lock, name, &status)) {
-        return false;
+        return Return::Error();
     }
 
     // The COW file size should have been rounded up to the nearest sector in CreateSnapshot.
@@ -304,12 +362,12 @@
     if (status.cow_file_size() % kSectorSize != 0) {
         LOG(ERROR) << "Snapshot " << name << " COW file size is not a multiple of the sector size: "
                    << status.cow_file_size();
-        return false;
+        return Return::Error();
     }
 
     std::string cow_image_name = GetCowImageDeviceName(name);
     int cow_flags = IImageManager::CREATE_IMAGE_DEFAULT;
-    return images_->CreateBackingImage(cow_image_name, status.cow_file_size(), cow_flags);
+    return Return(images_->CreateBackingImage(cow_image_name, status.cow_file_size(), cow_flags));
 }
 
 bool SnapshotManager::MapSnapshot(LockedFile* lock, const std::string& name,
@@ -559,9 +617,26 @@
         }
     }
 
+    DmTargetSnapshot::Status initial_target_values = {};
+    for (const auto& snapshot : snapshots) {
+        DmTargetSnapshot::Status current_status;
+        if (!QuerySnapshotStatus(snapshot, nullptr, &current_status)) {
+            return false;
+        }
+        initial_target_values.sectors_allocated += current_status.sectors_allocated;
+        initial_target_values.total_sectors += current_status.total_sectors;
+        initial_target_values.metadata_sectors += current_status.metadata_sectors;
+    }
+
+    SnapshotUpdateStatus initial_status;
+    initial_status.set_state(UpdateState::Merging);
+    initial_status.set_sectors_allocated(initial_target_values.sectors_allocated);
+    initial_status.set_total_sectors(initial_target_values.total_sectors);
+    initial_status.set_metadata_sectors(initial_target_values.metadata_sectors);
+
     // Point of no return - mark that we're starting a merge. From now on every
     // snapshot must be a merge target.
-    if (!WriteUpdateState(lock.get(), UpdateState::Merging)) {
+    if (!WriteSnapshotUpdateStatus(lock.get(), initial_status)) {
         return false;
     }
 
@@ -810,7 +885,8 @@
                 cancelled = true;
                 break;
             default:
-                LOG(ERROR) << "Unknown merge status: " << static_cast<uint32_t>(snapshot_state);
+                LOG(ERROR) << "Unknown merge status for \"" << snapshot << "\": "
+                           << "\"" << snapshot_state << "\"";
                 failed = true;
                 break;
         }
@@ -926,14 +1002,8 @@
     return metadata_dir_ + "/" + android::base::Basename(kBootIndicatorPath);
 }
 
-void SnapshotManager::RemoveSnapshotBootIndicator() {
-    // It's okay if this fails - first-stage init performs a deeper check after
-    // reading the indicator file, so it's not a problem if it still exists
-    // after the update completes.
-    auto boot_file = GetSnapshotBootIndicatorPath();
-    if (unlink(boot_file.c_str()) == -1 && errno != ENOENT) {
-        PLOG(ERROR) << "unlink " << boot_file;
-    }
+std::string SnapshotManager::GetRollbackIndicatorPath() {
+    return metadata_dir_ + "/rollback-indicator";
 }
 
 void SnapshotManager::AcknowledgeMergeSuccess(LockedFile* lock) {
@@ -1104,25 +1174,18 @@
     if (slot == Slot::Unknown) {
         return false;
     }
-    if (slot == Slot::Target) {
-        // We're booted into the target slot, which means we just rebooted
-        // after applying the update.
-        if (!HandleCancelledUpdateOnNewSlot(lock)) {
-            return false;
-        }
+
+    // If all snapshots were reflashed, then cancel the entire update.
+    if (AreAllSnapshotsCancelled(lock)) {
+        RemoveAllUpdateState(lock);
+        return true;
     }
 
-    // The only way we can get here is if:
-    //  (1) The device rolled back to the previous slot.
-    //  (2) This function was called prematurely before rebooting the device.
-    //  (3) fastboot set_active was used.
-    //  (4) The device updates to the new slot but re-flashed *all* partitions
-    //      in the new slot.
-    //
-    // In any case, delete the snapshots. It may be worth using the boot_control
-    // HAL to differentiate case (2).
-    RemoveAllUpdateState(lock);
-    return true;
+    // This unverified update might be rolled back, or it might not (b/147347110
+    // comment #77). Take no action, as update_engine is responsible for deciding
+    // whether to cancel.
+    LOG(ERROR) << "Update state is being processed before reboot, taking no action.";
+    return false;
 }
 
 std::unique_ptr<LpMetadata> SnapshotManager::ReadCurrentMetadata() {
@@ -1147,7 +1210,7 @@
     return MetadataPartitionState::Flashed;
 }
 
-bool SnapshotManager::HandleCancelledUpdateOnNewSlot(LockedFile* lock) {
+bool SnapshotManager::AreAllSnapshotsCancelled(LockedFile* lock) {
     std::vector<std::string> snapshots;
     if (!ListSnapshots(lock, &snapshots)) {
         LOG(WARNING) << "Failed to list snapshots to determine whether device has been flashed "
@@ -1156,35 +1219,45 @@
         return true;
     }
 
+    auto source_slot_suffix = ReadUpdateSourceSlotSuffix();
+    if (source_slot_suffix.empty()) {
+        return false;
+    }
+    uint32_t source_slot = SlotNumberForSlotSuffix(source_slot_suffix);
+    uint32_t target_slot = (source_slot == 0) ? 1 : 0;
+
     // Attempt to detect re-flashing on each partition.
     // - If all partitions are re-flashed, we can proceed to cancel the whole update.
     // - If only some of the partitions are re-flashed, snapshots for re-flashed partitions are
     //   deleted. Caller is responsible for merging the rest of the snapshots.
     // - If none of the partitions are re-flashed, caller is responsible for merging the snapshots.
-    auto metadata = ReadCurrentMetadata();
-    if (!metadata) return false;
-    bool all_snapshot_cancelled = true;
+    //
+    // Note that we use target slot metadata, since if an OTA has been applied
+    // to the target slot, we can detect the UPDATED flag. Any kind of flash
+    // operation against dynamic partitions ensures that all copies of the
+    // metadata are in sync, so flashing all partitions on the source slot will
+    // remove the UPDATED flag on the target slot as well.
+    const auto& opener = device_->GetPartitionOpener();
+    auto super_device = device_->GetSuperDevice(target_slot);
+    auto metadata = android::fs_mgr::ReadMetadata(opener, super_device, target_slot);
+    if (!metadata) {
+        return false;
+    }
+
+    bool all_snapshots_cancelled = true;
     for (const auto& snapshot_name : snapshots) {
         if (GetMetadataPartitionState(*metadata, snapshot_name) ==
             MetadataPartitionState::Updated) {
-            LOG(WARNING) << "Cannot cancel update because snapshot" << snapshot_name
-                         << " is in use.";
-            all_snapshot_cancelled = false;
+            all_snapshots_cancelled = false;
             continue;
         }
         // Delete snapshots for partitions that are re-flashed after the update.
-        LOG(INFO) << "Detected re-flashing of partition " << snapshot_name << ".";
-        if (!DeleteSnapshot(lock, snapshot_name)) {
-            // This is an error, but it is okay to leave the snapshot in the short term.
-            // However, if all_snapshot_cancelled == false after exiting the loop, caller may
-            // initiate merge for this unused snapshot, which is likely to fail.
-            LOG(WARNING) << "Failed to delete snapshot for re-flashed partition " << snapshot_name;
-        }
+        LOG(WARNING) << "Detected re-flashing of partition " << snapshot_name << ".";
     }
-    if (!all_snapshot_cancelled) return false;
-
-    LOG(INFO) << "All partitions are re-flashed after update, removing all update states.";
-    return true;
+    if (all_snapshots_cancelled) {
+        LOG(WARNING) << "All partitions are re-flashed after update, removing all update states.";
+    }
+    return all_snapshots_cancelled;
 }
 
 bool SnapshotManager::RemoveAllSnapshots(LockedFile* lock) {
@@ -1200,7 +1273,8 @@
         if (!UnmapPartitionWithSnapshot(lock, name) || !DeleteSnapshot(lock, name)) {
             // Remember whether or not we were able to unmap the cow image.
             auto cow_image_device = GetCowImageDeviceName(name);
-            has_mapped_cow_images |= images_->IsImageMapped(cow_image_device);
+            has_mapped_cow_images |=
+                    (EnsureImageManager() && images_->IsImageMapped(cow_image_device));
 
             ok = false;
         }
@@ -1231,15 +1305,45 @@
         return UpdateState::None;
     }
 
-    auto state = ReadUpdateState(lock.get());
-    if (progress) {
-        *progress = 0.0;
-        if (state == UpdateState::Merging) {
-            // :TODO: When merging is implemented, set progress_val.
-        } else if (state == UpdateState::MergeCompleted) {
-            *progress = 100.0;
-        }
+    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock.get());
+    auto state = update_status.state();
+    if (progress == nullptr) {
+        return state;
     }
+
+    if (state == UpdateState::MergeCompleted) {
+        *progress = 100.0;
+        return state;
+    }
+
+    *progress = 0.0;
+    if (state != UpdateState::Merging) {
+        return state;
+    }
+
+    // Sum all the snapshot states as if the system consists of a single huge
+    // snapshots device, then compute the merge completion percentage of that
+    // device.
+    std::vector<std::string> snapshots;
+    if (!ListSnapshots(lock.get(), &snapshots)) {
+        LOG(ERROR) << "Could not list snapshots";
+        return state;
+    }
+
+    DmTargetSnapshot::Status fake_snapshots_status = {};
+    for (const auto& snapshot : snapshots) {
+        DmTargetSnapshot::Status current_status;
+
+        if (!QuerySnapshotStatus(snapshot, nullptr, &current_status)) continue;
+
+        fake_snapshots_status.sectors_allocated += current_status.sectors_allocated;
+        fake_snapshots_status.total_sectors += current_status.total_sectors;
+        fake_snapshots_status.metadata_sectors += current_status.metadata_sectors;
+    }
+
+    *progress = DmTargetSnapshot::MergePercent(fake_snapshots_status,
+                                               update_status.sectors_allocated());
+
     return state;
 }
 
@@ -1273,7 +1377,16 @@
     // the reason be clearer? Because the indicator file still exists, and
     // if this was FATAL, reverting to the old slot would be broken.
     auto slot = GetCurrentSlot();
+
     if (slot != Slot::Target) {
+        if (slot == Slot::Source && !device_->IsRecovery()) {
+            // Device is rebooting into the original slot, so mark this as a
+            // rollback.
+            auto path = GetRollbackIndicatorPath();
+            if (!android::base::WriteStringToFile("1", path)) {
+                PLOG(ERROR) << "Unable to write rollback indicator: " << path;
+            }
+        }
         LOG(INFO) << "Not booting from new slot. Will not mount snapshots.";
         return false;
     }
@@ -1296,7 +1409,8 @@
     }
 }
 
-bool SnapshotManager::CreateLogicalAndSnapshotPartitions(const std::string& super_device) {
+bool SnapshotManager::CreateLogicalAndSnapshotPartitions(
+        const std::string& super_device, const std::chrono::milliseconds& timeout_ms) {
     LOG(INFO) << "Creating logical partitions with snapshots as needed";
 
     auto lock = LockExclusive();
@@ -1322,6 +1436,7 @@
                 .metadata = metadata.get(),
                 .partition = &partition,
                 .partition_opener = &opener,
+                .timeout_ms = timeout_ms,
         };
         std::string ignore_path;
         if (!MapPartitionWithSnapshot(lock.get(), std::move(params), &ignore_path)) {
@@ -1625,6 +1740,10 @@
     return metadata_dir_ + "/state"s;
 }
 
+std::string SnapshotManager::GetMergeStateFilePath() const {
+    return metadata_dir_ + "/merge_state"s;
+}
+
 std::string SnapshotManager::GetLockPath() const {
     return metadata_dir_;
 }
@@ -1642,15 +1761,7 @@
     return OpenLock(LOCK_EX);
 }
 
-UpdateState SnapshotManager::ReadUpdateState(LockedFile* lock) {
-    CHECK(lock);
-
-    std::string contents;
-    if (!android::base::ReadFileToString(GetStateFilePath(), &contents)) {
-        PLOG(ERROR) << "Read state file failed";
-        return UpdateState::None;
-    }
-
+static UpdateState UpdateStateFromString(const std::string& contents) {
     if (contents.empty() || contents == "none") {
         return UpdateState::None;
     } else if (contents == "initiated") {
@@ -1665,8 +1776,10 @@
         return UpdateState::MergeNeedsReboot;
     } else if (contents == "merge-failed") {
         return UpdateState::MergeFailed;
+    } else if (contents == "cancelled") {
+        return UpdateState::Cancelled;
     } else {
-        LOG(ERROR) << "Unknown merge state in update state file";
+        LOG(ERROR) << "Unknown merge state in update state file: \"" << contents << "\"";
         return UpdateState::None;
     }
 }
@@ -1687,24 +1800,62 @@
             return os << "merge-needs-reboot";
         case UpdateState::MergeFailed:
             return os << "merge-failed";
+        case UpdateState::Cancelled:
+            return os << "cancelled";
         default:
-            LOG(ERROR) << "Unknown update state";
+            LOG(ERROR) << "Unknown update state: " << static_cast<uint32_t>(state);
             return os;
     }
 }
 
+UpdateState SnapshotManager::ReadUpdateState(LockedFile* lock) {
+    SnapshotUpdateStatus status = ReadSnapshotUpdateStatus(lock);
+    return status.state();
+}
+
+SnapshotUpdateStatus SnapshotManager::ReadSnapshotUpdateStatus(LockedFile* lock) {
+    CHECK(lock);
+
+    SnapshotUpdateStatus status = {};
+    std::string contents;
+    if (!android::base::ReadFileToString(GetStateFilePath(), &contents)) {
+        PLOG(ERROR) << "Read state file failed";
+        status.set_state(UpdateState::None);
+        return status;
+    }
+
+    if (!status.ParseFromString(contents)) {
+        LOG(WARNING) << "Unable to parse state file as SnapshotUpdateStatus, using the old format";
+
+        // Try to rollback to legacy file to support devices that are
+        // currently using the old file format.
+        // TODO(b/147409432)
+        status.set_state(UpdateStateFromString(contents));
+    }
+
+    return status;
+}
+
 bool SnapshotManager::WriteUpdateState(LockedFile* lock, UpdateState state) {
+    SnapshotUpdateStatus status = {};
+    status.set_state(state);
+    return WriteSnapshotUpdateStatus(lock, status);
+}
+
+bool SnapshotManager::WriteSnapshotUpdateStatus(LockedFile* lock,
+                                                const SnapshotUpdateStatus& status) {
     CHECK(lock);
     CHECK(lock->lock_mode() == LOCK_EX);
 
-    std::stringstream ss;
-    ss << state;
-    std::string contents = ss.str();
-    if (contents.empty()) return false;
+    std::string contents;
+    if (!status.SerializeToString(&contents)) {
+        LOG(ERROR) << "Unable to serialize SnapshotUpdateStatus.";
+        return false;
+    }
 
 #ifdef LIBSNAPSHOT_USE_HAL
     auto merge_status = MergeStatus::UNKNOWN;
-    switch (state) {
+    switch (status.state()) {
         // The needs-reboot and completed cases imply that /data and /metadata
         // can be safely wiped, so we don't report a merge status.
         case UpdateState::None:
@@ -1723,7 +1874,7 @@
         default:
             // Note that Cancelled flows to here - it is never written, since
             // it only communicates a transient state to the caller.
-            LOG(ERROR) << "Unexpected update status: " << state;
+            LOG(ERROR) << "Unexpected update status: " << status.state();
             break;
     }
 
@@ -1844,9 +1995,21 @@
     }
 }
 
-bool SnapshotManager::CreateUpdateSnapshots(const DeltaArchiveManifest& manifest) {
+static Return AddRequiredSpace(Return orig,
+                               const std::map<std::string, SnapshotStatus>& all_snapshot_status) {
+    if (orig.error_code() != Return::ErrorCode::NO_SPACE) {
+        return orig;
+    }
+    uint64_t sum = 0;
+    for (auto&& [name, status] : all_snapshot_status) {
+        sum += status.cow_file_size();
+    }
+    return Return::NoSpace(sum);
+}
+
+Return SnapshotManager::CreateUpdateSnapshots(const DeltaArchiveManifest& manifest) {
     auto lock = LockExclusive();
-    if (!lock) return false;
+    if (!lock) return Return::Error();
 
     // TODO(b/134949511): remove this check. Right now, with overlayfs mounted, the scratch
     // partition takes up a big chunk of space in super, causing COW images to be created on
@@ -1854,7 +2017,7 @@
     if (device_->IsOverlayfsSetup()) {
         LOG(ERROR) << "Cannot create update snapshots with overlayfs setup. Run `adb enable-verity`"
                    << ", reboot, then try again.";
-        return false;
+        return Return::Error();
     }
 
     const auto& opener = device_->GetPartitionOpener();
@@ -1865,8 +2028,17 @@
     auto current_super = device_->GetSuperDevice(current_slot);
 
     auto current_metadata = MetadataBuilder::New(opener, current_super, current_slot);
+    if (current_metadata == nullptr) {
+        LOG(ERROR) << "Cannot create metadata builder.";
+        return Return::Error();
+    }
+
     auto target_metadata =
             MetadataBuilder::NewForUpdate(opener, current_super, current_slot, target_slot);
+    if (target_metadata == nullptr) {
+        LOG(ERROR) << "Cannot create target metadata builder.";
+        return Return::Error();
+    }
 
     // Delete partitions with target suffix in |current_metadata|. Otherwise,
     // partition_cow_creator recognizes these left-over partitions as used space.
@@ -1879,7 +2051,7 @@
     SnapshotMetadataUpdater metadata_updater(target_metadata.get(), target_slot, manifest);
     if (!metadata_updater.Update()) {
         LOG(ERROR) << "Cannot calculate new metadata.";
-        return false;
+        return Return::Error();
     }
 
     // Delete previous COW partitions in current_metadata so that PartitionCowCreator marks those as
@@ -1911,36 +2083,34 @@
             .extra_extents = {},
     };
 
-    if (!CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices,
-                                       &all_snapshot_status)) {
-        return false;
-    }
+    auto ret = CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices,
+                                             &all_snapshot_status);
+    if (!ret.is_ok()) return ret;
 
     auto exported_target_metadata = target_metadata->Export();
     if (exported_target_metadata == nullptr) {
         LOG(ERROR) << "Cannot export target metadata";
-        return false;
+        return Return::Error();
     }
 
-    if (!InitializeUpdateSnapshots(lock.get(), target_metadata.get(),
-                                   exported_target_metadata.get(), target_suffix,
-                                   all_snapshot_status)) {
-        return false;
-    }
+    ret = InitializeUpdateSnapshots(lock.get(), target_metadata.get(),
+                                    exported_target_metadata.get(), target_suffix,
+                                    all_snapshot_status);
+    if (!ret.is_ok()) return ret;
 
     if (!UpdatePartitionTable(opener, device_->GetSuperDevice(target_slot),
                               *exported_target_metadata, target_slot)) {
         LOG(ERROR) << "Cannot write target metadata";
-        return false;
+        return Return::Error();
     }
 
     created_devices.Release();
     LOG(INFO) << "Successfully created all snapshots for target slot " << target_suffix;
 
-    return true;
+    return Return::Ok();
 }
 
-bool SnapshotManager::CreateUpdateSnapshotsInternal(
+Return SnapshotManager::CreateUpdateSnapshotsInternal(
         LockedFile* lock, const DeltaArchiveManifest& manifest, PartitionCowCreator* cow_creator,
         AutoDeviceList* created_devices,
         std::map<std::string, SnapshotStatus>* all_snapshot_status) {
@@ -1951,7 +2121,7 @@
 
     if (!target_metadata->AddGroup(kCowGroupName, 0)) {
         LOG(ERROR) << "Cannot add group " << kCowGroupName;
-        return false;
+        return Return::Error();
     }
 
     std::map<std::string, const RepeatedPtrField<InstallOperation>*> install_operation_map;
@@ -1963,7 +2133,7 @@
         if (!inserted) {
             LOG(ERROR) << "Duplicated partition " << partition_update.partition_name()
                        << " in update manifest.";
-            return false;
+            return Return::Error();
         }
 
         auto& extra_extents = extra_extents_map[suffixed_name];
@@ -1992,7 +2162,7 @@
         // Compute the device sizes for the partition.
         auto cow_creator_ret = cow_creator->Run();
         if (!cow_creator_ret.has_value()) {
-            return false;
+            return Return::Error();
         }
 
         LOG(INFO) << "For partition " << target_partition->name()
@@ -2006,7 +2176,7 @@
         if (!DeleteSnapshot(lock, target_partition->name())) {
             LOG(ERROR) << "Cannot delete existing snapshot before creating a new one for partition "
                        << target_partition->name();
-            return false;
+            return Return::Error();
         }
 
         // It is possible that the whole partition uses free space in super, and snapshot / COW
@@ -2024,7 +2194,7 @@
 
         // Store these device sizes to snapshot status file.
         if (!CreateSnapshot(lock, &cow_creator_ret->snapshot_status)) {
-            return false;
+            return Return::Error();
         }
         created_devices->EmplaceBack<AutoDeleteSnapshot>(this, lock, target_partition->name());
 
@@ -2038,7 +2208,7 @@
             auto cow_partition = target_metadata->AddPartition(GetCowName(target_partition->name()),
                                                                kCowGroupName, 0 /* flags */);
             if (cow_partition == nullptr) {
-                return false;
+                return Return::Error();
             }
 
             if (!target_metadata->ResizePartition(
@@ -2046,28 +2216,34 @@
                         cow_creator_ret->cow_partition_usable_regions)) {
                 LOG(ERROR) << "Cannot create COW partition on metadata with size "
                            << cow_creator_ret->snapshot_status.cow_partition_size();
-                return false;
+                return Return::Error();
             }
             // Only the in-memory target_metadata is modified; nothing to clean up if there is an
             // error in the future.
         }
 
-        // Create the backing COW image if necessary.
-        if (cow_creator_ret->snapshot_status.cow_file_size() > 0) {
-            if (!CreateCowImage(lock, target_partition->name())) {
-                return false;
-            }
-        }
-
         all_snapshot_status->emplace(target_partition->name(),
                                      std::move(cow_creator_ret->snapshot_status));
 
-        LOG(INFO) << "Successfully created snapshot for " << target_partition->name();
+        LOG(INFO) << "Successfully created snapshot partition for " << target_partition->name();
     }
-    return true;
+
+    LOG(INFO) << "Allocating CoW images.";
+
+    for (auto&& [name, snapshot_status] : *all_snapshot_status) {
+        // Create the backing COW image if necessary.
+        if (snapshot_status.cow_file_size() > 0) {
+            auto ret = CreateCowImage(lock, name);
+            if (!ret.is_ok()) return AddRequiredSpace(ret, *all_snapshot_status);
+        }
+
+        LOG(INFO) << "Successfully created snapshot for " << name;
+    }
+
+    return Return::Ok();
 }
 
-bool SnapshotManager::InitializeUpdateSnapshots(
+Return SnapshotManager::InitializeUpdateSnapshots(
         LockedFile* lock, MetadataBuilder* target_metadata,
         const LpMetadata* exported_target_metadata, const std::string& target_suffix,
         const std::map<std::string, SnapshotStatus>& all_snapshot_status) {
@@ -2086,7 +2262,7 @@
         if (!UnmapPartitionWithSnapshot(lock, target_partition->name())) {
             LOG(ERROR) << "Cannot unmap existing COW devices before re-mapping them for zero-fill: "
                        << target_partition->name();
-            return false;
+            return Return::Error();
         }
 
         auto it = all_snapshot_status.find(target_partition->name());
@@ -2094,23 +2270,24 @@
         cow_params.partition_name = target_partition->name();
         std::string cow_name;
         if (!MapCowDevices(lock, cow_params, it->second, &created_devices_for_cow, &cow_name)) {
-            return false;
+            return Return::Error();
         }
 
         std::string cow_path;
         if (!dm.GetDmDevicePathByName(cow_name, &cow_path)) {
             LOG(ERROR) << "Cannot determine path for " << cow_name;
-            return false;
+            return Return::Error();
         }
 
-        if (!InitializeCow(cow_path)) {
+        auto ret = InitializeCow(cow_path);
+        if (!ret.is_ok()) {
             LOG(ERROR) << "Can't zero-fill COW device for " << target_partition->name() << ": "
                        << cow_path;
-            return false;
+            return AddRequiredSpace(ret, all_snapshot_status);
         }
         // Let destructor of created_devices_for_cow to unmap the COW devices.
     };
-    return true;
+    return Return::Ok();
 }
 
 bool SnapshotManager::MapUpdateSnapshot(const CreateLogicalPartitionParams& params,
@@ -2211,7 +2388,7 @@
     return AutoUnmountDevice::New(device_->GetMetadataDir());
 }
 
-UpdateState SnapshotManager::InitiateMergeAndWait() {
+UpdateState SnapshotManager::InitiateMergeAndWait(SnapshotMergeReport* stats_report) {
     {
         auto lock = LockExclusive();
         // Sync update state from file with bootloader.
@@ -2221,28 +2398,56 @@
         }
     }
 
+    SnapshotMergeStats merge_stats(*this);
+
+    unsigned int last_progress = 0;
+    auto callback = [&]() -> void {
+        double progress;
+        GetUpdateState(&progress);
+        if (last_progress < static_cast<unsigned int>(progress)) {
+            last_progress = progress;
+            LOG(INFO) << "Waiting for merge to complete: " << last_progress << "%.";
+        }
+    };
+
     LOG(INFO) << "Waiting for any previous merge request to complete. "
               << "This can take up to several minutes.";
-    auto state = ProcessUpdateState();
+    merge_stats.Resume();
+    auto state = ProcessUpdateState(callback);
+    merge_stats.set_state(state);
     if (state == UpdateState::None) {
         LOG(INFO) << "Can't find any snapshot to merge.";
         return state;
     }
     if (state == UpdateState::Unverified) {
+        if (GetCurrentSlot() != Slot::Target) {
+            LOG(INFO) << "Cannot merge until device reboots.";
+            return state;
+        }
+
+        // This is the first snapshot merge that is requested after OTA. We can
+        // initialize the merge duration statistics.
+        merge_stats.Start();
+
         if (!InitiateMerge()) {
             LOG(ERROR) << "Failed to initiate merge.";
             return state;
         }
         // All other states can be handled by ProcessUpdateState.
         LOG(INFO) << "Waiting for merge to complete. This can take up to several minutes.";
-        state = ProcessUpdateState();
+        last_progress = 0;
+        state = ProcessUpdateState(callback);
+        merge_stats.set_state(state);
     }
 
     LOG(INFO) << "Merge finished with state \"" << state << "\".";
+    if (stats_report) {
+        *stats_report = merge_stats.GetReport();
+    }
     return state;
 }
 
-bool SnapshotManager::WaitForMerge() {
+Return SnapshotManager::WaitForMerge() {
     LOG(INFO) << "Waiting for any previous merge request to complete. "
               << "This can take up to several minutes.";
     while (true) {
@@ -2253,7 +2458,18 @@
             continue;
         }
         LOG(INFO) << "Wait for merge exits with state " << state;
-        return state == UpdateState::None || state == UpdateState::MergeCompleted;
+        switch (state) {
+            case UpdateState::None:
+                [[fallthrough]];
+            case UpdateState::MergeCompleted:
+                [[fallthrough]];
+            case UpdateState::Cancelled:
+                return Return::Ok();
+            case UpdateState::MergeNeedsReboot:
+                return Return::NeedsReboot();
+            default:
+                return Return::Error();
+        }
     }
 }
 
@@ -2356,5 +2572,37 @@
     return true;
 }
 
+CreateResult SnapshotManager::RecoveryCreateSnapshotDevices() {
+    if (!device_->IsRecovery()) {
+        LOG(ERROR) << __func__ << " is only allowed in recovery.";
+        return CreateResult::NOT_CREATED;
+    }
+
+    auto mount = EnsureMetadataMounted();
+    if (!mount || !mount->HasDevice()) {
+        LOG(ERROR) << "Couldn't mount Metadata.";
+        return CreateResult::NOT_CREATED;
+    }
+
+    auto state_file = GetStateFilePath();
+    if (access(state_file.c_str(), F_OK) != 0 && errno == ENOENT) {
+        LOG(ERROR) << "Couldn't access state file.";
+        return CreateResult::NOT_CREATED;
+    }
+
+    if (!NeedSnapshotsInFirstStageMount()) {
+        return CreateResult::NOT_CREATED;
+    }
+
+    auto slot_suffix = device_->GetOtherSlotSuffix();
+    auto slot_number = SlotNumberForSlotSuffix(slot_suffix);
+    auto super_path = device_->GetSuperDevice(slot_number);
+    if (!CreateLogicalAndSnapshotPartitions(super_path)) {
+        LOG(ERROR) << "Unable to map partitions.";
+        return CreateResult::ERROR;
+    }
+    return CreateResult::CREATED;
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp b/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp
index 337be4f..5530e59 100644
--- a/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp
@@ -19,6 +19,7 @@
 #include <memory>
 #include <string>
 
+#include <android-base/properties.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <liblp/builder.h>
@@ -42,6 +43,10 @@
 
 class SnapshotMetadataUpdaterTest : public ::testing::TestWithParam<uint32_t> {
   public:
+    SnapshotMetadataUpdaterTest() {
+        is_virtual_ab_ = android::base::GetBoolProperty("ro.virtual_ab.enabled", false);
+    }
+
     void SetUp() override {
         target_slot_ = GetParam();
         target_suffix_ = SlotSuffixForSlotNumber(target_slot_);
@@ -122,6 +127,7 @@
                                   << ".";
     }
 
+    bool is_virtual_ab_;
     std::unique_ptr<MetadataBuilder> builder_;
     uint32_t target_slot_;
     std::string target_suffix_;
diff --git a/fs_mgr/libsnapshot/snapshot_stats.cpp b/fs_mgr/libsnapshot/snapshot_stats.cpp
new file mode 100644
index 0000000..635b47d
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_stats.cpp
@@ -0,0 +1,89 @@
+// 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 "snapshot_stats.h"
+
+#include <sstream>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include "utility.h"
+
+namespace android {
+namespace snapshot {
+
+SnapshotMergeStats::SnapshotMergeStats(SnapshotManager& parent) : parent_(parent) {
+    init_time_ = std::chrono::steady_clock::now();
+}
+
+SnapshotMergeStats::~SnapshotMergeStats() {
+    std::string error;
+    auto file_path = parent_.GetMergeStateFilePath();
+    if (!android::base::RemoveFileIfExists(file_path, &error)) {
+        LOG(ERROR) << "Failed to remove merge statistics file " << file_path << ": " << error;
+        return;
+    }
+}
+
+bool SnapshotMergeStats::ReadState() {
+    std::string contents;
+    if (!android::base::ReadFileToString(parent_.GetMergeStateFilePath(), &contents)) {
+        PLOG(INFO) << "Read merge statistics file failed";
+        return false;
+    }
+    if (!report_.ParseFromString(contents)) {
+        LOG(ERROR) << "Unable to parse merge statistics file as SnapshotMergeReport";
+        return false;
+    }
+    return true;
+}
+
+bool SnapshotMergeStats::WriteState() {
+    std::string contents;
+    if (!report_.SerializeToString(&contents)) {
+        LOG(ERROR) << "Unable to serialize SnapshotMergeStats.";
+        return false;
+    }
+    auto file_path = parent_.GetMergeStateFilePath();
+    if (!WriteStringToFileAtomic(contents, file_path)) {
+        PLOG(ERROR) << "Could not write to merge statistics file";
+        return false;
+    }
+    return true;
+}
+
+void SnapshotMergeStats::Start() {
+    report_.set_resume_count(0);
+    report_.set_state(UpdateState::None);
+    WriteState();
+}
+
+void SnapshotMergeStats::Resume() {
+    if (!ReadState()) {
+        return;
+    }
+    report_.set_resume_count(report_.resume_count() + 1);
+    WriteState();
+}
+
+void SnapshotMergeStats::set_state(android::snapshot::UpdateState state) {
+    report_.set_state(state);
+}
+
+SnapshotMergeReport SnapshotMergeStats::GetReport() {
+    return report_;
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_stats.h b/fs_mgr/libsnapshot/snapshot_stats.h
new file mode 100644
index 0000000..60109a4
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_stats.h
@@ -0,0 +1,45 @@
+// 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 <chrono>
+
+#include <android/snapshot/snapshot.pb.h>
+#include <libsnapshot/snapshot.h>
+
+namespace android {
+namespace snapshot {
+
+class SnapshotMergeStats {
+  public:
+    SnapshotMergeStats(SnapshotManager& parent);
+    ~SnapshotMergeStats();
+    void Start();
+    void Resume();
+    void set_state(android::snapshot::UpdateState state);
+    SnapshotMergeReport GetReport();
+
+  private:
+    bool ReadState();
+    bool WriteState();
+
+    const SnapshotManager& parent_;
+    SnapshotMergeReport report_;
+    std::chrono::time_point<std::chrono::steady_clock> init_time_;
+    std::chrono::time_point<std::chrono::steady_clock> end_time_;
+};
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 2da0103..5d2840f 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -47,6 +47,7 @@
 using android::base::unique_fd;
 using android::dm::DeviceMapper;
 using android::dm::DmDeviceState;
+using android::fiemap::FiemapStatus;
 using android::fiemap::IImageManager;
 using android::fs_mgr::BlockDeviceInfo;
 using android::fs_mgr::CreateLogicalPartitionParams;
@@ -77,7 +78,9 @@
 
 class SnapshotTest : public ::testing::Test {
   public:
-    SnapshotTest() : dm_(DeviceMapper::Instance()) {}
+    SnapshotTest() : dm_(DeviceMapper::Instance()) {
+        is_virtual_ab_ = android::base::GetBoolProperty("ro.virtual_ab.enabled", false);
+    }
 
     // This is exposed for main.
     void Cleanup() {
@@ -87,6 +90,8 @@
 
   protected:
     void SetUp() override {
+        if (!is_virtual_ab_) GTEST_SKIP() << "Test for Virtual A/B devices only";
+
         SnapshotTestPropertyFetcher::SetUp();
         InitializeState();
         CleanupTestArtifacts();
@@ -96,6 +101,8 @@
     }
 
     void TearDown() override {
+        if (!is_virtual_ab_) return;
+
         lock_ = nullptr;
 
         CleanupTestArtifacts();
@@ -328,6 +335,8 @@
         return AssertionSuccess();
     }
 
+    static constexpr std::chrono::milliseconds snapshot_timeout_ = 5s;
+    bool is_virtual_ab_;
     DeviceMapper& dm_;
     std::unique_ptr<SnapshotManager::LockedFile> lock_;
     android::fiemap::IImageManager* image_manager_ = nullptr;
@@ -438,6 +447,9 @@
     auto sm = SnapshotManager::NewForFirstStageMount(info);
     ASSERT_NE(sm, nullptr);
     ASSERT_FALSE(sm->NeedSnapshotsInFirstStageMount());
+
+    auto indicator = sm->GetRollbackIndicatorPath();
+    ASSERT_EQ(access(indicator.c_str(), R_OK), 0);
 }
 
 TEST_F(SnapshotTest, Merge) {
@@ -494,6 +506,9 @@
 }
 
 TEST_F(SnapshotTest, FirstStageMountAndMerge) {
+#ifdef SKIP_TEST_IN_PRESUBMIT
+    GTEST_SKIP() << "WIP failure b/148889015";
+#endif
     ASSERT_TRUE(AcquireLock());
 
     static const uint64_t kDeviceSize = 1024 * 1024;
@@ -503,7 +518,7 @@
     auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
     ASSERT_NE(init, nullptr);
     ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
 
     ASSERT_TRUE(AcquireLock());
 
@@ -532,7 +547,7 @@
     auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
     ASSERT_NE(init, nullptr);
     ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
 
     ASSERT_TRUE(AcquireLock());
 
@@ -550,6 +565,9 @@
 }
 
 TEST_F(SnapshotTest, FlashSuperDuringMerge) {
+#ifdef SKIP_TEST_IN_PRESUBMIT
+    GTEST_SKIP() << "WIP failure b/148889015";
+#endif
     ASSERT_TRUE(AcquireLock());
 
     static const uint64_t kDeviceSize = 1024 * 1024;
@@ -559,7 +577,7 @@
     auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
     ASSERT_NE(init, nullptr);
     ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
     ASSERT_TRUE(init->InitiateMerge());
 
     // Now, reflash super. Note that we haven't called ProcessUpdateState, so the
@@ -569,7 +587,7 @@
     FormatFakeSuper();
     ASSERT_TRUE(CreatePartition("test_partition_b", kDeviceSize));
     ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
 
     // Because the status is Merging, we must call ProcessUpdateState, which should
     // detect a cancelled update.
@@ -753,6 +771,8 @@
 class SnapshotUpdateTest : public SnapshotTest {
   public:
     void SetUp() override {
+        if (!is_virtual_ab_) GTEST_SKIP() << "Test for Virtual A/B devices only";
+
         SnapshotTest::SetUp();
         Cleanup();
 
@@ -783,6 +803,7 @@
 
         // Initialize source partition metadata using |manifest_|.
         src_ = MetadataBuilder::New(*opener_, "super", 0);
+        ASSERT_NE(src_, nullptr);
         ASSERT_TRUE(FillFakeMetadata(src_.get(), manifest_, "_a"));
         // Add sys_b which is like system_other.
         ASSERT_TRUE(src_->AddGroup("group_b", kGroupSize));
@@ -812,6 +833,8 @@
         }
     }
     void TearDown() override {
+        if (!is_virtual_ab_) return;
+
         Cleanup();
         SnapshotTest::TearDown();
     }
@@ -956,6 +979,9 @@
 // Also test UnmapUpdateSnapshot unmaps everything.
 // Also test first stage mount and merge after this.
 TEST_F(SnapshotUpdateTest, FullUpdateFlow) {
+#ifdef SKIP_TEST_IN_PRESUBMIT
+    GTEST_SKIP() << "WIP failure b/148889015";
+#endif
     // OTA client blindly unmaps all partitions that are possibly mapped.
     for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
         ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
@@ -975,6 +1001,7 @@
 
     // Test that partitions prioritize using space in super.
     auto tgt = MetadataBuilder::New(*opener_, "super", 1);
+    ASSERT_NE(tgt, nullptr);
     ASSERT_NE(nullptr, tgt->FindPartition("sys_b-cow"));
     ASSERT_NE(nullptr, tgt->FindPartition("vnd_b-cow"));
     ASSERT_EQ(nullptr, tgt->FindPartition("prd_b-cow"));
@@ -998,7 +1025,10 @@
     auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
     ASSERT_NE(init, nullptr);
     ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+
+    auto indicator = sm->GetRollbackIndicatorPath();
+    ASSERT_NE(access(indicator.c_str(), R_OK), 0);
 
     // Check that the target partitions have the same content.
     for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
@@ -1099,6 +1129,9 @@
 
 // Test that the old partitions are not modified.
 TEST_F(SnapshotUpdateTest, TestRollback) {
+#ifdef SKIP_TEST_IN_PRESUBMIT
+    GTEST_SKIP() << "WIP failure b/148889015";
+#endif
     // Execute the update.
     ASSERT_TRUE(sm->BeginUpdate());
     ASSERT_TRUE(sm->UnmapUpdateSnapshot("sys_b"));
@@ -1126,7 +1159,7 @@
     auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
     ASSERT_NE(init, nullptr);
     ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
 
     // Check that the target partitions have the same content.
     for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
@@ -1138,7 +1171,7 @@
     init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_a"));
     ASSERT_NE(init, nullptr);
     ASSERT_FALSE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
 
     // Assert that the source partitions aren't affected.
     for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
@@ -1175,7 +1208,7 @@
     auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
     ASSERT_NE(init, nullptr);
     ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
     init = nullptr;
 
     // Initiate the merge and wait for it to be completed.
@@ -1188,7 +1221,9 @@
 
     // Check that the old COW space is reclaimed and does not occupy space of mapped partitions.
     auto src = MetadataBuilder::New(*opener_, "super", 1);
+    ASSERT_NE(src, nullptr);
     auto tgt = MetadataBuilder::New(*opener_, "super", 0);
+    ASSERT_NE(tgt, nullptr);
     for (const auto& cow_part_name : {"sys_a-cow", "vnd_a-cow", "prd_a-cow"}) {
         auto* cow_part = tgt->FindPartition(cow_part_name);
         ASSERT_NE(nullptr, cow_part) << cow_part_name << " does not exist in target metadata";
@@ -1274,11 +1309,15 @@
 }
 
 TEST_F(SnapshotUpdateTest, MergeCannotRemoveCow) {
+#ifdef SKIP_TEST_IN_PRESUBMIT
+    GTEST_SKIP() << "WIP failure b/148889015";
+#endif
     // Make source partitions as big as possible to force COW image to be created.
     SetSize(sys_, 5_MiB);
     SetSize(vnd_, 5_MiB);
     SetSize(prd_, 5_MiB);
     src_ = MetadataBuilder::New(*opener_, "super", 0);
+    ASSERT_NE(src_, nullptr);
     src_->RemoveGroupAndPartitions(group_->name() + "_a");
     src_->RemoveGroupAndPartitions(group_->name() + "_b");
     ASSERT_TRUE(FillFakeMetadata(src_.get(), manifest_, "_a"));
@@ -1308,7 +1347,7 @@
     // won't be set.
     auto init = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
     ASSERT_NE(init, nullptr);
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
 
     // Keep an open handle to the cow device. This should cause the merge to
     // be incomplete.
@@ -1324,7 +1363,7 @@
     ASSERT_TRUE(UnmapAll());
 
     // init does first stage mount again.
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
 
     // sys_b should be mapped as a dm-linear device directly.
     ASSERT_FALSE(sm->IsSnapshotDevice("sys_b", nullptr));
@@ -1410,7 +1449,7 @@
     auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
     ASSERT_NE(init, nullptr);
     ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
     init = nullptr;
 
     // Initiate the merge and then immediately stop it to simulate a reboot.
@@ -1515,7 +1554,7 @@
     auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
     ASSERT_NE(init, nullptr);
     ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
 
     // Check that the target partition have the same content. Hashtree and FEC extents
     // should be accounted for.
@@ -1547,6 +1586,9 @@
 }
 
 TEST_F(SnapshotUpdateTest, WaitForMerge) {
+#ifdef SKIP_TEST_IN_PRESUBMIT
+    GTEST_SKIP() << "WIP failure b/148889015";
+#endif
     AddOperationForPartitions();
 
     // Execute the update.
@@ -1567,7 +1609,7 @@
     {
         auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
         ASSERT_NE(nullptr, init);
-        ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+        ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
     }
 
     auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
@@ -1585,12 +1627,35 @@
     ASSERT_THAT(merger.get(), AnyOf(UpdateState::None, UpdateState::MergeCompleted));
 }
 
+TEST_F(SnapshotUpdateTest, LowSpace) {
+    static constexpr auto kMaxFree = 10_MiB;
+    auto userdata = std::make_unique<LowSpaceUserdata>();
+    ASSERT_TRUE(userdata->Init(kMaxFree));
+
+    // Grow all partitions to 5_MiB, total 15_MiB. This requires 15 MiB of CoW space. After
+    // using the empty space in super (< 1 MiB), it uses at least 14 MiB of /userdata space.
+    constexpr uint64_t partition_size = 5_MiB;
+    SetSize(sys_, partition_size);
+    SetSize(vnd_, partition_size);
+    SetSize(prd_, partition_size);
+
+    AddOperationForPartitions();
+
+    // Execute the update.
+    ASSERT_TRUE(sm->BeginUpdate());
+    auto res = sm->CreateUpdateSnapshots(manifest_);
+    ASSERT_FALSE(res);
+    ASSERT_EQ(Return::ErrorCode::NO_SPACE, res.error_code());
+    ASSERT_GE(res.required_size(), 14_MiB);
+    ASSERT_LT(res.required_size(), 15_MiB);
+}
+
 class FlashAfterUpdateTest : public SnapshotUpdateTest,
                              public WithParamInterface<std::tuple<uint32_t, bool>> {
   public:
     AssertionResult InitiateMerge(const std::string& slot_suffix) {
         auto sm = SnapshotManager::New(new TestDeviceInfo(fake_super, slot_suffix));
-        if (!sm->CreateLogicalAndSnapshotPartitions("super")) {
+        if (!sm->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_)) {
             return AssertionFailure() << "Cannot CreateLogicalAndSnapshotPartitions";
         }
         if (!sm->InitiateMerge()) {
@@ -1601,6 +1666,8 @@
 };
 
 TEST_P(FlashAfterUpdateTest, FlashSlotAfterUpdate) {
+    if (!is_virtual_ab_) GTEST_SKIP() << "Test for Virtual A/B devices only";
+
     // OTA client blindly unmaps all partitions that are possibly mapped.
     for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
         ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
@@ -1615,7 +1682,8 @@
     // Simulate shutting down the device.
     ASSERT_TRUE(UnmapAll());
 
-    if (std::get<1>(GetParam()) /* merge */) {
+    bool after_merge = std::get<1>(GetParam());
+    if (after_merge) {
         ASSERT_TRUE(InitiateMerge("_b"));
         // Simulate shutting down the device after merge has initiated.
         ASSERT_TRUE(UnmapAll());
@@ -1626,6 +1694,7 @@
 
     // Simulate flashing |flashed_slot|. This clears the UPDATED flag.
     auto flashed_builder = MetadataBuilder::New(*opener_, "super", flashed_slot);
+    ASSERT_NE(flashed_builder, nullptr);
     flashed_builder->RemoveGroupAndPartitions(group_->name() + flashed_slot_suffix);
     flashed_builder->RemoveGroupAndPartitions(kCowGroupName);
     ASSERT_TRUE(FillFakeMetadata(flashed_builder.get(), manifest_, flashed_slot_suffix));
@@ -1636,9 +1705,11 @@
     ASSERT_NE(nullptr, flashed_builder->FindPartition("prd" + flashed_slot_suffix));
     flashed_builder->RemovePartition("prd" + flashed_slot_suffix);
 
+    // Note that fastbootd always updates the partition table of both slots.
     auto flashed_metadata = flashed_builder->Export();
     ASSERT_NE(nullptr, flashed_metadata);
-    ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *flashed_metadata, flashed_slot));
+    ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *flashed_metadata, 0));
+    ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *flashed_metadata, 1));
 
     std::string path;
     for (const auto& name : {"sys", "vnd"}) {
@@ -1664,21 +1735,11 @@
     auto init = SnapshotManager::NewForFirstStageMount(
             new TestDeviceInfo(fake_super, flashed_slot_suffix));
     ASSERT_NE(init, nullptr);
-    if (init->NeedSnapshotsInFirstStageMount()) {
-        ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
-    } else {
-        for (const auto& name : {"sys", "vnd"}) {
-            ASSERT_TRUE(CreateLogicalPartition(
-                    CreateLogicalPartitionParams{
-                            .block_device = fake_super,
-                            .metadata_slot = flashed_slot,
-                            .partition_name = name + flashed_slot_suffix,
-                            .timeout_ms = 1s,
-                            .partition_opener = opener_.get(),
-                    },
-                    &path));
-        }
+
+    if (flashed_slot && after_merge) {
+        ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
     }
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
 
     // Check that the target partitions have the same content.
     for (const auto& name : {"sys", "vnd"}) {
@@ -1700,6 +1761,61 @@
                                     "Merge"s;
                          });
 
+// Test behavior of ImageManager::Create on low space scenario. These tests assumes image manager
+// uses /data as backup device.
+class ImageManagerTest : public SnapshotTest, public WithParamInterface<uint64_t> {
+  protected:
+    void SetUp() override {
+        if (!is_virtual_ab_) GTEST_SKIP() << "Test for Virtual A/B devices only";
+        GTEST_SKIP() << "WIP failure b/149738928";
+
+        SnapshotTest::SetUp();
+        userdata_ = std::make_unique<LowSpaceUserdata>();
+        ASSERT_TRUE(userdata_->Init(GetParam()));
+    }
+    void TearDown() override {
+        if (!is_virtual_ab_) return;
+        return;  // BUG(149738928)
+
+        EXPECT_TRUE(!image_manager_->BackingImageExists(kImageName) ||
+                    image_manager_->DeleteBackingImage(kImageName));
+    }
+    static constexpr const char* kImageName = "my_image";
+    std::unique_ptr<LowSpaceUserdata> userdata_;
+};
+
+TEST_P(ImageManagerTest, CreateImageEnoughAvailSpace) {
+    if (userdata_->available_space() == 0) {
+        GTEST_SKIP() << "/data is full (" << userdata_->available_space()
+                     << " bytes available), skipping";
+    }
+    ASSERT_TRUE(image_manager_->CreateBackingImage(kImageName, userdata_->available_space(),
+                                                   IImageManager::CREATE_IMAGE_DEFAULT))
+            << "Should be able to create image with size = " << userdata_->available_space()
+            << " bytes";
+    ASSERT_TRUE(image_manager_->DeleteBackingImage(kImageName))
+            << "Should be able to delete created image";
+}
+
+TEST_P(ImageManagerTest, CreateImageNoSpace) {
+    uint64_t to_allocate = userdata_->free_space() + userdata_->bsize();
+    auto res = image_manager_->CreateBackingImage(kImageName, to_allocate,
+                                                  IImageManager::CREATE_IMAGE_DEFAULT);
+    ASSERT_FALSE(res) << "Should not be able to create image with size = " << to_allocate
+                      << " bytes because only " << userdata_->free_space() << " bytes are free";
+    ASSERT_EQ(FiemapStatus::ErrorCode::NO_SPACE, res.error_code()) << res.string();
+}
+
+std::vector<uint64_t> ImageManagerTestParams() {
+    std::vector<uint64_t> ret;
+    for (uint64_t size = 1_MiB; size <= 512_MiB; size *= 2) {
+        ret.push_back(size);
+    }
+    return ret;
+}
+
+INSTANTIATE_TEST_SUITE_P(ImageManagerTest, ImageManagerTest, ValuesIn(ImageManagerTestParams()));
+
 }  // namespace snapshot
 }  // namespace android
 
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index 1bc0357..e35ad4b 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -19,9 +19,15 @@
 #include <chrono>
 #include <iostream>
 #include <map>
+#include <sstream>
 
+#include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/unique_fd.h>
 #include <libsnapshot/snapshot.h>
+#include "utility.h"
+
+#include "utility.h"
 
 using namespace std::string_literals;
 
@@ -31,9 +37,11 @@
                  "Actions:\n"
                  "  dump\n"
                  "    Print snapshot states.\n"
-                 "  merge [--logcat]\n"
+                 "  merge [--logcat] [--log-to-file]\n"
                  "    Initialize merge and wait for it to be completed.\n"
-                 "    If --logcat is specified, log to logcat. Otherwise, log to stdout.\n";
+                 "    If --logcat is specified, log to logcat.\n"
+                 "    If --log-to-file is specified, log to /data/misc/snapshotctl_log/.\n"
+                 "    If both specified, log to both. If none specified, log to stdout.\n";
     return EX_USAGE;
 }
 
@@ -45,24 +53,78 @@
     return SnapshotManager::New()->Dump(std::cout);
 }
 
+class FileLogger {
+  public:
+    FileLogger() {
+        static constexpr const char* kLogFilePath = "/data/misc/snapshotctl_log/";
+        std::stringstream ss;
+        ss << kLogFilePath << "snapshotctl." << Now() << ".log";
+        fd_.reset(TEMP_FAILURE_RETRY(
+                open(ss.str().c_str(),
+                     O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_SYNC, 0644)));
+        if (fd_ == -1) {
+            PLOG(ERROR) << "Cannot open persistent log " << ss.str();
+            return;
+        }
+        // Explicitly chmod again because mode in open() may be masked by umask.
+        if (fchmod(fd_.get(), 0644) == -1) {
+            PLOG(ERROR) << "Cannot chmod 0644 persistent log " << ss.str();
+            return;
+        }
+    }
+    // Copy-contuctor needed to be converted to std::function.
+    FileLogger(const FileLogger& other) { fd_.reset(dup(other.fd_)); }
+    void operator()(android::base::LogId, android::base::LogSeverity, const char* /*tag*/,
+                    const char* /*file*/, unsigned int /*line*/, const char* message) {
+        if (fd_ == -1) return;
+        std::stringstream ss;
+        ss << Now() << ":" << message << "\n";
+        (void)android::base::WriteStringToFd(ss.str(), fd_);
+    }
+
+  private:
+    android::base::unique_fd fd_;
+};
+
+class MergeCmdLogger {
+  public:
+    MergeCmdLogger(int argc, char** argv) {
+        for (int i = 0; i < argc; ++i) {
+            if (argv[i] == "--logcat"s) {
+                loggers_.push_back(android::base::LogdLogger());
+            }
+            if (argv[i] == "--log-to-file"s) {
+                loggers_.push_back(std::move(FileLogger()));
+            }
+        }
+        if (loggers_.empty()) {
+            loggers_.push_back(&android::base::StdioLogger);
+        }
+    }
+    void operator()(android::base::LogId id, android::base::LogSeverity severity, const char* tag,
+                    const char* file, unsigned int line, const char* message) {
+        for (auto&& logger : loggers_) {
+            logger(id, severity, tag, file, line, message);
+        }
+    }
+
+  private:
+    std::vector<android::base::LogFunction> loggers_;
+};
+
 bool MergeCmdHandler(int argc, char** argv) {
     auto begin = std::chrono::steady_clock::now();
 
-    bool log_to_logcat = false;
-    for (int i = 2; i < argc; ++i) {
-        if (argv[i] == "--logcat"s) {
-            log_to_logcat = true;
-        }
-    }
-    if (log_to_logcat) {
-        android::base::InitLogging(argv);
-    } else {
-        android::base::InitLogging(argv, &android::base::StdioLogger);
-    }
+    // 'snapshotctl merge' is stripped away from arguments to
+    // Logger.
+    android::base::InitLogging(argv);
+    android::base::SetLogger(MergeCmdLogger(argc - 2, argv + 2));
 
     auto state = SnapshotManager::New()->InitiateMergeAndWait();
 
-    if (state == UpdateState::None) {
+    // We could wind up in the Unverified state if the device rolled back or
+    // hasn't fully rebooted. Ignore this.
+    if (state == UpdateState::None || state == UpdateState::Unverified) {
         return true;
     }
     if (state == UpdateState::MergeCompleted) {
diff --git a/fs_mgr/libsnapshot/snapshotctl.rc b/fs_mgr/libsnapshot/snapshotctl.rc
index 3ab0645..5dbe352 100644
--- a/fs_mgr/libsnapshot/snapshotctl.rc
+++ b/fs_mgr/libsnapshot/snapshotctl.rc
@@ -1,2 +1,2 @@
 on property:sys.boot_completed=1
-    exec_background - root root -- /system/bin/snapshotctl merge --logcat
+    exec_background - root root -- /system/bin/snapshotctl merge --logcat --log-to-file
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index f7f25af..b036606 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -14,8 +14,11 @@
 
 #include <libsnapshot/test_helpers.h>
 
+#include <sys/statvfs.h>
+
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <gtest/gtest.h>
 #include <openssl/sha.h>
@@ -167,5 +170,67 @@
     return partition_update->mutable_new_partition_info()->size();
 }
 
+AssertionResult LowSpaceUserdata::Init(uint64_t max_free_space) {
+    auto res = ReadUserdataStats();
+    if (!res) return res;
+
+    // Try to fill up the disk as much as possible until free_space_ <= max_free_space.
+    big_file_ = std::make_unique<TemporaryFile>();
+    if (big_file_->fd == -1) {
+        return AssertionFailure() << strerror(errno);
+    }
+    if (!android::base::StartsWith(big_file_->path, kUserDataDevice)) {
+        return AssertionFailure() << "Temp file allocated to " << big_file_->path << ", not in "
+                                  << kUserDataDevice;
+    }
+    uint64_t next_consume =
+            std::min(free_space_ - max_free_space, (uint64_t)std::numeric_limits<off_t>::max());
+    off_t allocated = 0;
+    while (next_consume > 0 && free_space_ > max_free_space) {
+        int status = fallocate(big_file_->fd, 0, allocated, next_consume);
+        if (status == -1 && errno == ENOSPC) {
+            next_consume /= 2;
+            continue;
+        }
+        if (status == -1) {
+            return AssertionFailure() << strerror(errno);
+        }
+        allocated += next_consume;
+
+        res = ReadUserdataStats();
+        if (!res) return res;
+    }
+
+    LOG(INFO) << allocated << " bytes allocated to " << big_file_->path;
+    initialized_ = true;
+    return AssertionSuccess();
+}
+
+AssertionResult LowSpaceUserdata::ReadUserdataStats() {
+    struct statvfs buf;
+    if (statvfs(kUserDataDevice, &buf) == -1) {
+        return AssertionFailure() << strerror(errno);
+    }
+    bsize_ = buf.f_bsize;
+    free_space_ = buf.f_bsize * buf.f_bfree;
+    available_space_ = buf.f_bsize * buf.f_bavail;
+    return AssertionSuccess();
+}
+
+uint64_t LowSpaceUserdata::free_space() const {
+    CHECK(initialized_);
+    return free_space_;
+}
+
+uint64_t LowSpaceUserdata::available_space() const {
+    CHECK(initialized_);
+    return available_space_;
+}
+
+uint64_t LowSpaceUserdata::bsize() const {
+    CHECK(initialized_);
+    return bsize_;
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index fa1d7f0..3318b33 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -14,12 +14,19 @@
 
 #include "utility.h"
 
+#include <errno.h>
+#include <time.h>
+
+#include <iomanip>
+#include <sstream>
+
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/strings.h>
 #include <fs_mgr/roots.h>
 
 using android::dm::kSectorSize;
+using android::fiemap::FiemapStatus;
 using android::fs_mgr::EnsurePathMounted;
 using android::fs_mgr::EnsurePathUnmounted;
 using android::fs_mgr::Fstab;
@@ -83,7 +90,7 @@
     }
 }
 
-bool InitializeCow(const std::string& device) {
+Return InitializeCow(const std::string& device) {
     // When the kernel creates a persistent dm-snapshot, it requires a CoW file
     // to store the modifications. The kernel interface does not specify how
     // the CoW is used, and there is no standard associated.
@@ -103,15 +110,15 @@
     android::base::unique_fd fd(open(device.c_str(), O_WRONLY | O_BINARY));
     if (fd < 0) {
         PLOG(ERROR) << "Can't open COW device: " << device;
-        return false;
+        return Return(FiemapStatus::FromErrno(errno));
     }
 
     LOG(INFO) << "Zero-filling COW device: " << device;
     if (!android::base::WriteFully(fd, zeros.data(), kDmSnapZeroFillSize)) {
         PLOG(ERROR) << "Can't zero-fill COW device for " << device;
-        return false;
+        return Return(FiemapStatus::FromErrno(errno));
     }
-    return true;
+    return Return::Ok();
 }
 
 std::unique_ptr<AutoUnmountDevice> AutoUnmountDevice::New(const std::string& path) {
@@ -152,5 +159,12 @@
     return true;
 }
 
+std::ostream& operator<<(std::ostream& os, const Now&) {
+    struct tm now;
+    time_t t = time(nullptr);
+    localtime_r(&t, &now);
+    return os << std::put_time(&now, "%Y%m%d-%H%M%S");
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index 5cc572e..90ad0fe 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -15,6 +15,7 @@
 #pragma once
 
 #include <functional>
+#include <iostream>
 #include <string>
 
 #include <android-base/macros.h>
@@ -26,6 +27,7 @@
 #include <update_engine/update_metadata.pb.h>
 
 #include <libsnapshot/auto_device.h>
+#include <libsnapshot/snapshot.h>
 
 namespace android {
 namespace snapshot {
@@ -110,7 +112,7 @@
         android::fs_mgr::MetadataBuilder* builder, const std::string& suffix);
 
 // Initialize a device before using it as the COW device for a dm-snapshot device.
-bool InitializeCow(const std::string& device);
+Return InitializeCow(const std::string& device);
 
 // "Atomically" write string to file. This is done by a series of actions:
 // 1. Write to path + ".tmp"
@@ -119,5 +121,9 @@
 // is an open fd to |path|, because that fd has an old view of the file.
 bool WriteStringToFileAtomic(const std::string& content, const std::string& path);
 
+// Writes current time to a given stream.
+struct Now {};
+std::ostream& operator<<(std::ostream& os, const Now&);
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libstorage_literals/Android.bp b/fs_mgr/libstorage_literals/Android.bp
index 11611dd..beb18ef 100644
--- a/fs_mgr/libstorage_literals/Android.bp
+++ b/fs_mgr/libstorage_literals/Android.bp
@@ -2,5 +2,6 @@
 cc_library_headers {
     name: "libstorage_literals_headers",
     host_supported: true,
+    recovery_available: true,
     export_include_dirs: ["."],
 }
diff --git a/fs_mgr/libvbmeta/Android.bp b/fs_mgr/libvbmeta/Android.bp
index 937e0f3..bceabab 100644
--- a/fs_mgr/libvbmeta/Android.bp
+++ b/fs_mgr/libvbmeta/Android.bp
@@ -37,6 +37,7 @@
 cc_test_host {
     name: "libvbmeta_test",
     static_libs: [
+        "liblog",
         "libsparse",
         "libvbmeta",
         "libz",
diff --git a/fs_mgr/libvbmeta/builder.cpp b/fs_mgr/libvbmeta/builder.cpp
index a901a4f..e6576ce 100644
--- a/fs_mgr/libvbmeta/builder.cpp
+++ b/fs_mgr/libvbmeta/builder.cpp
@@ -40,18 +40,18 @@
 Result<void> SuperVBMetaBuilder::Build() {
     for (const auto& [vbmeta_name, file_path] : images_path_) {
         Result<std::string> content = ReadVBMetaImageFromFile(file_path);
-        if (!content) {
+        if (!content.ok()) {
             return content.error();
         }
 
         Result<uint8_t> vbmeta_index = AddVBMetaImage(vbmeta_name);
-        if (!vbmeta_index) {
+        if (!vbmeta_index.ok()) {
             return vbmeta_index.error();
         }
 
         Result<void> rv_export_vbmeta_image =
                 ExportVBMetaImageToFile(vbmeta_index.value(), content.value());
-        if (!rv_export_vbmeta_image) {
+        if (!rv_export_vbmeta_image.ok()) {
             return rv_export_vbmeta_image;
         }
     }
@@ -65,7 +65,7 @@
     }
 
     Result<uint64_t> file_size = GetFileSize(source_fd);
-    if (!file_size) {
+    if (!file_size.ok()) {
         return file_size.error();
     }
 
@@ -98,7 +98,7 @@
         slot_number = desc->vbmeta_index;
     } else {
         Result<uint8_t> new_slot = GetEmptySlot();
-        if (!new_slot) {
+        if (!new_slot.ok()) {
             return new_slot;
         }
         slot_number = new_slot.value();
@@ -162,7 +162,7 @@
 
     android::base::Result<void> rv_write_primary_vbmeta_table =
             WritePrimaryVBMetaTable(super_vbmeta_fd_, serialized_table);
-    if (!rv_write_primary_vbmeta_table) {
+    if (!rv_write_primary_vbmeta_table.ok()) {
         return rv_write_primary_vbmeta_table;
     }
 
@@ -175,7 +175,7 @@
                                                          const std::string& vbmeta_image) {
     Result<void> rv_write_vbmeta_image =
             WriteVBMetaImage(super_vbmeta_fd_, vbmeta_index, vbmeta_image);
-    if (!rv_write_vbmeta_image) {
+    if (!rv_write_vbmeta_image.ok()) {
         return rv_write_vbmeta_image;
     }
 
@@ -196,13 +196,13 @@
     SuperVBMetaBuilder builder(super_vbmeta_fd, images_path);
 
     Result<void> rv_build = builder.Build();
-    if (!rv_build) {
+    if (!rv_build.ok()) {
         LERROR << rv_build.error();
         return false;
     }
 
     Result<void> rv_export = builder.ExportVBMetaTableToFile();
-    if (!rv_export) {
+    if (!rv_export.ok()) {
         LERROR << rv_export.error();
         return false;
     }
@@ -211,4 +211,4 @@
 }
 
 }  // namespace fs_mgr
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/fs_mgr/libvbmeta/builder_test.cpp b/fs_mgr/libvbmeta/builder_test.cpp
index 9a015fd..487bece 100644
--- a/fs_mgr/libvbmeta/builder_test.cpp
+++ b/fs_mgr/libvbmeta/builder_test.cpp
@@ -26,24 +26,20 @@
     std::unique_ptr<SuperVBMetaBuilder> builder = std::make_unique<SuperVBMetaBuilder>();
     ASSERT_NE(builder, nullptr);
 
-    Result<uint8_t> vbmeta_index = builder->AddVBMetaImage("vbmeta" /* vbmeta_name */
-    );
-    EXPECT_TRUE(vbmeta_index);
+    Result<uint8_t> vbmeta_index = builder->AddVBMetaImage("vbmeta" /* vbmeta_name */);
+    EXPECT_RESULT_OK(vbmeta_index);
 
-    Result<uint8_t> vbmeta_system_slot = builder->AddVBMetaImage("vbmeta_system" /* vbmeta_name */
-    );
-    EXPECT_TRUE(vbmeta_system_slot);
+    Result<uint8_t> vbmeta_system_slot = builder->AddVBMetaImage("vbmeta_system" /* vbmeta_name */);
+    EXPECT_RESULT_OK(vbmeta_system_slot);
 
-    Result<uint8_t> vbmeta_vendor_slot = builder->AddVBMetaImage("vbmeta_vendor" /* vbmeta_name */
-    );
-    EXPECT_TRUE(vbmeta_vendor_slot);
+    Result<uint8_t> vbmeta_vendor_slot = builder->AddVBMetaImage("vbmeta_vendor" /* vbmeta_name */);
+    EXPECT_RESULT_OK(vbmeta_vendor_slot);
 
-    builder->DeleteVBMetaImage("vbmeta_system" /* vbmeta_name */
-    );
+    builder->DeleteVBMetaImage("vbmeta_system" /* vbmeta_name */);
 
-    Result<uint8_t> vbmeta_product_slot = builder->AddVBMetaImage("vbmeta_product" /* vbmeta_name */
-    );
-    EXPECT_TRUE(vbmeta_product_slot);
+    Result<uint8_t> vbmeta_product_slot =
+            builder->AddVBMetaImage("vbmeta_product" /* vbmeta_name */);
+    EXPECT_RESULT_OK(vbmeta_product_slot);
 
     std::unique_ptr<VBMetaTable> table = builder->ExportVBMetaTable();
     ASSERT_NE(table, nullptr);
@@ -77,4 +73,4 @@
     for (int i = 0; i < sizeof(table->descriptors[2].reserved); i++)
         EXPECT_EQ(table->descriptors[2].reserved[i], 0);
     EXPECT_EQ(table->descriptors[2].vbmeta_name, "vbmeta_product");
-}
\ No newline at end of file
+}
diff --git a/fs_mgr/libvbmeta/reader.cpp b/fs_mgr/libvbmeta/reader.cpp
index 212d186..7b5ed93 100644
--- a/fs_mgr/libvbmeta/reader.cpp
+++ b/fs_mgr/libvbmeta/reader.cpp
@@ -64,7 +64,7 @@
     }
 
     Result<void> rv_header = LoadAndVerifySuperVBMetaHeader(header_buffer.get(), &table->header);
-    if (!rv_header) {
+    if (!rv_header.ok()) {
         return rv_header;
     }
 
@@ -104,7 +104,7 @@
 Result<void> ValidateVBMetaImage(int super_vbmeta_fd, int vbmeta_index,
                                  const std::string& vbmeta_image) {
     Result<std::string> content = ReadVBMetaImage(super_vbmeta_fd, vbmeta_index);
-    if (!content) {
+    if (!content.ok()) {
         return content.error();
     }
 
diff --git a/fs_mgr/libvbmeta/super_vbmeta_test.cpp b/fs_mgr/libvbmeta/super_vbmeta_test.cpp
index 6b4fc5d..daed0d1 100644
--- a/fs_mgr/libvbmeta/super_vbmeta_test.cpp
+++ b/fs_mgr/libvbmeta/super_vbmeta_test.cpp
@@ -77,7 +77,7 @@
     android::base::unique_fd fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
     EXPECT_GT(fd, 0);
     Result<uint64_t> file_size = GetFileSize(fd);
-    EXPECT_TRUE(file_size);
+    EXPECT_RESULT_OK(file_size);
     std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(VBMETA_IMAGE_MAX_SIZE);
     EXPECT_TRUE(android::base::ReadFully(fd, buffer.get(), file_size.value()));
     return std::string(reinterpret_cast<char*>(buffer.get()), VBMETA_IMAGE_MAX_SIZE);
@@ -138,15 +138,15 @@
 
     // Check the size of vbmeta table
     Result<uint64_t> super_vbmeta_size = GetFileSize(fd);
-    EXPECT_TRUE(super_vbmeta_size);
+    EXPECT_RESULT_OK(super_vbmeta_size);
     EXPECT_EQ(super_vbmeta_size.value(),
               SUPER_VBMETA_TABLE_MAX_SIZE * 2 + VBMETA_IMAGE_MAX_SIZE * 3);
 
     // Check Primary vbmeta table is equal to Backup one
     VBMetaTable table;
-    EXPECT_TRUE(android::fs_mgr::ReadPrimaryVBMetaTable(fd, &table));
+    EXPECT_RESULT_OK(android::fs_mgr::ReadPrimaryVBMetaTable(fd, &table));
     VBMetaTable table_backup;
-    EXPECT_TRUE(android::fs_mgr::ReadBackupVBMetaTable(fd, &table_backup));
+    EXPECT_RESULT_OK(android::fs_mgr::ReadBackupVBMetaTable(fd, &table_backup));
     EXPECT_EQ(android::fs_mgr::SerializeVBMetaTable(table),
               android::fs_mgr::SerializeVBMetaTable(table_backup));
 
@@ -167,25 +167,25 @@
     EXPECT_EQ(table.descriptors[0].vbmeta_name_length, 14);
     EXPECT_EQ(table.descriptors[0].vbmeta_name, "vbmeta_product");
     Result<std::string> vbmeta_product_content = ReadVBMetaImage(fd, 0);
-    EXPECT_TRUE(vbmeta_product_content);
+    EXPECT_RESULT_OK(vbmeta_product_content);
     EXPECT_EQ(ReadVBMetaImageFromFile(vbmeta_product_path), vbmeta_product_content.value());
 
     EXPECT_EQ(table.descriptors[1].vbmeta_index, 1);
     EXPECT_EQ(table.descriptors[1].vbmeta_name_length, 13);
     EXPECT_EQ(table.descriptors[1].vbmeta_name, "vbmeta_system");
     Result<std::string> vbmeta_system_content = ReadVBMetaImage(fd, 1);
-    EXPECT_TRUE(vbmeta_system_content);
+    EXPECT_RESULT_OK(vbmeta_system_content);
     EXPECT_EQ(ReadVBMetaImageFromFile(vbmeta_system_path), vbmeta_system_content.value());
 
     EXPECT_EQ(table.descriptors[2].vbmeta_index, 2);
     EXPECT_EQ(table.descriptors[2].vbmeta_name_length, 13);
     EXPECT_EQ(table.descriptors[2].vbmeta_name, "vbmeta_vendor");
     Result<std::string> vbmeta_vendor_content = ReadVBMetaImage(fd, 2);
-    EXPECT_TRUE(vbmeta_vendor_content);
+    EXPECT_RESULT_OK(vbmeta_vendor_content);
     EXPECT_EQ(ReadVBMetaImageFromFile(vbmeta_vendor_path), vbmeta_vendor_content.value());
 }
 
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
     return RUN_ALL_TESTS();
-}
\ No newline at end of file
+}
diff --git a/fs_mgr/tests/AndroidTest.xml b/fs_mgr/tests/AndroidTest.xml
index 91c3fb9..0ff8995 100644
--- a/fs_mgr/tests/AndroidTest.xml
+++ b/fs_mgr/tests/AndroidTest.xml
@@ -15,6 +15,7 @@
     <option name="config-descriptor:metadata" key="component" value="systems" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
         <option name="push" value="CtsFsMgrTestCases->/data/local/tmp/CtsFsMgrTestCases" />
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index 4226e95..cf324fe 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -474,20 +474,9 @@
       if [ 0 != ${counter} ]; then
         adb_wait
       fi
-      if [ -n "`get_property sys.boot.reason`" ]
-      then
-        vals=`get_property |
-              sed -n 's/[[]sys[.]\(boot_completed\|logbootcomplete\)[]]: [[]\([01]\)[]]$/\1=\2/p'`
-        if [ "${vals}" = "`echo boot_completed=1 ; echo logbootcomplete=1`" ]
-        then
-          sleep 1
-          break
-        fi
-        if [ "${vals}" = "`echo logbootcomplete=1 ; echo boot_completed=1`" ]
-        then
-          sleep 1
-          break
-        fi
+      if [ "1" = "`get_property sys.boot_completed`" ]; then
+        sleep 1
+        break
       fi
     fi
     counter=`expr ${counter} + 1`
@@ -858,7 +847,7 @@
 USB_SERIAL=
 [ -z "${ANDROID_SERIAL}" ] || USB_SERIAL=`find /sys/devices -name serial |
                                           grep usb |
-                                          xargs grep -l ${ANDROID_SERIAL}`
+                                          xargs -r grep -l ${ANDROID_SERIAL}`
 USB_ADDRESS=
 if [ -n "${USB_SERIAL}" ]; then
   USB_ADDRESS=${USB_SERIAL%/serial}
@@ -913,7 +902,11 @@
   done
 
 # If reboot too soon after fresh flash, could trip device update failure logic
-wait_for_screen
+if ! wait_for_screen && ${screen_wait}; then
+  screen_wait=false
+  echo "${ORANGE}[  WARNING ]${NORMAL} not healthy, no launcher, skipping wait for screen" >&2
+fi
+
 # Can we test remount -R command?
 OVERLAYFS_BACKING="cache mnt/scratch"
 overlayfs_supported=true
@@ -1160,13 +1153,16 @@
 ret=${?}
 uses_dynamic_scratch=false
 scratch_partition=
+virtual_ab=`get_property ro.virtual_ab.enabled`
 if ${overlayfs_needed}; then
   if [ ${ret} != 0 ]; then
     die -t ${T} "overlay takeover failed"
   fi
   echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
    echo "${ORANGE}[  WARNING ]${NORMAL} overlay takeover not complete" >&2
-  scratch_partition=scratch
+  if [ -z "${virtual_ab}" ]; then
+    scratch_partition=scratch
+  fi
   if echo "${D}" | grep " /mnt/scratch" >/dev/null; then
     echo "${BLUE}[     INFO ]${NORMAL} using ${scratch_partition} dynamic partition for overrides" >&2
   fi
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index c5adea6..69b3150 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -413,7 +413,7 @@
         EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
     }
     EXPECT_EQ("", entry->key_loc);
-    EXPECT_EQ("", entry->key_dir);
+    EXPECT_EQ("", entry->metadata_key_dir);
     EXPECT_EQ(0, entry->length);
     EXPECT_EQ("", entry->label);
     EXPECT_EQ(-1, entry->partnum);
@@ -440,7 +440,7 @@
         EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
     }
     EXPECT_EQ("", entry->key_loc);
-    EXPECT_EQ("", entry->key_dir);
+    EXPECT_EQ("", entry->metadata_key_dir);
     EXPECT_EQ(0, entry->length);
     EXPECT_EQ("", entry->label);
     EXPECT_EQ(-1, entry->partnum);
@@ -892,7 +892,45 @@
     FstabEntry::FsMgrFlags flags = {};
     EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
 
-    EXPECT_EQ("/dir/key", entry->key_dir);
+    EXPECT_EQ("/dir/key", entry->metadata_key_dir);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_MetadataEncryption) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    std::string fstab_contents = R"fs(
+source none0       swap   defaults      keydirectory=/dir/key,metadata_encryption=adiantum
+)fs";
+
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+    ASSERT_EQ(1U, fstab.size());
+
+    auto entry = fstab.begin();
+    EXPECT_EQ("adiantum", entry->metadata_encryption);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_MetadataEncryption_WrappedKey) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    std::string fstab_contents = R"fs(
+source none0       swap   defaults      keydirectory=/dir/key,metadata_encryption=aes-256-xts:wrappedkey_v0
+)fs";
+
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+    ASSERT_EQ(1U, fstab.size());
+
+    auto entry = fstab.begin();
+    EXPECT_EQ("aes-256-xts:wrappedkey_v0", entry->metadata_encryption);
+    auto parts = android::base::Split(entry->metadata_encryption, ":");
+    EXPECT_EQ(2U, parts.size());
+    EXPECT_EQ("aes-256-xts", parts[0]);
+    EXPECT_EQ("wrappedkey_v0", parts[1]);
 }
 
 TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_SysfsPath) {
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index bdf4aac..8e9e074 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -55,6 +55,7 @@
 using android::hardware::health::V1_0::BatteryHealth;
 using android::hardware::health::V1_0::BatteryStatus;
 using android::hardware::health::V2_1::BatteryCapacityLevel;
+using android::hardware::health::V2_1::Constants;
 
 namespace android {
 
@@ -79,6 +80,8 @@
     // HIDL enum values are zero initialized, so they need to be initialized
     // properly.
     health_info_2_1->batteryCapacityLevel = BatteryCapacityLevel::UNKNOWN;
+    health_info_2_1->batteryChargeTimeToFullNowSeconds =
+            (int64_t)Constants::BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED;
     auto* props = &health_info_2_1->legacy.legacy;
     props->batteryStatus = BatteryStatus::UNKNOWN;
     props->batteryHealth = BatteryHealth::UNKNOWN;
@@ -126,6 +129,26 @@
     return *ret;
 }
 
+BatteryCapacityLevel getBatteryCapacityLevel(const char* capacityLevel) {
+    static SysfsStringEnumMap<BatteryCapacityLevel> batteryCapacityLevelMap[] = {
+            {"Unknown", BatteryCapacityLevel::UNKNOWN},
+            {"Critical", BatteryCapacityLevel::CRITICAL},
+            {"Low", BatteryCapacityLevel::LOW},
+            {"Normal", BatteryCapacityLevel::NORMAL},
+            {"High", BatteryCapacityLevel::HIGH},
+            {"Full", BatteryCapacityLevel::FULL},
+            {NULL, BatteryCapacityLevel::UNSUPPORTED},
+    };
+
+    auto ret = mapSysfsString(capacityLevel, batteryCapacityLevelMap);
+    if (!ret) {
+        KLOG_WARNING(LOG_TAG, "Unsupported battery capacity level '%s'\n", capacityLevel);
+        *ret = BatteryCapacityLevel::UNSUPPORTED;
+    }
+
+    return *ret;
+}
+
 BatteryHealth getBatteryHealth(const char* status) {
     static SysfsStringEnumMap<BatteryHealth> batteryHealthMap[] = {
             {"Unknown", BatteryHealth::UNKNOWN},
@@ -241,10 +264,13 @@
         mHealthInfo->legacy.batteryCurrentAverage =
                 getIntField(mHealthdConfig->batteryCurrentAvgPath);
 
-    // TODO(b/142260281): Retrieve these values correctly.
-    mHealthInfo->batteryCapacityLevel = BatteryCapacityLevel::UNKNOWN;
-    mHealthInfo->batteryChargeTimeToFullNowSeconds = 0;
-    mHealthInfo->batteryFullCapacityUah = props.batteryFullCharge;
+    if (!mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty())
+        mHealthInfo->batteryChargeTimeToFullNowSeconds =
+                getIntField(mHealthdConfig->batteryChargeTimeToFullNowPath);
+
+    if (!mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty())
+        mHealthInfo->batteryFullChargeDesignCapacityUah =
+                getIntField(mHealthdConfig->batteryFullChargeDesignCapacityUahPath);
 
     props.batteryTemperature = mBatteryFixedTemperature ?
         mBatteryFixedTemperature :
@@ -252,6 +278,9 @@
 
     std::string buf;
 
+    if (readFromFile(mHealthdConfig->batteryCapacityLevelPath, &buf) > 0)
+        mHealthInfo->batteryCapacityLevel = getBatteryCapacityLevel(buf.c_str());
+
     if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
         props.batteryStatus = getBatteryStatus(buf.c_str());
 
@@ -585,6 +614,26 @@
                         mHealthdConfig->batteryCycleCountPath = path;
                 }
 
+                if (mHealthdConfig->batteryCapacityLevelPath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/capacity_level", POWER_SUPPLY_SYSFS_PATH, name);
+                    if (access(path, R_OK) == 0) mHealthdConfig->batteryCapacityLevelPath = path;
+                }
+
+                if (mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/time_to_full_now", POWER_SUPPLY_SYSFS_PATH, name);
+                    if (access(path, R_OK) == 0)
+                        mHealthdConfig->batteryChargeTimeToFullNowPath = path;
+                }
+
+                if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/charge_full_design", POWER_SUPPLY_SYSFS_PATH, name);
+                    if (access(path, R_OK) == 0)
+                        mHealthdConfig->batteryFullChargeDesignCapacityUahPath = path;
+                }
+
                 if (mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
                     path.clear();
                     path.appendFormat("%s/%s/current_avg",
@@ -653,6 +702,12 @@
             KLOG_WARNING(LOG_TAG, "BatteryFullChargePath not found\n");
         if (mHealthdConfig->batteryCycleCountPath.isEmpty())
             KLOG_WARNING(LOG_TAG, "BatteryCycleCountPath not found\n");
+        if (mHealthdConfig->batteryCapacityLevelPath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "batteryCapacityLevelPath not found\n");
+        if (mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "batteryChargeTimeToFullNowPath. not found\n");
+        if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "batteryFullChargeDesignCapacityUahPath. not found\n");
     }
 
     if (property_get("ro.boot.fake_battery", pval, NULL) > 0
diff --git a/healthd/charger.cpp b/healthd/charger.cpp
index 58ed416..d03978d 100644
--- a/healthd/charger.cpp
+++ b/healthd/charger.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <android-base/logging.h>
+
 #include "charger.sysprop.h"
 #include "healthd_mode_charger.h"
 #include "healthd_mode_charger_nops.h"
@@ -23,6 +25,7 @@
 #endif
 
 int main(int argc, char** argv) {
+    android::base::InitLogging(argv, &android::base::KernelLogger);
     if (CHARGER_FORCE_NO_UI || android::sysprop::ChargerProperties::no_ui().value_or(false)) {
         return healthd_charger_nops(argc, argv);
     } else {
diff --git a/healthd/charger_utils.cpp b/healthd/charger_utils.cpp
index 0cf9df5..8bbfb4e 100644
--- a/healthd/charger_utils.cpp
+++ b/healthd/charger_utils.cpp
@@ -17,50 +17,28 @@
 #include "charger_utils.h"
 
 #include <android-base/logging.h>
-#include <android/hidl/manager/1.0/IServiceManager.h>
+#include <android/hardware/health/2.1/IHealth.h>
 #include <health/utils.h>
 #include <health2impl/Health.h>
-#include <hidl/ServiceManagement.h>
-
-using android::hardware::getPassthroughServiceManager;
-using android::hidl::base::V1_0::IBase;
-using android::hidl::manager::V1_0::IServiceManager;
 
 namespace android {
 namespace hardware {
 namespace health {
-sp<V2_1::IHealth> GetPassthroughHealthImpl() {
-    // Not using getService() because there is no hwservicemanager in charger mode.
-    sp<IServiceManager> pm = getPassthroughServiceManager();
-    if (pm == nullptr) {
-        LOG(WARNING) << "Cannot get passthrough service manager.";
-        return nullptr;
-    }
-    sp<IBase> base = pm->get(V2_0::IHealth::descriptor, "default");
-    if (base == nullptr) {
-        LOG(WARNING) << "Cannot find passthrough implementation of health 2.0 HAL for instance "
-                        "'default' on the device.";
-        return nullptr;
-    }
-    sp<V2_1::IHealth> service = V2_1::IHealth::castFrom(base);
-    if (service == nullptr) {
-        LOG(WARNING)
-                << "Cannot cast passthrough implementation of health 2.0 HAL to 2.1 for instance "
-                   "'default' on the device.";
-        return nullptr;
-    }
-    return service;
-}
 
-sp<V2_1::IHealth> GetPassthroughHealth() {
-    auto impl = GetPassthroughHealthImpl();
-    if (impl == nullptr) {
+sp<V2_1::IHealth> GetHealthServiceOrDefault() {
+    // No need to use get_health_service from libhealthhalutils that
+    // checks for "backup" instance provided by healthd, since
+    // V2_1::implementation::Health does the same thing.
+    sp<V2_1::IHealth> service = V2_1::IHealth::getService();
+    if (service != nullptr) {
+        LOG(INFO) << "Charger uses health HAL service.";
+    } else {
         LOG(WARNING) << "Charger uses system defaults.";
         auto config = std::make_unique<healthd_config>();
         InitHealthdConfig(config.get());
-        impl = new V2_1::implementation::Health(std::move(config));
+        service = new V2_1::implementation::Health(std::move(config));
     }
-    return impl;
+    return service;
 }
 
 }  // namespace health
diff --git a/healthd/charger_utils.h b/healthd/charger_utils.h
index f96e827..39d8aab 100644
--- a/healthd/charger_utils.h
+++ b/healthd/charger_utils.h
@@ -21,7 +21,9 @@
 namespace android {
 namespace hardware {
 namespace health {
-sp<V2_1::IHealth> GetPassthroughHealth();
+// Return health HAL service. If it is not supported on the device (with
+// VINTF checks), return a default passthrough implementation.
+sp<V2_1::IHealth> GetHealthServiceOrDefault();
 }  // namespace health
 }  // namespace hardware
 }  // namespace android
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 7d844c9..386ba1a 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -60,7 +60,7 @@
 
 using namespace android;
 using android::hardware::Return;
-using android::hardware::health::GetPassthroughHealth;
+using android::hardware::health::GetHealthServiceOrDefault;
 using android::hardware::health::HealthLoop;
 using android::hardware::health::V1_0::BatteryStatus;
 using android::hardware::health::V2_0::Result;
@@ -742,6 +742,6 @@
         }
     }
 
-    Charger charger(GetPassthroughHealth());
+    Charger charger(GetHealthServiceOrDefault());
     return charger.StartLoop();
 }
diff --git a/healthd/healthd_mode_charger.h b/healthd/healthd_mode_charger.h
index 370ca86..6e569ee 100644
--- a/healthd/healthd_mode_charger.h
+++ b/healthd/healthd_mode_charger.h
@@ -72,7 +72,7 @@
     int64_t next_pwr_check_ = 0;
     int64_t wait_batt_level_timestamp_ = 0;
 
-    key_state keys_[KEY_MAX + 1];
+    key_state keys_[KEY_MAX + 1] = {};
 
     animation batt_anim_;
     GRSurface* surf_unknown_ = nullptr;
diff --git a/healthd/healthd_mode_charger_nops.cpp b/healthd/healthd_mode_charger_nops.cpp
index 13e7348..9fe381e 100644
--- a/healthd/healthd_mode_charger_nops.cpp
+++ b/healthd/healthd_mode_charger_nops.cpp
@@ -20,10 +20,10 @@
 
 #include "charger_utils.h"
 
-using android::hardware::health::GetPassthroughHealth;
+using android::hardware::health::GetHealthServiceOrDefault;
 using android::hardware::health::V2_1::implementation::HalHealthLoop;
 
 int healthd_charger_nops(int /* argc */, char** /* argv */) {
-    HalHealthLoop charger("charger", GetPassthroughHealth());
+    HalHealthLoop charger("charger", GetHealthServiceOrDefault());
     return charger.StartLoop();
 }
diff --git a/healthd/include/healthd/healthd.h b/healthd/include/healthd/healthd.h
index a900071..706c332 100644
--- a/healthd/include/healthd/healthd.h
+++ b/healthd/include/healthd/healthd.h
@@ -69,6 +69,9 @@
     android::String8 batteryChargeCounterPath;
     android::String8 batteryFullChargePath;
     android::String8 batteryCycleCountPath;
+    android::String8 batteryCapacityLevelPath;
+    android::String8 batteryChargeTimeToFullNowPath;
+    android::String8 batteryFullChargeDesignCapacityUahPath;
 
     int (*energyCounter)(int64_t *);
     int boot_min_cap;
diff --git a/init/Android.bp b/init/Android.bp
index 42d0b33..f28934e 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -14,6 +14,61 @@
 // limitations under the License.
 //
 
+init_common_sources = [
+    "action.cpp",
+    "action_manager.cpp",
+    "action_parser.cpp",
+    "capabilities.cpp",
+    "epoll.cpp",
+    "import_parser.cpp",
+    "interface_utils.cpp",
+    "keychords.cpp",
+    "parser.cpp",
+    "property_type.cpp",
+    "rlimit_parser.cpp",
+    "service.cpp",
+    "service_list.cpp",
+    "service_parser.cpp",
+    "service_utils.cpp",
+    "subcontext.cpp",
+    "subcontext.proto",
+    "tokenizer.cpp",
+    "util.cpp",
+]
+init_device_sources = [
+    "bootchart.cpp",
+    "builtins.cpp",
+    "devices.cpp",
+    "firmware_handler.cpp",
+    "first_stage_init.cpp",
+    "first_stage_mount.cpp",
+    "fscrypt_init_extensions.cpp",
+    "init.cpp",
+    "lmkd_service.cpp",
+    "modalias_handler.cpp",
+    "mount_handler.cpp",
+    "mount_namespace.cpp",
+    "persistent_properties.cpp",
+    "persistent_properties.proto",
+    "property_service.cpp",
+    "property_service.proto",
+    "reboot.cpp",
+    "reboot_utils.cpp",
+    "security.cpp",
+    "selabel.cpp",
+    "selinux.cpp",
+    "sigchld_handler.cpp",
+    "switch_root.cpp",
+    "uevent_listener.cpp",
+    "ueventd.cpp",
+    "ueventd_parser.cpp",
+]
+init_host_sources = [
+    "check_builtins.cpp",
+    "host_import_parser.cpp",
+    "host_init_verifier.cpp",
+]
+
 cc_defaults {
     name: "init_defaults",
     cpp_std: "experimental",
@@ -103,53 +158,7 @@
         "init_defaults",
         "selinux_policy_version",
     ],
-    srcs: [
-        "action.cpp",
-        "action_manager.cpp",
-        "action_parser.cpp",
-        "bootchart.cpp",
-        "builtins.cpp",
-        "capabilities.cpp",
-        "devices.cpp",
-        "epoll.cpp",
-        "firmware_handler.cpp",
-        "first_stage_init.cpp",
-        "first_stage_mount.cpp",
-        "fscrypt_init_extensions.cpp",
-        "import_parser.cpp",
-        "init.cpp",
-        "interface_utils.cpp",
-        "keychords.cpp",
-        "lmkd_service.cpp",
-        "modalias_handler.cpp",
-        "mount_handler.cpp",
-        "mount_namespace.cpp",
-        "parser.cpp",
-        "persistent_properties.cpp",
-        "persistent_properties.proto",
-        "property_service.cpp",
-        "property_service.proto",
-        "property_type.cpp",
-        "reboot.cpp",
-        "reboot_utils.cpp",
-        "security.cpp",
-        "selabel.cpp",
-        "selinux.cpp",
-        "service.cpp",
-        "service_list.cpp",
-        "service_parser.cpp",
-        "service_utils.cpp",
-        "sigchld_handler.cpp",
-        "subcontext.cpp",
-        "subcontext.proto",
-        "switch_root.cpp",
-        "rlimit_parser.cpp",
-        "tokenizer.cpp",
-        "uevent_listener.cpp",
-        "ueventd.cpp",
-        "ueventd_parser.cpp",
-        "util.cpp",
-    ],
+    srcs: init_common_sources + init_device_sources,
     whole_static_libs: [
         "libcap",
         "com.android.sysprop.apex",
@@ -297,30 +306,7 @@
         "libprocessgroup",
         "libprotobuf-cpp-lite",
     ],
-    srcs: [
-        "action.cpp",
-        "action_manager.cpp",
-        "action_parser.cpp",
-        "capabilities.cpp",
-        "check_builtins.cpp",
-        "epoll.cpp",
-        "keychords.cpp",
-        "import_parser.cpp",
-        "interface_utils.cpp",
-        "host_import_parser.cpp",
-        "host_init_verifier.cpp",
-        "parser.cpp",
-        "property_type.cpp",
-        "rlimit_parser.cpp",
-        "tokenizer.cpp",
-        "service.cpp",
-        "service_list.cpp",
-        "service_parser.cpp",
-        "service_utils.cpp",
-        "subcontext.cpp",
-        "subcontext.proto",
-        "util.cpp",
-    ],
+    srcs: init_common_sources + init_host_sources,
     proto: {
         type: "lite",
     },
diff --git a/init/AndroidTest.xml b/init/AndroidTest.xml
index 667911d..920dc6c 100644
--- a/init/AndroidTest.xml
+++ b/init/AndroidTest.xml
@@ -18,6 +18,7 @@
     <option name="config-descriptor:metadata" key="component" value="systems" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
         <option name="push" value="CtsInitTestCases->/data/local/tmp/CtsInitTestCases" />
diff --git a/init/README.md b/init/README.md
index b8300fa..4f0a7ec 100644
--- a/init/README.md
+++ b/init/README.md
@@ -487,6 +487,25 @@
   -f: force installation of the module even if the version of the running kernel
   and the version of the kernel for which the module was compiled do not match.
 
+`interface_start <name>` \
+`interface_restart <name>` \
+`interface_stop <name>`
+> Find the service that provides the interface _name_ if it exists and run the `start`, `restart`,
+or `stop` commands on it respectively.  _name_ may be either a fully qualified HIDL name, in which
+case it is specified as `<interface>/<instance>`, or an AIDL name, in which case it is specified as
+`aidl/<interface>` for example `android.hardware.secure_element@1.1::ISecureElement/eSE1` or
+`aidl/aidl_lazy_test_1`.
+
+> Note that these commands only act on interfaces specified by the `interface` service option, not
+on interfaces registered at runtime.
+
+> Example usage of these commands: \
+`interface_start android.hardware.secure_element@1.1::ISecureElement/eSE1`
+will start the HIDL Service that provides the `android.hardware.secure_element@1.1` and `eSI1`
+instance. \
+`interface_start aidl/aidl_lazy_test_1` will start the AIDL service that
+provides the `aidl_lazy_test_1` interface.
+
 `load_system_props`
 > (This action is deprecated and no-op.)
 
@@ -700,6 +719,26 @@
   `/sys/fs/ext4/${dev.mnt.blk.<mount_point>}/` to tune the block device
   characteristics in a device agnostic manner.
 
+Init responds to properties that begin with `ctl.`.  These properties take the format of
+`ctl.<command>` and the _value_ of the system property is used as a parameter, for example:
+`SetProperty("ctl.start", "logd")` will run the `start` command on `logd`.  Note that these
+properties are only settable; they will have no value when read.
+
+`ctl.start` \
+`ctl.restart` \
+`ctl.stop`
+> These are equivalent to using the `start`, `restart`, and `stop` commands on the service specified
+by the _value_ of the property.
+
+`ctl.interface_start` \
+`ctl.interface_restart` \
+`ctl.interface_stop`
+> These are equivalent to using the `interface_start`, `interface_restart`, and `interface_stop`
+commands on the interface specified by the _value_ of the property.
+
+`ctl.sigstop_on` and `ctl.sigstop_off` will turn on or off the _sigstop_ feature for the service
+specified by the _value_ of the property.  See the _Debugging init_ section below for more details
+about this feature.
 
 Boot timing
 -----------
diff --git a/init/action.cpp b/init/action.cpp
index f05fa7c..1e998ae 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -36,7 +36,7 @@
     builtin_arguments.args[0] = args[0];
     for (std::size_t i = 1; i < args.size(); ++i) {
         auto expanded_arg = ExpandProps(args[i]);
-        if (!expanded_arg) {
+        if (!expanded_arg.ok()) {
             return expanded_arg.error();
         }
         builtin_arguments.args[i] = std::move(*expanded_arg);
@@ -59,7 +59,7 @@
         }
 
         auto expanded_args = subcontext->ExpandArgs(args_);
-        if (!expanded_args) {
+        if (!expanded_args.ok()) {
             return expanded_args.error();
         }
         return RunBuiltinFunction(func_, *expanded_args, subcontext->context());
@@ -75,7 +75,7 @@
     builtin_arguments.args[0] = args_[0];
     for (size_t i = 1; i < args_.size(); ++i) {
         auto expanded_arg = ExpandProps(args_[i]);
-        if (!expanded_arg) {
+        if (!expanded_arg.ok()) {
             if (expanded_arg.error().message().find("doesn't exist while expanding") !=
                 std::string::npos) {
                 // If we failed because we won't have a property, use an empty string, which is
@@ -114,7 +114,7 @@
     }
 
     auto map_result = function_map_->Find(args);
-    if (!map_result) {
+    if (!map_result.ok()) {
         return Error() << map_result.error();
     }
 
@@ -134,7 +134,7 @@
 size_t Action::CheckAllCommands() const {
     size_t failures = 0;
     for (const auto& command : commands_) {
-        if (auto result = command.CheckCommand(); !result) {
+        if (auto result = command.CheckCommand(); !result.ok()) {
             LOG(ERROR) << "Command '" << command.BuildCommandString() << "' (" << filename_ << ":"
                        << command.line() << ") failed: " << result.error();
             ++failures;
@@ -169,7 +169,7 @@
 
         LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << " (" << filename_
                   << ":" << command.line() << ") took " << duration.count() << "ms and "
-                  << (result ? "succeeded" : "failed: " + result.error().message());
+                  << (result.ok() ? "succeeded" : "failed: " + result.error().message());
     }
 }
 
diff --git a/init/action_parser.cpp b/init/action_parser.cpp
index a8e1e09..f316871 100644
--- a/init/action_parser.cpp
+++ b/init/action_parser.cpp
@@ -110,14 +110,14 @@
 
         if (!args[i].compare(0, prop_str.length(), prop_str)) {
             if (auto result = ParsePropertyTrigger(args[i], subcontext, property_triggers);
-                !result) {
+                !result.ok()) {
                 return result;
             }
         } else {
             if (!event_trigger->empty()) {
                 return Error() << "multiple event triggers are not allowed";
             }
-            if (auto result = ValidateEventTrigger(args[i]); !result) {
+            if (auto result = ValidateEventTrigger(args[i]); !result.ok()) {
                 return result;
             }
 
@@ -145,8 +145,9 @@
     std::string event_trigger;
     std::map<std::string, std::string> property_triggers;
 
-    if (auto result = ParseTriggers(triggers, action_subcontext, &event_trigger, &property_triggers);
-        !result) {
+    if (auto result =
+                ParseTriggers(triggers, action_subcontext, &event_trigger, &property_triggers);
+        !result.ok()) {
         return Error() << "ParseTriggers() failed: " << result.error();
     }
 
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 3c32d8b..200bfff 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -60,6 +60,7 @@
 #include <fscrypt/fscrypt.h>
 #include <libgsi/libgsi.h>
 #include <logwrap/logwrap.h>
+#include <private/android_filesystem_config.h>
 #include <selinux/android.h>
 #include <selinux/label.h>
 #include <selinux/selinux.h>
@@ -163,7 +164,7 @@
     // They must  be started individually.
     for (const auto& service : ServiceList::GetInstance()) {
         if (service->classnames().count(args[1])) {
-            if (auto result = service->StartIfNotDisabled(); !result) {
+            if (auto result = service->StartIfNotDisabled(); !result.ok()) {
                 LOG(ERROR) << "Could not start service '" << service->name()
                            << "' as part of class '" << args[1] << "': " << result.error();
             }
@@ -185,7 +186,7 @@
     }
     for (const auto& service : ServiceList::GetInstance()) {
         if (service->classnames().count(args[1])) {
-            if (auto result = service->StartIfPostData(); !result) {
+            if (auto result = service->StartIfPostData(); !result.ok()) {
                 LOG(ERROR) << "Could not start service '" << service->name()
                            << "' as part of class '" << args[1] << "': " << result.error();
             }
@@ -226,7 +227,7 @@
 }
 
 static Result<void> do_domainname(const BuiltinArguments& args) {
-    if (auto result = WriteFile("/proc/sys/kernel/domainname", args[1]); !result) {
+    if (auto result = WriteFile("/proc/sys/kernel/domainname", args[1]); !result.ok()) {
         return Error() << "Unable to write to /proc/sys/kernel/domainname: " << result.error();
     }
     return {};
@@ -236,7 +237,7 @@
     Service* svc = ServiceList::GetInstance().FindService(args[1]);
     if (!svc) return Error() << "Could not find service";
 
-    if (auto result = svc->Enable(); !result) {
+    if (auto result = svc->Enable(); !result.ok()) {
         return Error() << "Could not enable service: " << result.error();
     }
 
@@ -245,10 +246,10 @@
 
 static Result<void> do_exec(const BuiltinArguments& args) {
     auto service = Service::MakeTemporaryOneshotService(args.args);
-    if (!service) {
+    if (!service.ok()) {
         return Error() << "Could not create exec service: " << service.error();
     }
-    if (auto result = (*service)->ExecStart(); !result) {
+    if (auto result = (*service)->ExecStart(); !result.ok()) {
         return Error() << "Could not start exec service: " << result.error();
     }
 
@@ -258,10 +259,10 @@
 
 static Result<void> do_exec_background(const BuiltinArguments& args) {
     auto service = Service::MakeTemporaryOneshotService(args.args);
-    if (!service) {
+    if (!service.ok()) {
         return Error() << "Could not create exec background service: " << service.error();
     }
-    if (auto result = (*service)->Start(); !result) {
+    if (auto result = (*service)->Start(); !result.ok()) {
         return Error() << "Could not start exec background service: " << result.error();
     }
 
@@ -275,7 +276,7 @@
         return Error() << "Service not found";
     }
 
-    if (auto result = service->ExecStart(); !result) {
+    if (auto result = service->ExecStart(); !result.ok()) {
         return Error() << "Could not start exec service: " << result.error();
     }
 
@@ -290,7 +291,7 @@
 }
 
 static Result<void> do_hostname(const BuiltinArguments& args) {
-    if (auto result = WriteFile("/proc/sys/kernel/hostname", args[1]); !result) {
+    if (auto result = WriteFile("/proc/sys/kernel/hostname", args[1]); !result.ok()) {
         return Error() << "Unable to write to /proc/sys/kernel/hostname: " << result.error();
     }
     return {};
@@ -348,7 +349,7 @@
 static Result<void> do_interface_start(const BuiltinArguments& args) {
     Service* svc = ServiceList::GetInstance().FindInterface(args[1]);
     if (!svc) return Error() << "interface " << args[1] << " not found";
-    if (auto result = svc->Start(); !result) {
+    if (auto result = svc->Start(); !result.ok()) {
         return Error() << "Could not start interface: " << result.error();
     }
     return {};
@@ -412,7 +413,7 @@
 // mkdir <path> [mode] [owner] [group] [<option> ...]
 static Result<void> do_mkdir(const BuiltinArguments& args) {
     auto options = ParseMkdir(args.args);
-    if (!options) return options.error();
+    if (!options.ok()) return options.error();
     return make_dir_with_options(*options);
 }
 
@@ -680,7 +681,7 @@
          * and return processed return code*/
         initial_mount_fstab_return_code = mount_fstab_return_code;
         auto queue_fs_result = queue_fs_event(mount_fstab_return_code, false);
-        if (!queue_fs_result) {
+        if (!queue_fs_result.ok()) {
             return Error() << "queue_fs_event() failed: " << queue_fs_result.error();
         }
     }
@@ -730,7 +731,7 @@
 
 static Result<void> do_setrlimit(const BuiltinArguments& args) {
     auto rlimit = ParseRlimit(args.args);
-    if (!rlimit) return rlimit.error();
+    if (!rlimit.ok()) return rlimit.error();
 
     if (setrlimit(rlimit->first, &rlimit->second) == -1) {
         return ErrnoError() << "setrlimit failed";
@@ -741,7 +742,7 @@
 static Result<void> do_start(const BuiltinArguments& args) {
     Service* svc = ServiceList::GetInstance().FindService(args[1]);
     if (!svc) return Error() << "service " << args[1] << " not found";
-    if (auto result = svc->Start(); !result) {
+    if (auto result = svc->Start(); !result.ok()) {
         return ErrorIgnoreEnoent() << "Could not start service: " << result.error();
     }
     return {};
@@ -845,7 +846,7 @@
 }
 
 static Result<void> do_write(const BuiltinArguments& args) {
-    if (auto result = WriteFile(args[1], args[2]); !result) {
+    if (auto result = WriteFile(args[1], args[2]); !result.ok()) {
         return ErrorIgnoreEnoent()
                << "Unable to write to file '" << args[1] << "': " << result.error();
     }
@@ -903,7 +904,7 @@
         }
         android::base::Timer t;
         if (S_ISREG(sb.st_mode)) {
-            if (auto result = readahead_file(args[1], readfully); !result) {
+            if (auto result = readahead_file(args[1], readfully); !result.ok()) {
                 LOG(WARNING) << "Unable to readahead '" << args[1] << "': " << result.error();
                 _exit(EXIT_FAILURE);
             }
@@ -920,7 +921,7 @@
                  ftsent = fts_read(fts.get())) {
                 if (ftsent->fts_info & FTS_F) {
                     const std::string filename = ftsent->fts_accpath;
-                    if (auto result = readahead_file(filename, readfully); !result) {
+                    if (auto result = readahead_file(filename, readfully); !result.ok()) {
                         LOG(WARNING)
                             << "Unable to readahead '" << filename << "': " << result.error();
                     }
@@ -937,10 +938,10 @@
 
 static Result<void> do_copy(const BuiltinArguments& args) {
     auto file_contents = ReadFile(args[1]);
-    if (!file_contents) {
+    if (!file_contents.ok()) {
         return Error() << "Could not read input file '" << args[1] << "': " << file_contents.error();
     }
-    if (auto result = WriteFile(args[2], *file_contents); !result) {
+    if (auto result = WriteFile(args[2], *file_contents); !result.ok()) {
         return Error() << "Could not write to output file '" << args[2] << "': " << result.error();
     }
 
@@ -949,7 +950,7 @@
 
 static Result<void> do_chown(const BuiltinArguments& args) {
     auto uid = DecodeUid(args[1]);
-    if (!uid) {
+    if (!uid.ok()) {
         return Error() << "Unable to decode UID for '" << args[1] << "': " << uid.error();
     }
 
@@ -959,7 +960,7 @@
 
     if (args.size() == 4) {
         gid = DecodeUid(args[2]);
-        if (!gid) {
+        if (!gid.ok()) {
             return Error() << "Unable to decode GID for '" << args[2] << "': " << gid.error();
         }
     }
@@ -994,7 +995,7 @@
 
 static Result<void> do_restorecon(const BuiltinArguments& args) {
     auto restorecon_info = ParseRestorecon(args.args);
-    if (!restorecon_info) {
+    if (!restorecon_info.ok()) {
         return restorecon_info.error();
     }
 
@@ -1102,7 +1103,7 @@
 static Result<void> ExecWithFunctionOnFailure(const std::vector<std::string>& args,
                                               std::function<void(const std::string&)> function) {
     auto service = Service::MakeTemporaryOneshotService(args);
-    if (!service) {
+    if (!service.ok()) {
         function("MakeTemporaryOneshotService failed: " + service.error().message());
     }
     (*service)->AddReapCallback([function](const siginfo_t& siginfo) {
@@ -1110,7 +1111,7 @@
             function(StringPrintf("Exec service failed, status %d", siginfo.si_status));
         }
     });
-    if (auto result = (*service)->ExecStart(); !result) {
+    if (auto result = (*service)->ExecStart(); !result.ok()) {
         function("ExecStart failed: " + result.error().message());
     }
     ServiceList::GetInstance().AddService(std::move(*service));
@@ -1118,19 +1119,29 @@
 }
 
 static Result<void> ExecVdcRebootOnFailure(const std::string& vdc_arg) {
+    bool should_reboot_into_recovery = true;
     auto reboot_reason = vdc_arg + "_failed";
+    if (android::sysprop::InitProperties::userspace_reboot_in_progress().value_or(false)) {
+        should_reboot_into_recovery = false;
+        reboot_reason = "userspace_failed," + vdc_arg;
+    }
 
-    auto reboot = [reboot_reason](const std::string& message) {
+    auto reboot = [reboot_reason, should_reboot_into_recovery](const std::string& message) {
         // TODO (b/122850122): support this in gsi
-        if (fscrypt_is_native() && !android::gsi::IsGsiRunning()) {
-            LOG(ERROR) << message << ": Rebooting into recovery, reason: " << reboot_reason;
-            if (auto result = reboot_into_recovery(
-                        {"--prompt_and_wipe_data", "--reason="s + reboot_reason});
-                !result) {
-                LOG(FATAL) << "Could not reboot into recovery: " << result.error();
+        if (should_reboot_into_recovery) {
+            if (fscrypt_is_native() && !android::gsi::IsGsiRunning()) {
+                LOG(ERROR) << message << ": Rebooting into recovery, reason: " << reboot_reason;
+                if (auto result = reboot_into_recovery(
+                            {"--prompt_and_wipe_data", "--reason="s + reboot_reason});
+                    !result.ok()) {
+                    LOG(FATAL) << "Could not reboot into recovery: " << result.error();
+                }
+            } else {
+                LOG(ERROR) << "Failure (reboot suppressed): " << reboot_reason;
             }
         } else {
-            LOG(ERROR) << "Failure (reboot suppressed): " << reboot_reason;
+            LOG(ERROR) << message << ": rebooting, reason: " << reboot_reason;
+            trigger_shutdown("reboot," + reboot_reason);
         }
     };
 
@@ -1149,9 +1160,9 @@
     }
     // TODO(b/135984674): check that fstab contains /data.
     if (auto rc = fs_mgr_remount_userdata_into_checkpointing(&fstab); rc < 0) {
-        trigger_shutdown("reboot,mount-userdata-failed");
+        trigger_shutdown("reboot,mount_userdata_failed");
     }
-    if (auto result = queue_fs_event(initial_mount_fstab_return_code, true); !result) {
+    if (auto result = queue_fs_event(initial_mount_fstab_return_code, true); !result.ok()) {
         return Error() << "queue_fs_event() failed: " << result.error();
     }
     return {};
@@ -1179,7 +1190,7 @@
 
 static Result<void> GenerateLinkerConfiguration() {
     const char* linkerconfig_binary = "/system/bin/linkerconfig";
-    const char* linkerconfig_target = "/linkerconfig/ld.config.txt";
+    const char* linkerconfig_target = "/linkerconfig";
     const char* arguments[] = {linkerconfig_binary, "--target", linkerconfig_target};
 
     if (logwrap_fork_execvp(arraysize(arguments), arguments, nullptr, false, LOG_KLOG, false,
@@ -1187,11 +1198,6 @@
         return ErrnoError() << "failed to execute linkerconfig";
     }
 
-    mode_t mode = get_mode("0444");
-    if (fchmodat(AT_FDCWD, linkerconfig_target, mode, AT_SYMLINK_NOFOLLOW) < 0) {
-        return ErrnoErrorIgnoreEnoent() << "fchmodat() failed";
-    }
-
     LOG(INFO) << "linkerconfig generated " << linkerconfig_target
               << " with mounted APEX modules info";
 
@@ -1271,9 +1277,7 @@
         if (strchr(name, '@') != nullptr) continue;
 
         auto path = "/data/misc/apexdata/" + std::string(name);
-        auto system_uid = DecodeUid("system");
-        auto options =
-                MkdirOptions{path, 0700, *system_uid, *system_uid, FscryptAction::kNone, "ref"};
+        auto options = MkdirOptions{path, 0771, AID_ROOT, AID_SYSTEM, FscryptAction::kNone, "ref"};
         make_dir_with_options(options);
     }
     return {};
@@ -1281,16 +1285,16 @@
 
 static Result<void> do_perform_apex_config(const BuiltinArguments& args) {
     auto create_dirs = create_apex_data_dirs();
-    if (!create_dirs) {
+    if (!create_dirs.ok()) {
         return create_dirs.error();
     }
     auto parse_configs = parse_apex_configs();
-    if (!parse_configs) {
+    if (!parse_configs.ok()) {
         return parse_configs.error();
     }
 
     auto update_linker_config = do_update_linker_config(args);
-    if (!update_linker_config) {
+    if (!update_linker_config.ok()) {
         return update_linker_config.error();
     }
 
@@ -1305,17 +1309,6 @@
     }
 }
 
-static Result<void> do_finish_userspace_reboot(const BuiltinArguments&) {
-    LOG(INFO) << "Userspace reboot successfully finished";
-    boot_clock::time_point now = boot_clock::now();
-    SetProperty("sys.init.userspace_reboot.last_finished",
-                std::to_string(now.time_since_epoch().count()));
-    if (!android::sysprop::InitProperties::userspace_reboot_in_progress(false)) {
-        return Error() << "Failed to set sys.init.userspace_reboot.in_progress property";
-    }
-    return {};
-}
-
 // Builtin-function-map start
 const BuiltinFunctionMap& GetBuiltinFunctionMap() {
     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
@@ -1337,7 +1330,6 @@
         {"exec_background",         {1,     kMax, {false,  do_exec_background}}},
         {"exec_start",              {1,     1,    {false,  do_exec_start}}},
         {"export",                  {2,     2,    {false,  do_export}}},
-        {"finish_userspace_reboot", {0,     0,    {false,  do_finish_userspace_reboot}}},
         {"hostname",                {1,     1,    {true,   do_hostname}}},
         {"ifup",                    {1,     1,    {true,   do_ifup}}},
         {"init_user0",              {0,     0,    {false,  do_init_user0}}},
diff --git a/init/check_builtins.cpp b/init/check_builtins.cpp
index bef6966..d62ecb0 100644
--- a/init/check_builtins.cpp
+++ b/init/check_builtins.cpp
@@ -52,7 +52,7 @@
 Result<void> check_chown(const BuiltinArguments& args) {
     if (!args[1].empty()) {
         auto uid = DecodeUid(args[1]);
-        if (!uid) {
+        if (!uid.ok()) {
             return Error() << "Unable to decode UID for '" << args[1] << "': " << uid.error();
         }
     }
@@ -60,7 +60,7 @@
     // GID is optional and pushes the index of path out by one if specified.
     if (args.size() == 4 && !args[2].empty()) {
         auto gid = DecodeUid(args[2]);
-        if (!gid) {
+        if (!gid.ok()) {
             return Error() << "Unable to decode GID for '" << args[2] << "': " << gid.error();
         }
     }
@@ -72,7 +72,7 @@
     ReturnIfAnyArgsEmpty();
 
     auto result = Service::MakeTemporaryOneshotService(args.args);
-    if (!result) {
+    if (!result.ok()) {
         return result.error();
     }
 
@@ -93,7 +93,7 @@
 }
 
 Result<void> check_interface_restart(const BuiltinArguments& args) {
-    if (auto result = IsKnownInterface(args[1]); !result) {
+    if (auto result = IsKnownInterface(args[1]); !result.ok()) {
         return result.error();
     }
     return {};
@@ -124,7 +124,7 @@
 
 Result<void> check_mkdir(const BuiltinArguments& args) {
     auto options = ParseMkdir(args.args);
-    if (!options) {
+    if (!options.ok()) {
         return options.error();
     }
     return {};
@@ -134,7 +134,7 @@
     ReturnIfAnyArgsEmpty();
 
     auto restorecon_info = ParseRestorecon(args.args);
-    if (!restorecon_info) {
+    if (!restorecon_info.ok()) {
         return restorecon_info.error();
     }
 
@@ -157,7 +157,7 @@
     }
 
     if (!value.empty()) {
-        if (auto result = IsLegalPropertyValue(name, value); !result) {
+        if (auto result = IsLegalPropertyValue(name, value); !result.ok()) {
             return result.error();
         }
     }
@@ -189,7 +189,7 @@
     ReturnIfAnyArgsEmpty();
 
     auto rlimit = ParseRlimit(args.args);
-    if (!rlimit) return rlimit.error();
+    if (!rlimit.ok()) return rlimit.error();
     return {};
 }
 
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp
index 1dce2d5..dff7b69 100644
--- a/init/firmware_handler.cpp
+++ b/init/firmware_handler.cpp
@@ -165,7 +165,7 @@
 
             auto result =
                     RunExternalHandler(external_handler.handler_path, external_handler.uid, uevent);
-            if (!result) {
+            if (!result.ok()) {
                 LOG(ERROR) << "Using default firmware; External firmware handler failed: "
                            << result.error();
                 return uevent.firmware;
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 9121bac..622e457 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -21,6 +21,8 @@
 #include <unistd.h>
 
 #include <chrono>
+#include <filesystem>
+#include <map>
 #include <memory>
 #include <set>
 #include <string>
@@ -29,11 +31,13 @@
 #include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <fs_avb/fs_avb.h>
 #include <fs_mgr.h>
 #include <fs_mgr_dm_linear.h>
 #include <fs_mgr_overlayfs.h>
+#include <libfiemap/image_manager.h>
 #include <libgsi/libgsi.h>
 #include <liblp/liblp.h>
 #include <libsnapshot/snapshot.h>
@@ -44,8 +48,11 @@
 #include "uevent_listener.h"
 #include "util.h"
 
+using android::base::ReadFileToString;
 using android::base::Split;
+using android::base::StringPrintf;
 using android::base::Timer;
+using android::fiemap::IImageManager;
 using android::fs_mgr::AvbHandle;
 using android::fs_mgr::AvbHandleStatus;
 using android::fs_mgr::AvbHashtreeResult;
@@ -56,7 +63,6 @@
 using android::fs_mgr::ReadFstabFromDt;
 using android::fs_mgr::SkipMountingPartitions;
 using android::fs_mgr::TransformFstabForDsu;
-using android::init::WriteFile;
 using android::snapshot::SnapshotManager;
 
 using namespace std::literals;
@@ -93,7 +99,12 @@
     bool IsDmLinearEnabled();
     void GetDmLinearMetadataDevice(std::set<std::string>* devices);
     bool InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata);
-    void UseGsiIfPresent();
+    void UseDsuIfPresent();
+    // Reads all fstab.avb_keys from the ramdisk for first-stage mount.
+    void PreloadAvbKeys();
+    // Copies /avb/*.avbpubkey used for DSU from the ramdisk to /metadata for key
+    // revocation check by DSU installation service.
+    void CopyDsuAvbKeys();
 
     ListenerAction UeventCallback(const Uevent& uevent, std::set<std::string>* required_devices);
 
@@ -102,13 +113,16 @@
     virtual bool SetUpDmVerity(FstabEntry* fstab_entry) = 0;
 
     bool need_dm_verity_;
-    bool gsi_not_on_userdata_ = false;
+    bool dsu_not_on_userdata_ = false;
 
     Fstab fstab_;
     std::string lp_metadata_partition_;
     std::string super_partition_name_;
     std::unique_ptr<DeviceHandler> device_handler_;
     UeventListener uevent_listener_;
+    // Reads all AVB keys before chroot into /system, as they might be used
+    // later when mounting other partitions, e.g., /vendor and /product.
+    std::map<std::string, std::vector<std::string>> preload_avb_key_blobs_;
 };
 
 class FirstStageMountVBootV1 : public FirstStageMount {
@@ -507,11 +521,57 @@
     return mounted;
 }
 
+void FirstStageMount::PreloadAvbKeys() {
+    for (const auto& entry : fstab_) {
+        // No need to cache the key content if it's empty, or is already cached.
+        if (entry.avb_keys.empty() || preload_avb_key_blobs_.count(entry.avb_keys)) {
+            continue;
+        }
+
+        // Determines all key paths first.
+        std::vector<std::string> key_paths;
+        if (is_dir(entry.avb_keys.c_str())) {  // fstab_keys might be a dir, e.g., /avb.
+            const char* avb_key_dir = entry.avb_keys.c_str();
+            std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(avb_key_dir), closedir);
+            if (!dir) {
+                LOG(ERROR) << "Failed to opendir: " << dir;
+                continue;
+            }
+            // Gets all key pathes under the dir.
+            struct dirent* de;
+            while ((de = readdir(dir.get()))) {
+                if (de->d_type != DT_REG) continue;
+                std::string full_path = StringPrintf("%s/%s", avb_key_dir, de->d_name);
+                key_paths.emplace_back(std::move(full_path));
+            }
+            std::sort(key_paths.begin(), key_paths.end());
+        } else {
+            // avb_keys are key paths separated by ":", if it's not a dir.
+            key_paths = Split(entry.avb_keys, ":");
+        }
+
+        // Reads the key content then cache it.
+        std::vector<std::string> key_blobs;
+        for (const auto& path : key_paths) {
+            std::string key_value;
+            if (!ReadFileToString(path, &key_value)) {
+                continue;
+            }
+            key_blobs.emplace_back(std::move(key_value));
+        }
+
+        // Maps entry.avb_keys to actual key blobs.
+        preload_avb_key_blobs_[entry.avb_keys] = std::move(key_blobs);
+    }
+}
+
 // If system is in the fstab then we're not a system-as-root device, and in
 // this case, we mount system first then pivot to it.  From that point on,
 // we are effectively identical to a system-as-root device.
 bool FirstStageMount::TrySwitchSystemAsRoot() {
-    UseGsiIfPresent();
+    UseDsuIfPresent();
+    // Preloading all AVB keys from the ramdisk before switching root to /system.
+    PreloadAvbKeys();
 
     auto system_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
         return entry.mount_point == "/system";
@@ -520,7 +580,7 @@
     if (system_partition == fstab_.end()) return true;
 
     if (MountPartition(system_partition, false /* erase_same_mounts */)) {
-        if (gsi_not_on_userdata_ && fs_mgr_verity_is_check_at_most_once(*system_partition)) {
+        if (dsu_not_on_userdata_ && fs_mgr_verity_is_check_at_most_once(*system_partition)) {
             LOG(ERROR) << "check_most_at_once forbidden on external media";
             return false;
         }
@@ -540,7 +600,12 @@
         return entry.mount_point == "/metadata";
     });
     if (metadata_partition != fstab_.end()) {
-        MountPartition(metadata_partition, true /* erase_same_mounts */);
+        if (MountPartition(metadata_partition, true /* erase_same_mounts */)) {
+            // Copies DSU AVB keys from the ramdisk to /metadata.
+            // Must be done before the following TrySwitchSystemAsRoot().
+            // Otherwise, ramdisk will be inaccessible after switching root.
+            CopyDsuAvbKeys();
+        }
     }
 
     if (!CreateLogicalPartitions()) return false;
@@ -556,6 +621,14 @@
             continue;
         }
 
+        // Skip raw partition entries such as boot, dtbo, etc.
+        // Having emmc fstab entries allows us to probe current->vbmeta_partition
+        // in InitDevices() when they are AVB chained partitions.
+        if (current->fs_type == "emmc") {
+            ++current;
+            continue;
+        }
+
         Fstab::iterator end;
         if (!MountPartition(current, false /* erase_same_mounts */, &end)) {
             if (current->fs_mgr_flags.no_fail) {
@@ -582,63 +655,85 @@
     }
 
     // heads up for instantiating required device(s) for overlayfs logic
-    const auto devices = fs_mgr_overlayfs_required_devices(&fstab_);
-    for (auto const& device : devices) {
-        if (android::base::StartsWith(device, "/dev/block/by-name/")) {
-            InitRequiredDevices({basename(device.c_str())});
-        } else {
-            InitMappedDevice(device);
+    auto init_devices = [this](std::set<std::string> devices) -> bool {
+        for (auto iter = devices.begin(); iter != devices.end();) {
+            if (android::base::StartsWith(*iter, "/dev/block/dm-")) {
+                if (!InitMappedDevice(*iter)) return false;
+                iter = devices.erase(iter);
+            } else {
+                iter++;
+            }
         }
-    }
+        return InitRequiredDevices(std::move(devices));
+    };
+    MapScratchPartitionIfNeeded(&fstab_, init_devices);
 
     fs_mgr_overlayfs_mount_all(&fstab_);
 
     return true;
 }
 
-void FirstStageMount::UseGsiIfPresent() {
+// Preserves /avb/*.avbpubkey to /metadata/gsi/dsu/avb/, so they can be used for
+// key revocation check by DSU installation service.  Note that failing to
+// copy files to /metadata is NOT fatal, because it is auxiliary to perform
+// public key matching before booting into DSU images on next boot. The actual
+// public key matching will still be done on next boot to DSU.
+void FirstStageMount::CopyDsuAvbKeys() {
+    std::error_code ec;
+    // Removing existing keys in gsi::kDsuAvbKeyDir as they might be stale.
+    std::filesystem::remove_all(gsi::kDsuAvbKeyDir, ec);
+    if (ec) {
+        LOG(ERROR) << "Failed to remove directory " << gsi::kDsuAvbKeyDir << ": " << ec.message();
+    }
+    // Copy keys from the ramdisk /avb/* to gsi::kDsuAvbKeyDir.
+    static constexpr char kRamdiskAvbKeyDir[] = "/avb";
+    std::filesystem::copy(kRamdiskAvbKeyDir, gsi::kDsuAvbKeyDir, ec);
+    if (ec) {
+        LOG(ERROR) << "Failed to copy " << kRamdiskAvbKeyDir << " into " << gsi::kDsuAvbKeyDir
+                   << ": " << ec.message();
+    }
+}
+
+void FirstStageMount::UseDsuIfPresent() {
     std::string error;
 
     if (!android::gsi::CanBootIntoGsi(&error)) {
-        LOG(INFO) << "GSI " << error << ", proceeding with normal boot";
+        LOG(INFO) << "DSU " << error << ", proceeding with normal boot";
         return;
     }
 
-    auto metadata = android::fs_mgr::ReadFromImageFile(gsi::kDsuLpMetadataFile);
-    if (!metadata) {
-        LOG(ERROR) << "GSI partition layout could not be read";
+    auto init_devices = [this](std::set<std::string> devices) -> bool {
+        if (devices.count("userdata") == 0 || devices.size() > 1) {
+            dsu_not_on_userdata_ = true;
+        }
+        return InitRequiredDevices(std::move(devices));
+    };
+    std::string active_dsu;
+    if (!gsi::GetActiveDsu(&active_dsu)) {
+        LOG(ERROR) << "Failed to GetActiveDsu";
         return;
     }
-
-    if (!InitDmLinearBackingDevices(*metadata.get())) {
-        return;
-    }
-
-    // Find the super name. PartitionOpener will ensure this translates to the
-    // correct block device path.
-    auto super = GetMetadataSuperBlockDevice(*metadata.get());
-    auto super_name = android::fs_mgr::GetBlockDevicePartitionName(*super);
-    if (!android::fs_mgr::CreateLogicalPartitions(*metadata.get(), super_name)) {
-        LOG(ERROR) << "GSI partition layout could not be instantiated";
+    LOG(INFO) << "DSU slot: " << active_dsu;
+    auto images = IImageManager::Open("dsu/" + active_dsu, 0ms);
+    if (!images || !images->MapAllImages(init_devices)) {
+        LOG(ERROR) << "DSU partition layout could not be instantiated";
         return;
     }
 
     if (!android::gsi::MarkSystemAsGsi()) {
-        PLOG(ERROR) << "GSI indicator file could not be written";
+        PLOG(ERROR) << "DSU indicator file could not be written";
         return;
     }
 
     std::string lp_names = "";
     std::vector<std::string> dsu_partitions;
-    for (auto&& partition : metadata->partitions) {
-        auto name = fs_mgr::GetPartitionName(partition);
+    for (auto&& name : images->GetAllBackingImages()) {
         dsu_partitions.push_back(name);
         lp_names += name + ",";
     }
     // Publish the logical partition names for TransformFstabForDsu
     WriteFile(gsi::kGsiLpNamesFile, lp_names);
     TransformFstabForDsu(&fstab_, dsu_partitions);
-    gsi_not_on_userdata_ = (super_name != "userdata");
 }
 
 bool FirstStageMountVBootV1::GetDmVerityDevices(std::set<std::string>* devices) {
@@ -766,7 +861,8 @@
                        << fstab_entry->mount_point;
             return true;  // Returns true to mount the partition directly.
         } else {
-            auto avb_standalone_handle = AvbHandle::LoadAndVerifyVbmeta(*fstab_entry);
+            auto avb_standalone_handle = AvbHandle::LoadAndVerifyVbmeta(
+                    *fstab_entry, preload_avb_key_blobs_[fstab_entry->avb_keys]);
             if (!avb_standalone_handle) {
                 LOG(ERROR) << "Failed to load offline vbmeta for " << fstab_entry->mount_point;
                 // Fallbacks to built-in hashtree if fs_mgr_flags.avb is set.
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index 22de846..0bd4df4 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -248,7 +248,7 @@
     }
 
     auto interface_inheritance_hierarchy_map = ReadInterfaceInheritanceHierarchy();
-    if (!interface_inheritance_hierarchy_map) {
+    if (!interface_inheritance_hierarchy_map.ok()) {
         LOG(ERROR) << interface_inheritance_hierarchy_map.error();
         return EXIT_FAILURE;
     }
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
index 1a43508..e4b25ca 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -30,7 +30,7 @@
     }
 
     auto conf_file = ExpandProps(args[1]);
-    if (!conf_file) {
+    if (!conf_file.ok()) {
         return Error() << "Could not expand import: " << conf_file.error();
     }
 
diff --git a/init/init.cpp b/init/init.cpp
index 5f97e44..5bf1b36 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -237,7 +237,7 @@
 
         auto restart_time = s->time_started() + s->restart_period();
         if (boot_clock::now() > restart_time) {
-            if (auto result = s->Start(); !result) {
+            if (auto result = s->Start(); !result.ok()) {
                 LOG(ERROR) << "Could not restart process '" << s->name() << "': " << result.error();
             }
         } else {
@@ -333,7 +333,7 @@
         return false;
     }
 
-    if (auto result = function.action(svc); !result) {
+    if (auto result = function.action(svc); !result.ok()) {
         LOG(ERROR) << "Control message: Could not ctl." << msg << " for '" << name
                    << "' from pid: " << pid << " (" << process_cmdline << "): " << result.error();
         return false;
@@ -478,7 +478,7 @@
         PLOG(FATAL) << "failed to create signalfd";
     }
 
-    if (auto result = epoll->RegisterHandler(signal_fd, HandleSignalFd); !result) {
+    if (auto result = epoll->RegisterHandler(signal_fd, HandleSignalFd); !result.ok()) {
         LOG(FATAL) << result.error();
     }
 }
@@ -499,7 +499,7 @@
             found = true;
             LOG(INFO) << "Starting service '" << svc->name() << "' from keychord "
                       << android::base::Join(keycodes, ' ');
-            if (auto result = svc->Start(); !result) {
+            if (auto result = svc->Start(); !result.ok()) {
                 LOG(ERROR) << "Could not start service '" << svc->name() << "' from keychord "
                            << android::base::Join(keycodes, ' ') << ": " << result.error();
             }
@@ -558,7 +558,7 @@
 void SendLoadPersistentPropertiesMessage() {
     auto init_message = InitMessage{};
     init_message.set_load_persistent_properties(true);
-    if (auto result = SendMessage(property_fd, init_message); !result) {
+    if (auto result = SendMessage(property_fd, init_message); !result.ok()) {
         LOG(ERROR) << "Failed to send load persistent properties message: " << result.error();
     }
 }
@@ -566,7 +566,7 @@
 void SendStopSendingMessagesMessage() {
     auto init_message = InitMessage{};
     init_message.set_stop_sending_messages(true);
-    if (auto result = SendMessage(property_fd, init_message); !result) {
+    if (auto result = SendMessage(property_fd, init_message); !result.ok()) {
         LOG(ERROR) << "Failed to send 'stop sending messages' message: " << result.error();
     }
 }
@@ -574,14 +574,14 @@
 void SendStartSendingMessagesMessage() {
     auto init_message = InitMessage{};
     init_message.set_start_sending_messages(true);
-    if (auto result = SendMessage(property_fd, init_message); !result) {
+    if (auto result = SendMessage(property_fd, init_message); !result.ok()) {
         LOG(ERROR) << "Failed to send 'start sending messages' message: " << result.error();
     }
 }
 
 static void HandlePropertyFd() {
     auto message = ReadMessage(property_fd);
-    if (!message) {
+    if (!message.ok()) {
         LOG(ERROR) << "Could not read message from property service: " << message.error();
         return;
     }
@@ -636,7 +636,7 @@
     // Set init and its forked children's oom_adj.
     if (auto result =
                 WriteFile("/proc/1/oom_score_adj", StringPrintf("%d", DEFAULT_OOM_SCORE_ADJUST));
-        !result) {
+        !result.ok()) {
         LOG(ERROR) << "Unable to write " << DEFAULT_OOM_SCORE_ADJUST
                    << " to /proc/1/oom_score_adj: " << result.error();
     }
@@ -679,14 +679,14 @@
     SelinuxRestoreContext();
 
     Epoll epoll;
-    if (auto result = epoll.Open(); !result) {
+    if (auto result = epoll.Open(); !result.ok()) {
         PLOG(FATAL) << result.error();
     }
 
     InstallSignalFdHandler(&epoll);
 
     StartPropertyService(&property_fd);
-    if (auto result = epoll.RegisterHandler(property_fd, HandlePropertyFd); !result) {
+    if (auto result = epoll.RegisterHandler(property_fd, HandlePropertyFd); !result.ok()) {
         LOG(FATAL) << "Could not register epoll handler for property fd: " << result.error();
     }
 
@@ -730,8 +730,8 @@
     }
 
     am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
-
     am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
+    am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
     am.QueueEventTrigger("early-init");
 
     // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
@@ -780,24 +780,24 @@
         if (!(waiting_for_prop || Service::is_exec_service_running())) {
             am.ExecuteOneCommand();
         }
-        if (!(waiting_for_prop || Service::is_exec_service_running())) {
-            if (!IsShuttingDown()) {
-                auto next_process_action_time = HandleProcessActions();
+        if (!IsShuttingDown()) {
+            auto next_process_action_time = HandleProcessActions();
 
-                // If there's a process that needs restarting, wake up in time for that.
-                if (next_process_action_time) {
-                    epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
-                            *next_process_action_time - boot_clock::now());
-                    if (*epoll_timeout < 0ms) epoll_timeout = 0ms;
-                }
+            // If there's a process that needs restarting, wake up in time for that.
+            if (next_process_action_time) {
+                epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
+                        *next_process_action_time - boot_clock::now());
+                if (*epoll_timeout < 0ms) epoll_timeout = 0ms;
             }
+        }
 
+        if (!(waiting_for_prop || Service::is_exec_service_running())) {
             // If there's more work to do, wake up again immediately.
             if (am.HasMoreCommands()) epoll_timeout = 0ms;
         }
 
         auto pending_functions = epoll.Wait(epoll_timeout);
-        if (!pending_functions) {
+        if (!pending_functions.ok()) {
             LOG(ERROR) << pending_functions.error();
         } else if (!pending_functions->empty()) {
             // We always reap children before responding to the other pending functions. This is to
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 9f63e4f..caf3e03 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -204,9 +204,9 @@
                                "execute 3";
     // clang-format on
     // WriteFile() ensures the right mode is set
-    ASSERT_TRUE(WriteFile(std::string(dir.path) + "/a.rc", dir_a_script));
+    ASSERT_RESULT_OK(WriteFile(std::string(dir.path) + "/a.rc", dir_a_script));
 
-    ASSERT_TRUE(WriteFile(std::string(dir.path) + "/b.rc", "on boot\nexecute 5"));
+    ASSERT_RESULT_OK(WriteFile(std::string(dir.path) + "/b.rc", "on boot\nexecute 5"));
 
     // clang-format off
     std::string start_script = "import " + std::string(first_import.path) + "\n"
diff --git a/init/keychords.cpp b/init/keychords.cpp
index 5f2682b..adec383 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -187,7 +187,7 @@
         LambdaCheck();
     }
     if (auto result = epoll_->RegisterHandler(fd, [this, fd]() { this->LambdaHandler(fd); });
-        !result) {
+        !result.ok()) {
         LOG(WARNING) << "Could not register keychord epoll handler: " << result.error();
         return false;
     }
@@ -272,7 +272,7 @@
     if (inotify_fd_ >= 0) {
         if (auto result =
                     epoll_->RegisterHandler(inotify_fd_, [this]() { this->InotifyHandler(); });
-            !result) {
+            !result.ok()) {
             LOG(WARNING) << "Could not register keychord epoll handler: " << result.error();
         }
     }
diff --git a/init/keychords_test.cpp b/init/keychords_test.cpp
index 6e9b337..8a333a2 100644
--- a/init/keychords_test.cpp
+++ b/init/keychords_test.cpp
@@ -204,7 +204,7 @@
 
 TestFrame::TestFrame(const std::vector<const std::vector<int>>& chords, EventHandler* ev)
     : ev_(ev) {
-    if (!epoll_.Open()) return;
+    if (!epoll_.Open().ok()) return;
     for (const auto& keycodes : chords) keychords_.Register(keycodes);
     keychords_.Start(&epoll_, [this](const std::vector<int>& keycodes) {
         this->keycodes_.emplace_back(keycodes);
@@ -213,7 +213,7 @@
 
 void TestFrame::RelaxForMs(std::chrono::milliseconds wait) {
     auto pending_functions = epoll_.Wait(wait);
-    ASSERT_TRUE(pending_functions) << pending_functions.error();
+    ASSERT_RESULT_OK(pending_functions);
     for (const auto& function : *pending_functions) {
         (*function)();
     }
diff --git a/init/mount_handler.cpp b/init/mount_handler.cpp
index 0e4e024..01abba8 100644
--- a/init/mount_handler.cpp
+++ b/init/mount_handler.cpp
@@ -116,7 +116,7 @@
     if (!fp_) PLOG(FATAL) << "Could not open /proc/mounts";
     auto result = epoll->RegisterHandler(
             fileno(fp_.get()), [this]() { this->MountHandlerFunction(); }, EPOLLERR | EPOLLPRI);
-    if (!result) LOG(FATAL) << result.error();
+    if (!result.ok()) LOG(FATAL) << result.error();
 }
 
 MountHandler::~MountHandler() {
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index 93eb244..0749fe3 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -35,6 +35,19 @@
 namespace init {
 namespace {
 
+static bool BindMount(const std::string& source, const std::string& mount_point,
+                      bool recursive = false) {
+    unsigned long mountflags = MS_BIND;
+    if (recursive) {
+        mountflags |= MS_REC;
+    }
+    if (mount(source.c_str(), mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
+        PLOG(ERROR) << "Failed to bind mount " << source;
+        return false;
+    }
+    return true;
+}
+
 static bool MakeShared(const std::string& mount_point, bool recursive = false) {
     unsigned long mountflags = MS_SHARED;
     if (recursive) {
@@ -47,6 +60,18 @@
     return true;
 }
 
+static bool MakeSlave(const std::string& mount_point, bool recursive = false) {
+    unsigned long mountflags = MS_SLAVE;
+    if (recursive) {
+        mountflags |= MS_REC;
+    }
+    if (mount(nullptr, mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
+        PLOG(ERROR) << "Failed to change propagation type to slave";
+        return false;
+    }
+    return true;
+}
+
 static bool MakePrivate(const std::string& mount_point, bool recursive = false) {
     unsigned long mountflags = MS_PRIVATE;
     if (recursive) {
@@ -82,7 +107,7 @@
 }
 
 static Result<void> MountDir(const std::string& path, const std::string& mount_path) {
-    if (int ret = mkdir(mount_path.c_str(), 0755); ret != 0 && ret != EEXIST) {
+    if (int ret = mkdir(mount_path.c_str(), 0755); ret != 0 && errno != EEXIST) {
         return ErrnoError() << "Could not create mount point " << mount_path;
     }
     if (mount(path.c_str(), mount_path.c_str(), nullptr, MS_BIND, nullptr) != 0) {
@@ -116,12 +141,12 @@
         if (entry->d_type == DT_DIR) {
             const std::string apex_path = from_dir + "/" + entry->d_name;
             const auto apex_name = GetApexName(apex_path);
-            if (!apex_name) {
+            if (!apex_name.ok()) {
                 LOG(ERROR) << apex_path << " is not an APEX directory: " << apex_name.error();
                 continue;
             }
             const std::string mount_path = to_dir + "/" + (*apex_name);
-            if (auto result = MountDir(apex_path, mount_path); !result) {
+            if (auto result = MountDir(apex_path, mount_path); !result.ok()) {
                 return result;
             }
         }
@@ -143,7 +168,7 @@
     };
 
     for (const auto& dir : kBuiltinDirsForApexes) {
-        if (auto result = ActivateFlattenedApexesFrom(dir, kApexTop); !result) {
+        if (auto result = ActivateFlattenedApexesFrom(dir, kApexTop); !result.ok()) {
             LOG(ERROR) << result.error();
             return false;
         }
@@ -191,6 +216,39 @@
     // namespace
     if (!(MakePrivate("/linkerconfig"))) return false;
 
+    // The two mount namespaces present challenges for scoped storage, because
+    // vold, which is responsible for most of the mounting, lives in the
+    // bootstrap mount namespace, whereas most other daemons and all apps live
+    // in the default namespace.  Scoped storage has a need for a
+    // /mnt/installer view that is a slave bind mount of /mnt/user - in other
+    // words, all mounts under /mnt/user should automatically show up under
+    // /mnt/installer. However, additional mounts done under /mnt/installer
+    // should not propagate back to /mnt/user. In a single mount namespace
+    // this is easy to achieve, by simply marking the /mnt/installer a slave
+    // bind mount. Unfortunately, if /mnt/installer is only created and
+    // bind mounted after the two namespaces are created below, we end up
+    // with the following situation:
+    // /mnt/user and /mnt/installer share the same peer group in both the
+    // bootstrap and default namespaces. Marking /mnt/installer slave in either
+    // namespace means that it won't propagate events to the /mnt/installer in
+    // the other namespace, which is still something we require - vold is the
+    // one doing the mounting under /mnt/installer, and those mounts should
+    // show up in the default namespace as well.
+    //
+    // The simplest solution is to do the bind mount before the two namespaces
+    // are created: the effect is that in both namespaces, /mnt/installer is a
+    // 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.
+    if (!mkdir_recursive("/mnt/user", 0755)) return false;
+    if (!mkdir_recursive("/mnt/installer", 0755)) return false;
+    if (!(BindMount("/mnt/user", "/mnt/installer", true))) return false;
+    // First, make /mnt/installer a slave bind mount
+    if (!(MakeSlave("/mnt/installer"))) 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;
+
     bootstrap_ns_fd.reset(OpenMountNamespace());
     bootstrap_ns_id = GetMountNamespaceId();
 
@@ -237,7 +295,7 @@
             return false;
         }
 
-        if (auto result = MountLinkerConfigForDefaultNamespace(); !result) {
+        if (auto result = MountLinkerConfigForDefaultNamespace(); !result.ok()) {
             LOG(ERROR) << result.error();
             return false;
         }
diff --git a/init/parser.cpp b/init/parser.cpp
index 6ab61cb..507ee4a 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -61,7 +61,7 @@
         bad_section_found = false;
         if (section_parser == nullptr) return;
 
-        if (auto result = section_parser->EndSection(); !result) {
+        if (auto result = section_parser->EndSection(); !result.ok()) {
             parse_error_count_++;
             LOG(ERROR) << filename << ": " << section_start_line << ": " << result.error();
         }
@@ -92,7 +92,7 @@
                 if (line_callback != line_callbacks_.end()) {
                     end_section();
 
-                    if (auto result = line_callback->second(std::move(args)); !result) {
+                    if (auto result = line_callback->second(std::move(args)); !result.ok()) {
                         parse_error_count_++;
                         LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                     }
@@ -101,8 +101,8 @@
                     section_parser = section_parsers_[args[0]].get();
                     section_start_line = state.line;
                     if (auto result =
-                            section_parser->ParseSection(std::move(args), filename, state.line);
-                        !result) {
+                                section_parser->ParseSection(std::move(args), filename, state.line);
+                        !result.ok()) {
                         parse_error_count_++;
                         LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                         section_parser = nullptr;
@@ -110,7 +110,7 @@
                     }
                 } else if (section_parser) {
                     if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
-                        !result) {
+                        !result.ok()) {
                         parse_error_count_++;
                         LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                     }
@@ -143,7 +143,7 @@
     LOG(INFO) << "Parsing file " << path << "...";
     android::base::Timer t;
     auto config_contents = ReadFile(path);
-    if (!config_contents) {
+    if (!config_contents.ok()) {
         LOG(INFO) << "Unable to read config file '" << path << "': " << config_contents.error();
         return false;
     }
diff --git a/init/persistent_properties.cpp b/init/persistent_properties.cpp
index 1758cfa..716f62e 100644
--- a/init/persistent_properties.cpp
+++ b/init/persistent_properties.cpp
@@ -149,7 +149,7 @@
         unlink(temp_filename.c_str());
     }
     auto file_contents = ReadFile(persistent_property_filename);
-    if (!file_contents) {
+    if (!file_contents.ok()) {
         return Error() << "Unable to read persistent property file: " << file_contents.error();
     }
     return *file_contents;
@@ -159,7 +159,7 @@
 
 Result<PersistentProperties> LoadPersistentPropertyFile() {
     auto file_contents = ReadPersistentPropertyFile();
-    if (!file_contents) return file_contents.error();
+    if (!file_contents.ok()) return file_contents.error();
 
     PersistentProperties persistent_properties;
     if (persistent_properties.ParseFromString(*file_contents)) return persistent_properties;
@@ -212,7 +212,7 @@
 void WritePersistentProperty(const std::string& name, const std::string& value) {
     auto persistent_properties = LoadPersistentPropertyFile();
 
-    if (!persistent_properties) {
+    if (!persistent_properties.ok()) {
         LOG(ERROR) << "Recovering persistent properties from memory: "
                    << persistent_properties.error();
         persistent_properties = LoadPersistentPropertiesFromMemory();
@@ -227,7 +227,7 @@
         AddPersistentProperty(name, value, &persistent_properties.value());
     }
 
-    if (auto result = WritePersistentPropertyFile(*persistent_properties); !result) {
+    if (auto result = WritePersistentPropertyFile(*persistent_properties); !result.ok()) {
         LOG(ERROR) << "Could not store persistent property: " << result.error();
     }
 }
@@ -235,16 +235,16 @@
 PersistentProperties LoadPersistentProperties() {
     auto persistent_properties = LoadPersistentPropertyFile();
 
-    if (!persistent_properties) {
+    if (!persistent_properties.ok()) {
         LOG(ERROR) << "Could not load single persistent property file, trying legacy directory: "
                    << persistent_properties.error();
         persistent_properties = LoadLegacyPersistentProperties();
-        if (!persistent_properties) {
+        if (!persistent_properties.ok()) {
             LOG(ERROR) << "Unable to load legacy persistent properties: "
                        << persistent_properties.error();
             return {};
         }
-        if (auto result = WritePersistentPropertyFile(*persistent_properties); result) {
+        if (auto result = WritePersistentPropertyFile(*persistent_properties); result.ok()) {
             RemoveLegacyPersistentPropertyFiles();
         } else {
             LOG(ERROR) << "Unable to write single persistent property file: " << result.error();
diff --git a/init/persistent_properties_test.cpp b/init/persistent_properties_test.cpp
index 13796a6..60cecde 100644
--- a/init/persistent_properties_test.cpp
+++ b/init/persistent_properties_test.cpp
@@ -83,7 +83,8 @@
         {"persist.\x00\x01\x02\xFF\xFE\xFD\x7F\x8F\x9F", "non-ascii-name"},
     };
 
-    ASSERT_TRUE(WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
+    ASSERT_RESULT_OK(
+            WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
 
     auto read_back_properties = LoadPersistentProperties();
     CheckPropertiesEqual(persistent_properties, read_back_properties);
@@ -97,7 +98,8 @@
     std::vector<std::pair<std::string, std::string>> persistent_properties = {
         {"persist.sys.timezone", "America/Los_Angeles"},
     };
-    ASSERT_TRUE(WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
+    ASSERT_RESULT_OK(
+            WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
 
     WritePersistentProperty("persist.sys.locale", "pt-BR");
 
@@ -119,7 +121,8 @@
         {"persist.sys.locale", "en-US"},
         {"persist.sys.timezone", "America/Los_Angeles"},
     };
-    ASSERT_TRUE(WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
+    ASSERT_RESULT_OK(
+            WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
 
     WritePersistentProperty("persist.sys.locale", "pt-BR");
 
@@ -137,7 +140,7 @@
     ASSERT_TRUE(tf.fd != -1);
     persistent_property_filename = tf.path;
 
-    ASSERT_TRUE(WriteFile(tf.path, "ab"));
+    ASSERT_RESULT_OK(WriteFile(tf.path, "ab"));
 
     WritePersistentProperty("persist.sys.locale", "pt-BR");
 
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 5b35ad2..84644e8 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -153,7 +153,7 @@
     changed_message->set_name(name);
     changed_message->set_value(value);
 
-    if (auto result = SendMessage(init_socket, property_msg); !result) {
+    if (auto result = SendMessage(init_socket, property_msg); !result.ok()) {
         LOG(ERROR) << "Failed to send property changed message: " << result.error();
     }
 }
@@ -166,7 +166,7 @@
         return PROP_ERROR_INVALID_NAME;
     }
 
-    if (auto result = IsLegalPropertyValue(name, value); !result) {
+    if (auto result = IsLegalPropertyValue(name, value); !result.ok()) {
         *error = result.error().message();
         return PROP_ERROR_INVALID_VALUE;
     }
@@ -392,7 +392,7 @@
         control_message->set_fd(fd);
     }
 
-    if (auto result = SendMessage(init_socket, property_msg); !result) {
+    if (auto result = SendMessage(init_socket, property_msg); !result.ok()) {
         // We've already released the fd above, so if we fail to send the message to init, we need
         // to manually free it here.
         if (fd != -1) {
@@ -670,7 +670,7 @@
             std::string raw_filename(fn);
             auto expanded_filename = ExpandProps(raw_filename);
 
-            if (!expanded_filename) {
+            if (!expanded_filename.ok()) {
                 LOG(ERROR) << "Could not expand filename ': " << expanded_filename.error();
                 continue;
             }
@@ -726,7 +726,7 @@
                                       std::map<std::string, std::string>* properties) {
     Timer t;
     auto file_contents = ReadFile(filename);
-    if (!file_contents) {
+    if (!file_contents.ok()) {
         PLOG(WARNING) << "Couldn't load property file '" << filename
                       << "': " << file_contents.error();
         return false;
@@ -1084,7 +1084,7 @@
 
 static void HandleInitSocket() {
     auto message = ReadMessage(init_socket);
-    if (!message) {
+    if (!message.ok()) {
         LOG(ERROR) << "Could not read message from init_dedicated_recv_socket: " << message.error();
         return;
     }
@@ -1123,21 +1123,22 @@
 
 static void PropertyServiceThread() {
     Epoll epoll;
-    if (auto result = epoll.Open(); !result) {
+    if (auto result = epoll.Open(); !result.ok()) {
         LOG(FATAL) << result.error();
     }
 
-    if (auto result = epoll.RegisterHandler(property_set_fd, handle_property_set_fd); !result) {
+    if (auto result = epoll.RegisterHandler(property_set_fd, handle_property_set_fd);
+        !result.ok()) {
         LOG(FATAL) << result.error();
     }
 
-    if (auto result = epoll.RegisterHandler(init_socket, HandleInitSocket); !result) {
+    if (auto result = epoll.RegisterHandler(init_socket, HandleInitSocket); !result.ok()) {
         LOG(FATAL) << result.error();
     }
 
     while (true) {
         auto pending_functions = epoll.Wait(std::nullopt);
-        if (!pending_functions) {
+        if (!pending_functions.ok()) {
             LOG(ERROR) << pending_functions.error();
         } else {
             for (const auto& function : *pending_functions) {
@@ -1159,7 +1160,8 @@
     accept_messages = true;
 
     if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
-                                   false, 0666, 0, 0, {})) {
+                                   false, 0666, 0, 0, {});
+        result.ok()) {
         property_set_fd = *result;
     } else {
         LOG(FATAL) << "start_property_service socket creation failed: " << result.error();
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 225bc9c..048c1e7 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -96,6 +96,11 @@
     return ret;
 }
 
+static void PersistRebootReason(const char* reason) {
+    SetProperty(LAST_REBOOT_REASON_PROPERTY, reason);
+    WriteStringToFile(reason, LAST_REBOOT_REASON_FILE);
+}
+
 // represents umount status during reboot / shutdown.
 enum UmountStat {
     /* umount succeeded. */
@@ -180,7 +185,7 @@
         LOG(WARNING) << "cannot find blank_screen in TurnOffBacklight";
         return;
     }
-    if (auto result = service->Start(); !result) {
+    if (auto result = service->Start(); !result.ok()) {
         LOG(WARNING) << "Could not start blank_screen service: " << result.error();
     }
 }
@@ -547,7 +552,7 @@
          reasons[1] == "hard" || reasons[1] == "warm")) {
         skip = strlen("reboot,");
     }
-    SetProperty(LAST_REBOOT_REASON_PROPERTY, reason.c_str() + skip);
+    PersistRebootReason(reason.c_str() + skip);
     sync();
 
     bool is_thermal_shutdown = cmd == ANDROID_RB_THERMOFF;
@@ -591,14 +596,14 @@
             // keep debugging tools until non critical ones are all gone.
             s->SetShutdownCritical();
         } else if (to_starts.count(s->name())) {
-            if (auto result = s->Start(); !result) {
+            if (auto result = s->Start(); !result.ok()) {
                 LOG(ERROR) << "Could not start shutdown 'to_start' service '" << s->name()
                            << "': " << result.error();
             }
             s->SetShutdownCritical();
         } else if (s->IsShutdownCritical()) {
             // Start shutdown critical service if not started.
-            if (auto result = s->Start(); !result) {
+            if (auto result = s->Start(); !result.ok()) {
                 LOG(ERROR) << "Could not start shutdown critical service '" << s->name()
                            << "': " << result.error();
             }
@@ -731,22 +736,22 @@
 
 static Result<void> DoUserspaceReboot() {
     LOG(INFO) << "Userspace reboot initiated";
-    boot_clock::time_point now = boot_clock::now();
-    SetProperty("sys.init.userspace_reboot.last_started",
-                std::to_string(now.time_since_epoch().count()));
     auto guard = android::base::make_scope_guard([] {
         // Leave shutdown so that we can handle a full reboot.
         LeaveShutdown();
-        trigger_shutdown("reboot,abort-userspace-reboot");
+        trigger_shutdown("reboot,userspace_failed,shutdown_aborted");
     });
     // Triggering userspace-reboot-requested will result in a bunch of setprop
     // actions. We should make sure, that all of them are propagated before
-    // proceeding with userspace reboot. Synchronously setting kUserspaceRebootInProgress property
-    // is not perfect, but it should do the trick.
+    // proceeding with userspace reboot. Synchronously setting sys.init.userspace_reboot.in_progress
+    // property is not perfect, but it should do the trick.
     if (!android::sysprop::InitProperties::userspace_reboot_in_progress(true)) {
         return Error() << "Failed to set sys.init.userspace_reboot.in_progress property";
     }
     EnterShutdown();
+    if (!SetProperty("sys.powerctl", "")) {
+        return Error() << "Failed to reset sys.powerctl property";
+    }
     std::vector<Service*> stop_first;
     // Remember the services that were enabled. We will need to manually enable them again otherwise
     // triggers like class_start won't restart them.
@@ -773,10 +778,10 @@
         // TODO(b/135984674): store information about offending services for debugging.
         return Error() << r << " post-data services are still running";
     }
-    if (auto result = KillZramBackingDevice(); !result) {
+    if (auto result = KillZramBackingDevice(); !result.ok()) {
         return result;
     }
-    if (auto result = CallVdc("volume", "reset"); !result) {
+    if (auto result = CallVdc("volume", "reset"); !result.ok()) {
         return result;
     }
     if (int r = StopServicesAndLogViolations(GetDebuggingServices(true /* only_post_data */), 5s,
@@ -791,7 +796,7 @@
         sync();
         LOG(INFO) << "sync() took " << sync_timer;
     }
-    if (auto result = UnmountAllApexes(); !result) {
+    if (auto result = UnmountAllApexes(); !result.ok()) {
         return result;
     }
     if (!SwitchToBootstrapMountNamespaceIfNeeded()) {
@@ -828,7 +833,8 @@
     if (!WaitForProperty("sys.boot_completed", "1", timeout)) {
         LOG(ERROR) << "Failed to boot in " << timeout.count() << "ms. Switching to full reboot";
         // In this case device is in a boot loop. Only way to recover is to do dirty reboot.
-        RebootSystem(ANDROID_RB_RESTART2, "userspace-reboot-watchdog-triggered");
+        PersistRebootReason("userspace_failed,watchdog_triggered");
+        RebootSystem(ANDROID_RB_RESTART2, "userspace_failed,watchdog_triggered");
     }
     LOG(INFO) << "Device booted, stopping userspace reboot watchdog";
 }
@@ -844,7 +850,7 @@
     if (pid < 0) {
         PLOG(ERROR) << "Failed to fork process for userspace reboot watchdog. Switching to full "
                     << "reboot";
-        trigger_shutdown("reboot,userspace-reboot-failed-to-fork");
+        trigger_shutdown("reboot,userspace_failed,watchdog_fork");
         return;
     }
     if (pid == 0) {
@@ -860,6 +866,30 @@
     am.QueueBuiltinAction(handler, "userspace-reboot");
 }
 
+/**
+ * Check if "command" field is set in bootloader message.
+ *
+ * If "command" field is broken (contains non-printable characters prior to
+ * terminating zero), it will be zeroed.
+ *
+ * @param[in,out] boot Bootloader message (BCB) structure
+ * @return true if "command" field is already set, and false if it's empty
+ */
+static bool CommandIsPresent(bootloader_message* boot) {
+    if (boot->command[0] == '\0')
+        return false;
+
+    for (size_t i = 0; i < arraysize(boot->command); ++i) {
+        if (boot->command[i] == '\0')
+            return true;
+        if (!isprint(boot->command[i]))
+            break;
+    }
+
+    memset(boot->command, 0, sizeof(boot->command));
+    return false;
+}
+
 void HandlePowerctlMessage(const std::string& command) {
     unsigned int cmd = 0;
     std::vector<std::string> cmd_params = Split(command, ",");
@@ -912,7 +942,7 @@
                 }
                 // Update the boot command field if it's empty, and preserve
                 // the other arguments in the bootloader message.
-                if (boot.command[0] == '\0') {
+                if (!CommandIsPresent(&boot)) {
                     strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
                     if (std::string err; !write_bootloader_message(boot, &err)) {
                         LOG(ERROR) << "Failed to set bootloader message: " << err;
diff --git a/init/reboot_utils.cpp b/init/reboot_utils.cpp
index dac0cf4..485188b 100644
--- a/init/reboot_utils.cpp
+++ b/init/reboot_utils.cpp
@@ -34,12 +34,16 @@
 namespace init {
 
 static std::string init_fatal_reboot_target = "bootloader";
+static bool init_fatal_panic = false;
 
 void SetFatalRebootTarget() {
     std::string cmdline;
     android::base::ReadFileToString("/proc/cmdline", &cmdline);
     cmdline = android::base::Trim(cmdline);
 
+    const char kInitFatalPanicString[] = "androidboot.init_fatal_panic=true";
+    init_fatal_panic = cmdline.find(kInitFatalPanicString) != std::string::npos;
+
     const char kRebootTargetString[] = "androidboot.init_fatal_reboot_target=";
     auto start_pos = cmdline.find(kRebootTargetString);
     if (start_pos == std::string::npos) {
@@ -133,6 +137,9 @@
     for (size_t i = 0; i < backtrace->NumFrames(); i++) {
         LOG(ERROR) << backtrace->FormatFrameData(i);
     }
+    if (init_fatal_panic) {
+        _exit(signal_number);
+    }
     RebootSystem(ANDROID_RB_RESTART2, init_fatal_reboot_target);
 }
 
diff --git a/init/result.h b/init/result.h
index b70dd1b..8c1f91e 100644
--- a/init/result.h
+++ b/init/result.h
@@ -22,8 +22,6 @@
 #include <android-base/result.h>
 
 using android::base::ErrnoError;
-using android::base::ErrnoErrorf;
 using android::base::Error;
-using android::base::Errorf;
 using android::base::Result;
 using android::base::ResultError;
diff --git a/init/rlimit_parser_test.cpp b/init/rlimit_parser_test.cpp
index 6a16d3b..3c3f848 100644
--- a/init/rlimit_parser_test.cpp
+++ b/init/rlimit_parser_test.cpp
@@ -29,7 +29,7 @@
     ASSERT_EQ(4U, input.size());
     auto result = ParseRlimit(input);
 
-    ASSERT_TRUE(result) << "input: " << input[1];
+    ASSERT_TRUE(result.ok()) << "input: " << input[1];
     const auto& [resource, rlimit] = *result;
     const auto& [expected_resource, expected_rlimit] = expected_result;
     EXPECT_EQ(expected_resource, resource);
@@ -42,7 +42,7 @@
     ASSERT_EQ(4U, input.size());
     auto result = ParseRlimit(input);
 
-    ASSERT_FALSE(result) << "input: " << input[1];
+    ASSERT_FALSE(result.ok()) << "input: " << input[1];
     EXPECT_EQ(expected_result, result.error().message());
     EXPECT_EQ(0, result.error().code());
 }
diff --git a/init/security.cpp b/init/security.cpp
index 586d0c7..6cbe642 100644
--- a/init/security.cpp
+++ b/init/security.cpp
@@ -18,14 +18,19 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <linux/perf_event.h>
+#include <sys/ioctl.h>
+#include <sys/syscall.h>
 #include <unistd.h>
 
 #include <fstream>
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android-base/unique_fd.h>
 
 using android::base::unique_fd;
+using android::base::SetProperty;
 
 namespace android {
 namespace init {
@@ -197,5 +202,61 @@
     return {};
 }
 
+// Test for whether the kernel has SELinux hooks for the perf_event_open()
+// syscall. If the hooks are present, we can stop using the other permission
+// mechanism (perf_event_paranoid sysctl), and use only the SELinux policy to
+// control access to the syscall. The hooks are expected on all Android R
+// release kernels, but might be absent on devices that upgrade while keeping an
+// older kernel.
+//
+// There is no direct/synchronous way of finding out that a syscall failed due
+// to SELinux. Therefore we test for a combination of a success and a failure
+// that are explained by the platform's SELinux policy for the "init" domain:
+// * cpu-scoped perf_event is allowed
+// * ioctl() on the event fd is disallowed with EACCES
+//
+// Since init has CAP_SYS_ADMIN, these tests are not affected by the system-wide
+// perf_event_paranoid sysctl.
+//
+// If the SELinux hooks are detected, a special sysprop
+// (sys.init.perf_lsm_hooks) is set, which translates to a modification of
+// perf_event_paranoid (through init.rc sysprop actions).
+//
+// TODO(b/137092007): this entire test can be removed once the platform stops
+// supporting kernels that precede the perf_event_open hooks (Android common
+// kernels 4.4 and 4.9).
+Result<void> TestPerfEventSelinuxAction(const BuiltinArguments&) {
+    // Use a trivial event that will be configured, but not started.
+    struct perf_event_attr pe = {
+            .type = PERF_TYPE_SOFTWARE,
+            .size = sizeof(struct perf_event_attr),
+            .config = PERF_COUNT_SW_TASK_CLOCK,
+            .disabled = 1,
+            .exclude_kernel = 1,
+    };
+
+    // Open the above event targeting cpu 0. (EINTR not possible.)
+    unique_fd fd(static_cast<int>(syscall(__NR_perf_event_open, &pe, /*pid=*/-1,
+                                          /*cpu=*/0,
+                                          /*group_fd=*/-1, /*flags=*/0)));
+    if (fd == -1) {
+        PLOG(ERROR) << "Unexpected perf_event_open error";
+        return {};
+    }
+
+    int ioctl_ret = ioctl(fd, PERF_EVENT_IOC_RESET);
+    if (ioctl_ret != -1) {
+        // Success implies that the kernel doesn't have the hooks.
+        return {};
+    } else if (errno != EACCES) {
+        PLOG(ERROR) << "Unexpected perf_event ioctl error";
+        return {};
+    }
+
+    // Conclude that the SELinux hooks are present.
+    SetProperty("sys.init.perf_lsm_hooks", "1");
+    return {};
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/init/security.h b/init/security.h
index b081a05..43c2739 100644
--- a/init/security.h
+++ b/init/security.h
@@ -29,6 +29,7 @@
 Result<void> MixHwrngIntoLinuxRngAction(const BuiltinArguments&);
 Result<void> SetMmapRndBitsAction(const BuiltinArguments&);
 Result<void> SetKptrRestrictAction(const BuiltinArguments&);
+Result<void> TestPerfEventSelinuxAction(const BuiltinArguments&);
 
 }  // namespace init
 }  // namespace android
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 852d6ca..c5b7576 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -65,6 +65,7 @@
 #include <android-base/parseint.h>
 #include <android-base/unique_fd.h>
 #include <fs_avb/fs_avb.h>
+#include <libgsi/libgsi.h>
 #include <selinux/android.h>
 
 #include "debug_ramdisk.h"
@@ -477,7 +478,7 @@
         }
     }
 
-    if (auto result = WriteFile("/sys/fs/selinux/checkreqprot", "0"); !result) {
+    if (auto result = WriteFile("/sys/fs/selinux/checkreqprot", "0"); !result.ok()) {
         LOG(FATAL) << "Unable to write to /sys/fs/selinux/checkreqprot: " << result.error();
     }
 }
@@ -533,6 +534,8 @@
     selinux_android_restorecon("/apex", 0);
 
     selinux_android_restorecon("/linkerconfig", 0);
+
+    selinux_android_restorecon(gsi::kDsuAvbKeyDir, SELINUX_ANDROID_RESTORECON_RECURSE);
 }
 
 int SelinuxKlogCallback(int type, const char* fmt, ...) {
diff --git a/init/service.cpp b/init/service.cpp
index 0e27ff1..665a1b0 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -106,7 +106,7 @@
     c_strings.push_back(const_cast<char*>(args[0].data()));
     for (std::size_t i = 1; i < args.size(); ++i) {
         auto expanded_arg = ExpandProps(args[i]);
-        if (!expanded_arg) {
+        if (!expanded_arg.ok()) {
             LOG(FATAL) << args[0] << ": cannot expand arguments': " << expanded_arg.error();
         }
         expanded_args[i] = *expanded_arg;
@@ -232,7 +232,7 @@
         }
     }
 
-    if (auto result = SetProcessAttributes(proc_attr_); !result) {
+    if (auto result = SetProcessAttributes(proc_attr_); !result.ok()) {
         LOG(FATAL) << "cannot set attribute for " << name_ << ": " << result.error();
     }
 
@@ -374,7 +374,7 @@
 
     flags_ |= SVC_ONESHOT;
 
-    if (auto result = Start(); !result) {
+    if (auto result = Start(); !result.ok()) {
         return result;
     }
 
@@ -449,7 +449,7 @@
         scon = seclabel_;
     } else {
         auto result = ComputeContextFromExecutable(args_[0]);
-        if (!result) {
+        if (!result.ok()) {
             return result.error();
         }
         scon = *result;
@@ -469,7 +469,7 @@
 
     std::vector<Descriptor> descriptors;
     for (const auto& socket : sockets_) {
-        if (auto result = socket.Create(scon)) {
+        if (auto result = socket.Create(scon); result.ok()) {
             descriptors.emplace_back(std::move(*result));
         } else {
             LOG(INFO) << "Could not create socket '" << socket.name << "': " << result.error();
@@ -477,7 +477,7 @@
     }
 
     for (const auto& file : files_) {
-        if (auto result = file.Create()) {
+        if (auto result = file.Create(); result.ok()) {
             descriptors.emplace_back(std::move(*result));
         } else {
             LOG(INFO) << "Could not open file '" << file.name << "': " << result.error();
@@ -494,7 +494,7 @@
     if (pid == 0) {
         umask(077);
 
-        if (auto result = EnterNamespaces(namespaces_, name_, pre_apexd_); !result) {
+        if (auto result = EnterNamespaces(namespaces_, name_, pre_apexd_); !result.ok()) {
             LOG(FATAL) << "Service '" << name_
                        << "' failed to set up namespaces: " << result.error();
         }
@@ -507,7 +507,7 @@
             descriptor.Publish();
         }
 
-        if (auto result = WritePidToFiles(&writepid_files_); !result) {
+        if (auto result = WritePidToFiles(&writepid_files_); !result.ok()) {
             LOG(ERROR) << "failed to write pid to files: " << result.error();
         }
 
@@ -669,7 +669,7 @@
         StopOrReset(SVC_RESTART);
     } else if (!(flags_ & SVC_RESTARTING)) {
         /* Just start the service since it's not running. */
-        if (auto result = Start(); !result) {
+        if (auto result = Start(); !result.ok()) {
             LOG(ERROR) << "Could not restart '" << name_ << "': " << result.error();
         }
     } /* else: Service is restarting anyways. */
@@ -742,7 +742,7 @@
     Result<uid_t> uid = 0;
     if (command_arg > 3) {
         uid = DecodeUid(args[2]);
-        if (!uid) {
+        if (!uid.ok()) {
             return Error() << "Unable to decode UID for '" << args[2] << "': " << uid.error();
         }
     }
@@ -750,13 +750,13 @@
     std::vector<gid_t> supp_gids;
     if (command_arg > 4) {
         gid = DecodeUid(args[3]);
-        if (!gid) {
+        if (!gid.ok()) {
             return Error() << "Unable to decode GID for '" << args[3] << "': " << gid.error();
         }
         std::size_t nr_supp_gids = command_arg - 1 /* -- */ - 4 /* exec SECLABEL UID GID */;
         for (size_t i = 0; i < nr_supp_gids; ++i) {
             auto supp_gid = DecodeUid(args[4 + i]);
-            if (!supp_gid) {
+            if (!supp_gid.ok()) {
                 return Error() << "Unable to decode GID for '" << args[4 + i]
                                << "': " << supp_gid.error();
             }
diff --git a/init/service_list.cpp b/init/service_list.cpp
index c51a9cf..3047821 100644
--- a/init/service_list.cpp
+++ b/init/service_list.cpp
@@ -86,7 +86,7 @@
             LOG(ERROR) << "delayed service '" << name << "' could not be found.";
             continue;
         }
-        if (auto result = service->Start(); !result) {
+        if (auto result = service->Start(); !result.ok()) {
             LOG(ERROR) << result.error().message();
         }
     }
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 1d431e3..4b04ba0 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -119,14 +119,14 @@
 
 Result<void> ServiceParser::ParseGroup(std::vector<std::string>&& args) {
     auto gid = DecodeUid(args[1]);
-    if (!gid) {
+    if (!gid.ok()) {
         return Error() << "Unable to decode GID for '" << args[1] << "': " << gid.error();
     }
     service_->proc_attr_.gid = *gid;
 
     for (std::size_t n = 2; n < args.size(); n++) {
         gid = DecodeUid(args[n]);
-        if (!gid) {
+        if (!gid.ok()) {
             return Error() << "Unable to decode GID for '" << args[n] << "': " << gid.error();
         }
         service_->proc_attr_.supp_gids.emplace_back(*gid);
@@ -202,13 +202,13 @@
     auto it = args.begin() + 1;
     if (args.size() == 2 && StartsWith(args[1], "$")) {
         auto expanded = ExpandProps(args[1]);
-        if (!expanded) {
+        if (!expanded.ok()) {
             return expanded.error();
         }
 
         // If the property is not set, it defaults to none, in which case there are no keycodes
         // for this service.
-        if (expanded == "none") {
+        if (*expanded == "none") {
             return {};
         }
 
@@ -240,7 +240,7 @@
 Result<void> ServiceParser::ParseOnrestart(std::vector<std::string>&& args) {
     args.erase(args.begin());
     int line = service_->onrestart_.NumCommands() + 1;
-    if (auto result = service_->onrestart_.AddCommand(std::move(args), line); !result) {
+    if (auto result = service_->onrestart_.AddCommand(std::move(args), line); !result.ok()) {
         return Error() << "cannot add Onrestart command: " << result.error();
     }
     return {};
@@ -310,7 +310,7 @@
 
 Result<void> ServiceParser::ParseProcessRlimit(std::vector<std::string>&& args) {
     auto rlimit = ParseRlimit(args);
-    if (!rlimit) return rlimit.error();
+    if (!rlimit.ok()) return rlimit.error();
 
     service_->proc_attr_.rlimits.emplace_back(*rlimit);
     return {};
@@ -407,7 +407,7 @@
 
     if (args.size() > 4) {
         auto uid = DecodeUid(args[4]);
-        if (!uid) {
+        if (!uid.ok()) {
             return Error() << "Unable to find UID for '" << args[4] << "': " << uid.error();
         }
         socket.uid = *uid;
@@ -415,7 +415,7 @@
 
     if (args.size() > 5) {
         auto gid = DecodeUid(args[5]);
-        if (!gid) {
+        if (!gid.ok()) {
             return Error() << "Unable to find GID for '" << args[5] << "': " << gid.error();
         }
         socket.gid = *gid;
@@ -453,7 +453,7 @@
     file.type = args[2];
 
     auto file_name = ExpandProps(args[1]);
-    if (!file_name) {
+    if (!file_name.ok()) {
         return Error() << "Could not expand file path ': " << file_name.error();
     }
     file.name = *file_name;
@@ -475,7 +475,7 @@
 
 Result<void> ServiceParser::ParseUser(std::vector<std::string>&& args) {
     auto uid = DecodeUid(args[1]);
-    if (!uid) {
+    if (!uid.ok()) {
         return Error() << "Unable to find UID for '" << args[1] << "': " << uid.error();
     }
     service_->proc_attr_.uid = *uid;
@@ -580,7 +580,7 @@
 
     auto parser = GetParserMap().Find(args);
 
-    if (!parser) return parser.error();
+    if (!parser.ok()) return parser.error();
 
     return std::invoke(*parser, this, std::move(args));
 }
@@ -593,7 +593,7 @@
     if (interface_inheritance_hierarchy_) {
         if (const auto& check_hierarchy_result = CheckInterfaceInheritanceHierarchy(
                     service_->interfaces(), *interface_inheritance_hierarchy_);
-            !check_hierarchy_result) {
+            !check_hierarchy_result.ok()) {
             return Error() << check_hierarchy_result.error();
         }
     }
diff --git a/init/service_test.cpp b/init/service_test.cpp
index c158b0a..22ee844 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -76,15 +76,15 @@
 TEST(service, make_temporary_oneshot_service_invalid_syntax) {
     std::vector<std::string> args;
     // Nothing.
-    ASSERT_FALSE(Service::MakeTemporaryOneshotService(args));
+    ASSERT_FALSE(Service::MakeTemporaryOneshotService(args).ok());
 
     // No arguments to 'exec'.
     args.push_back("exec");
-    ASSERT_FALSE(Service::MakeTemporaryOneshotService(args));
+    ASSERT_FALSE(Service::MakeTemporaryOneshotService(args).ok());
 
     // No command in "exec --".
     args.push_back("--");
-    ASSERT_FALSE(Service::MakeTemporaryOneshotService(args));
+    ASSERT_FALSE(Service::MakeTemporaryOneshotService(args).ok());
 }
 
 TEST(service, make_temporary_oneshot_service_too_many_supplementary_gids) {
@@ -98,7 +98,7 @@
     }
     args.push_back("--");
     args.push_back("/system/bin/id");
-    ASSERT_FALSE(Service::MakeTemporaryOneshotService(args));
+    ASSERT_FALSE(Service::MakeTemporaryOneshotService(args).ok());
 }
 
 static void Test_make_temporary_oneshot_service(bool dash_dash, bool seclabel, bool uid, bool gid,
@@ -124,7 +124,7 @@
     args.push_back("/system/bin/toybox");
     args.push_back("id");
     auto service_ret = Service::MakeTemporaryOneshotService(args);
-    ASSERT_TRUE(service_ret);
+    ASSERT_RESULT_OK(service_ret);
     auto svc = std::move(*service_ret);
 
     if (seclabel) {
@@ -134,14 +134,14 @@
     }
     if (uid) {
         auto decoded_uid = DecodeUid("log");
-        ASSERT_TRUE(decoded_uid);
+        ASSERT_RESULT_OK(decoded_uid);
         ASSERT_EQ(*decoded_uid, svc->uid());
     } else {
         ASSERT_EQ(0U, svc->uid());
     }
     if (gid) {
         auto decoded_uid = DecodeUid("shell");
-        ASSERT_TRUE(decoded_uid);
+        ASSERT_RESULT_OK(decoded_uid);
         ASSERT_EQ(*decoded_uid, svc->gid());
     } else {
         ASSERT_EQ(0U, svc->gid());
@@ -150,11 +150,11 @@
         ASSERT_EQ(2U, svc->supp_gids().size());
 
         auto decoded_uid = DecodeUid("system");
-        ASSERT_TRUE(decoded_uid);
+        ASSERT_RESULT_OK(decoded_uid);
         ASSERT_EQ(*decoded_uid, svc->supp_gids()[0]);
 
         decoded_uid = DecodeUid("adb");
-        ASSERT_TRUE(decoded_uid);
+        ASSERT_RESULT_OK(decoded_uid);
         ASSERT_EQ(*decoded_uid, svc->supp_gids()[1]);
     } else {
         ASSERT_EQ(0U, svc->supp_gids().size());
diff --git a/init/service_utils.cpp b/init/service_utils.cpp
index 93cffd8..484c2c8 100644
--- a/init/service_utils.cpp
+++ b/init/service_utils.cpp
@@ -167,7 +167,7 @@
 Result<Descriptor> SocketDescriptor::Create(const std::string& global_context) const {
     const auto& socket_context = context.empty() ? global_context : context;
     auto result = CreateSocket(name, type | SOCK_CLOEXEC, passcred, perm, uid, gid, socket_context);
-    if (!result) {
+    if (!result.ok()) {
         return result.error();
     }
 
@@ -196,7 +196,7 @@
 
 Result<void> EnterNamespaces(const NamespaceInfo& info, const std::string& name, bool pre_apexd) {
     for (const auto& [nstype, path] : info.namespaces_to_enter) {
-        if (auto result = EnterNamespace(nstype, path.c_str()); !result) {
+        if (auto result = EnterNamespace(nstype, path.c_str()); !result.ok()) {
             return result;
         }
     }
@@ -214,14 +214,14 @@
         bool remount_sys =
                 std::any_of(info.namespaces_to_enter.begin(), info.namespaces_to_enter.end(),
                             [](const auto& entry) { return entry.first == CLONE_NEWNET; });
-        if (auto result = SetUpMountNamespace(remount_proc, remount_sys); !result) {
+        if (auto result = SetUpMountNamespace(remount_proc, remount_sys); !result.ok()) {
             return result;
         }
     }
 
     if (info.flags & CLONE_NEWPID) {
         // This will fork again to run an init process inside the PID namespace.
-        if (auto result = SetUpPidNamespace(name.c_str()); !result) {
+        if (auto result = SetUpPidNamespace(name.c_str()); !result.ok()) {
             return result;
         }
     }
@@ -249,9 +249,8 @@
 
     for (const auto& rlimit : attr.rlimits) {
         if (setrlimit(rlimit.first, &rlimit.second) == -1) {
-            return ErrnoError() << StringPrintf(
-                           "setrlimit(%d, {rlim_cur=%ld, rlim_max=%ld}) failed", rlimit.first,
-                           rlimit.second.rlim_cur, rlimit.second.rlim_max);
+            return ErrnoErrorf("setrlimit({}, {{rlim_cur={}, rlim_max={}}}) failed", rlimit.first,
+                               rlimit.second.rlim_cur, rlimit.second.rlim_max);
         }
     }
 
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index f3f759d..3260159 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -80,13 +80,13 @@
 
     auto map_result = function_map_->Find(args);
     Result<void> result;
-    if (!map_result) {
+    if (!map_result.ok()) {
         result = Error() << "Cannot find command: " << map_result.error();
     } else {
         result = RunBuiltinFunction(map_result->function, args, context_);
     }
 
-    if (result) {
+    if (result.ok()) {
         reply->set_success(true);
     } else {
         auto* failure = reply->mutable_failure();
@@ -99,7 +99,7 @@
                                    SubcontextReply* reply) const {
     for (const auto& arg : expand_args_command.args()) {
         auto expanded_arg = ExpandProps(arg);
-        if (!expanded_arg) {
+        if (!expanded_arg.ok()) {
             auto* failure = reply->mutable_failure();
             failure->set_error_string(expanded_arg.error().message());
             failure->set_error_errno(0);
@@ -125,7 +125,7 @@
         }
 
         auto init_message = ReadMessage(init_fd_);
-        if (!init_message) {
+        if (!init_message.ok()) {
             if (init_message.error().code() == 0) {
                 // If the init file descriptor was closed, let's exit quietly. If
                 // this was accidental, init will restart us. If init died, this
@@ -160,7 +160,7 @@
             shutdown_command.clear();
         }
 
-        if (auto result = SendMessage(init_fd_, reply); !result) {
+        if (auto result = SendMessage(init_fd_, reply); !result.ok()) {
             LOG(FATAL) << "Failed to send message to init: " << result.error();
         }
     }
@@ -246,13 +246,13 @@
 }
 
 Result<SubcontextReply> Subcontext::TransmitMessage(const SubcontextCommand& subcontext_command) {
-    if (auto result = SendMessage(socket_, subcontext_command); !result) {
+    if (auto result = SendMessage(socket_, subcontext_command); !result.ok()) {
         Restart();
         return ErrnoError() << "Failed to send message to subcontext";
     }
 
     auto subcontext_message = ReadMessage(socket_);
-    if (!subcontext_message) {
+    if (!subcontext_message.ok()) {
         Restart();
         return Error() << "Failed to receive result from subcontext: " << subcontext_message.error();
     }
@@ -277,7 +277,7 @@
         RepeatedPtrFieldBackInserter(subcontext_command.mutable_execute_command()->mutable_args()));
 
     auto subcontext_reply = TransmitMessage(subcontext_command);
-    if (!subcontext_reply) {
+    if (!subcontext_reply.ok()) {
         return subcontext_reply.error();
     }
 
@@ -301,7 +301,7 @@
                   subcontext_command.mutable_expand_args_command()->mutable_args()));
 
     auto subcontext_reply = TransmitMessage(subcontext_command);
-    if (!subcontext_reply) {
+    if (!subcontext_reply.ok()) {
         return subcontext_reply.error();
     }
 
diff --git a/init/subcontext_test.cpp b/init/subcontext_test.cpp
index 2e5a256..ee765a7 100644
--- a/init/subcontext_test.cpp
+++ b/init/subcontext_test.cpp
@@ -55,7 +55,7 @@
 TEST(subcontext, CheckDifferentPid) {
     RunTest([](auto& subcontext) {
         auto result = subcontext.Execute(std::vector<std::string>{"return_pids_as_error"});
-        ASSERT_FALSE(result);
+        ASSERT_FALSE(result.ok());
 
         auto pids = Split(result.error().message(), " ");
         ASSERT_EQ(2U, pids.size());
@@ -81,7 +81,7 @@
             "success",
         };
         auto result = subcontext.Execute(args);
-        ASSERT_TRUE(result) << result.error();
+        ASSERT_RESULT_OK(result);
 
         EXPECT_TRUE(WaitForProperty("init.test.subcontext", "success", 10s));
     });
@@ -104,11 +104,11 @@
                 word,
             };
             auto result = subcontext.Execute(args);
-            ASSERT_TRUE(result) << result.error();
+            ASSERT_RESULT_OK(result);
         }
 
         auto result = subcontext.Execute(std::vector<std::string>{"return_words_as_error"});
-        ASSERT_FALSE(result);
+        ASSERT_FALSE(result.ok());
         EXPECT_EQ(Join(expected_words, " "), result.error().message());
         EXPECT_EQ(first_pid, subcontext.pid());
     });
@@ -119,10 +119,10 @@
         auto first_pid = subcontext.pid();
 
         auto result = subcontext.Execute(std::vector<std::string>{"cause_log_fatal"});
-        ASSERT_FALSE(result);
+        ASSERT_FALSE(result.ok());
 
         auto result2 = subcontext.Execute(std::vector<std::string>{"generate_sane_error"});
-        ASSERT_FALSE(result2);
+        ASSERT_FALSE(result2.ok());
         EXPECT_EQ("Sane error!", result2.error().message());
         EXPECT_NE(subcontext.pid(), first_pid);
     });
@@ -131,7 +131,7 @@
 TEST(subcontext, ContextString) {
     RunTest([](auto& subcontext) {
         auto result = subcontext.Execute(std::vector<std::string>{"return_context_as_error"});
-        ASSERT_FALSE(result);
+        ASSERT_FALSE(result.ok());
         ASSERT_EQ(kTestContext, result.error().message());
     });
 }
@@ -143,7 +143,7 @@
     RunTest([](auto& subcontext) {
         auto result = subcontext.Execute(
                 std::vector<std::string>{"trigger_shutdown", kTestShutdownCommand});
-        ASSERT_TRUE(result);
+        ASSERT_RESULT_OK(result);
     });
     EXPECT_EQ(kTestShutdownCommand, trigger_shutdown_command);
 }
@@ -156,7 +156,7 @@
             "$$third",
         };
         auto result = subcontext.ExpandArgs(args);
-        ASSERT_TRUE(result) << result.error();
+        ASSERT_RESULT_OK(result);
         ASSERT_EQ(3U, result->size());
         EXPECT_EQ(args[0], result->at(0));
         EXPECT_EQ(GetProperty("ro.hardware", ""), result->at(1));
@@ -171,7 +171,7 @@
             "${",
         };
         auto result = subcontext.ExpandArgs(args);
-        ASSERT_FALSE(result);
+        ASSERT_FALSE(result.ok());
         EXPECT_EQ("unexpected end of string in '" + args[1] + "', looking for }",
                   result.error().message());
     });
diff --git a/init/sysprop/InitProperties.sysprop b/init/sysprop/InitProperties.sysprop
index c856358..b876dc0 100644
--- a/init/sysprop/InitProperties.sysprop
+++ b/init/sysprop/InitProperties.sysprop
@@ -29,7 +29,7 @@
 prop {
     api_name: "is_userspace_reboot_supported"
     type: Boolean
-    scope: System
+    scope: Public
     access: Readonly
     prop_name: "ro.init.userspace_reboot.is_supported"
     integer_as_bool: true
diff --git a/init/test_utils/include/init-test-utils/service_utils.h b/init/test_utils/include/init-test-utils/service_utils.h
index 3ec61d4..f9366ea 100644
--- a/init/test_utils/include/init-test-utils/service_utils.h
+++ b/init/test_utils/include/init-test-utils/service_utils.h
@@ -20,12 +20,20 @@
 #include <set>
 
 #include <android-base/result.h>
-#include <hidl-util/FqInstance.h>
 
 namespace android {
 namespace init {
 
-using ServiceInterfacesMap = std::map<std::string, std::set<android::FqInstance>>;
+// this is service name -> interface declaration
+//
+// So, for:
+//     service foo ..
+//         interface aidl baz
+//         interface android.hardware.foo@1.0 IFoo
+//
+// We have:
+//     foo -> { aidl/baz, android.hardware.foo@1.0/IFoo }
+using ServiceInterfacesMap = std::map<std::string, std::set<std::string>>;
 android::base::Result<ServiceInterfacesMap> GetOnDeviceServiceInterfacesMap();
 
 }  // namespace init
diff --git a/init/test_utils/service_utils.cpp b/init/test_utils/service_utils.cpp
index bc00702..ae68679 100644
--- a/init/test_utils/service_utils.cpp
+++ b/init/test_utils/service_utils.cpp
@@ -47,14 +47,7 @@
     for (const auto& service : service_list.services()) {
         // Create an entry for all services, including services that may not
         // have any declared interfaces.
-        result[service->name()] = std::set<android::FqInstance>();
-        for (const auto& intf : service->interfaces()) {
-            android::FqInstance fqInstance;
-            if (!fqInstance.setTo(intf)) {
-                return android::base::Error() << "Unable to parse interface: '" << intf << "'";
-            }
-            result[service->name()].insert(fqInstance);
-        }
+        result[service->name()] = service->interfaces();
     }
     return result;
 }
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index a74b247..09dce44 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -209,7 +209,7 @@
 
     auto parser = parser_map.Find(args);
 
-    if (!parser) return Error() << parser.error();
+    if (!parser.ok()) return Error() << parser.error();
 
     return std::invoke(*parser, this, std::move(args));
 }
diff --git a/init/util.cpp b/init/util.cpp
index 0ca0da5..503c705 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -497,14 +497,14 @@
                 break;
             case 3:
                 uid = DecodeUid(args[3]);
-                if (!uid) {
+                if (!uid.ok()) {
                     return Error()
                            << "Unable to decode UID for '" << args[3] << "': " << uid.error();
                 }
                 break;
             case 4:
                 gid = DecodeUid(args[4]);
-                if (!gid) {
+                if (!gid.ok()) {
                     return Error()
                            << "Unable to decode GID for '" << args[4] << "': " << gid.error();
                 }
diff --git a/init/util_test.cpp b/init/util_test.cpp
index a8fcc87..96a5b55 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -33,7 +33,7 @@
     errno = 0;
     auto file_contents = ReadFile("/proc/does-not-exist");
     EXPECT_EQ(ENOENT, errno);
-    ASSERT_FALSE(file_contents);
+    ASSERT_FALSE(file_contents.ok());
     EXPECT_EQ("open() failed: No such file or directory", file_contents.error().message());
 }
 
@@ -41,10 +41,10 @@
     std::string s("hello");
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
-    EXPECT_TRUE(WriteFile(tf.path, s)) << strerror(errno);
+    EXPECT_RESULT_OK(WriteFile(tf.path, s));
     EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0620, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
     auto file_contents = ReadFile(tf.path);
-    ASSERT_FALSE(file_contents) << strerror(errno);
+    ASSERT_FALSE(file_contents.ok()) << strerror(errno);
     EXPECT_EQ("Skipping insecure file", file_contents.error().message());
 }
 
@@ -52,10 +52,10 @@
     std::string s("hello");
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
-    EXPECT_TRUE(WriteFile(tf.path, s)) << strerror(errno);
+    EXPECT_RESULT_OK(WriteFile(tf.path, s));
     EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0602, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
     auto file_contents = ReadFile(tf.path);
-    ASSERT_FALSE(file_contents) << strerror(errno);
+    ASSERT_FALSE(file_contents.ok());
     EXPECT_EQ("Skipping insecure file", file_contents.error().message());
 }
 
@@ -64,14 +64,14 @@
     // lrw------- 1 root root 23 2008-12-31 19:00 default.prop -> system/etc/prop.default
     auto file_contents = ReadFile("/default.prop");
     EXPECT_EQ(ELOOP, errno);
-    ASSERT_FALSE(file_contents);
+    ASSERT_FALSE(file_contents.ok());
     EXPECT_EQ("open() failed: Too many symbolic links encountered",
               file_contents.error().message());
 }
 
 TEST(util, ReadFileSuccess) {
     auto file_contents = ReadFile("/proc/version");
-    ASSERT_TRUE(file_contents);
+    ASSERT_TRUE(file_contents.ok());
     EXPECT_GT(file_contents->length(), 6U);
     EXPECT_EQ('\n', file_contents->at(file_contents->length() - 1));
     (*file_contents)[5] = 0;
@@ -87,10 +87,10 @@
 
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
-    EXPECT_TRUE(WriteFile(tf.path, contents)) << strerror(errno);
+    EXPECT_RESULT_OK(WriteFile(tf.path, contents));
 
     auto read_back_contents = ReadFile(tf.path);
-    ASSERT_TRUE(read_back_contents) << strerror(errno);
+    ASSERT_RESULT_OK(read_back_contents);
     EXPECT_EQ(contents, *read_back_contents);
     EXPECT_EQ(10u, read_back_contents->size());
 }
@@ -99,14 +99,15 @@
     std::string s("hello");
     TemporaryDir test_dir;
     std::string path = android::base::StringPrintf("%s/does-not-exist", test_dir.path);
-    EXPECT_TRUE(WriteFile(path, s));
+    EXPECT_RESULT_OK(WriteFile(path, s));
     auto file_contents = ReadFile(path);
-    ASSERT_TRUE(file_contents);
+    ASSERT_RESULT_OK(file_contents);
     EXPECT_EQ(s, *file_contents);
     struct stat sb;
     int fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
     EXPECT_NE(-1, fd);
     EXPECT_EQ(0, fstat(fd, &sb));
+    EXPECT_EQ(0, close(fd));
     EXPECT_EQ((const unsigned int)(S_IRUSR | S_IWUSR), sb.st_mode & 0777);
     EXPECT_EQ(0, unlink(path.c_str()));
 }
@@ -114,27 +115,27 @@
 TEST(util, WriteFileExist) {
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
-    EXPECT_TRUE(WriteFile(tf.path, "1hello1")) << strerror(errno);
+    EXPECT_RESULT_OK(WriteFile(tf.path, "1hello1"));
     auto file_contents = ReadFile(tf.path);
-    ASSERT_TRUE(file_contents);
+    ASSERT_RESULT_OK(file_contents);
     EXPECT_EQ("1hello1", *file_contents);
-    EXPECT_TRUE(WriteFile(tf.path, "2ll2"));
+    EXPECT_RESULT_OK(WriteFile(tf.path, "2ll2"));
     file_contents = ReadFile(tf.path);
-    ASSERT_TRUE(file_contents);
+    ASSERT_RESULT_OK(file_contents);
     EXPECT_EQ("2ll2", *file_contents);
 }
 
 TEST(util, DecodeUid) {
     auto decoded_uid = DecodeUid("root");
-    EXPECT_TRUE(decoded_uid);
+    EXPECT_TRUE(decoded_uid.ok());
     EXPECT_EQ(0U, *decoded_uid);
 
     decoded_uid = DecodeUid("toot");
-    EXPECT_FALSE(decoded_uid);
+    EXPECT_FALSE(decoded_uid.ok());
     EXPECT_EQ("getpwnam failed: No such file or directory", decoded_uid.error().message());
 
     decoded_uid = DecodeUid("123");
-    EXPECT_TRUE(decoded_uid);
+    EXPECT_RESULT_OK(decoded_uid);
     EXPECT_EQ(123U, *decoded_uid);
 }
 
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 334364e..ecd130b 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -21,11 +21,6 @@
     "fs.cpp",
     "hashmap.cpp",
     "multiuser.cpp",
-    "socket_inaddr_any_server_unix.cpp",
-    "socket_local_client_unix.cpp",
-    "socket_local_server_unix.cpp",
-    "socket_network_client_unix.cpp",
-    "sockets_unix.cpp",
     "str_parms.cpp",
 ]
 
@@ -49,6 +44,90 @@
     },
 }
 
+// Socket specific parts of libcutils that are safe to statically link into an APEX.
+cc_library_static {
+    name: "libcutils_sockets",
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+        support_system_process: true,
+    },
+    recovery_available: true,
+    host_supported: true,
+    native_bridge_supported: true,
+    apex_available: [
+        "//apex_available:platform",
+        "//apex_available:anyapex",
+    ],
+
+    export_include_dirs: ["include"],
+
+    srcs: ["sockets.cpp"],
+    target: {
+        linux_bionic: {
+            enabled: true,
+        },
+
+        not_windows: {
+            srcs: [
+                "socket_inaddr_any_server_unix.cpp",
+                "socket_local_client_unix.cpp",
+                "socket_local_server_unix.cpp",
+                "socket_network_client_unix.cpp",
+                "sockets_unix.cpp",
+            ],
+        },
+
+        // "not_windows" means "non-Windows host".
+        android: {
+            srcs: [
+                "android_get_control_file.cpp",
+                "socket_inaddr_any_server_unix.cpp",
+                "socket_local_client_unix.cpp",
+                "socket_local_server_unix.cpp",
+                "socket_network_client_unix.cpp",
+                "sockets_unix.cpp",
+            ],
+            static_libs: ["libbase"],
+        },
+
+        windows: {
+            host_ldlibs: ["-lws2_32"],
+            srcs: [
+                "socket_inaddr_any_server_windows.cpp",
+                "socket_network_client_windows.cpp",
+                "sockets_windows.cpp",
+            ],
+
+            enabled: true,
+            cflags: [
+                "-D_GNU_SOURCE",
+            ],
+        },
+    },
+}
+
+cc_test {
+    name: "libcutils_sockets_test",
+    test_suites: ["device-tests"],
+    static_libs: ["libbase", "libcutils_sockets"],
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+
+    srcs: ["sockets_test.cpp"],
+    target: {
+        android: {
+            srcs: [
+                "android_get_control_file_test.cpp",
+                "android_get_control_socket_test.cpp",
+            ],
+        },
+    },
+}
+
 cc_library {
     name: "libcutils",
     vendor_available: true,
@@ -66,7 +145,6 @@
         "load_file.cpp",
         "native_handle.cpp",
         "record_stream.cpp",
-        "sockets.cpp",
         "strlcpy.c",
         "threads.cpp",
     ],
@@ -86,9 +164,6 @@
             host_ldlibs: ["-lws2_32"],
 
             srcs: [
-                "socket_inaddr_any_server_windows.cpp",
-                "socket_network_client_windows.cpp",
-                "sockets_windows.cpp",
                 "trace-host.cpp",
             ],
 
@@ -97,10 +172,8 @@
                 "-D_GNU_SOURCE",
             ],
         },
-
         android: {
             srcs: libcutils_nonwindows_sources + [
-                "android_get_control_file.cpp",
                 "android_reboot.cpp",
                 "ashmem-dev.cpp",
                 "fs_config.cpp",
@@ -172,6 +245,7 @@
         }
     },
 
+    whole_static_libs: ["libcutils_sockets"],
     shared_libs: [
         "liblog",
         "libbase",
@@ -250,9 +324,8 @@
     require_root: true,
 }
 
-cc_test {
-    name: "libcutils_test_static",
-    test_suites: ["device-tests"],
+cc_defaults {
+    name: "libcutils_test_static_defaults",
     defaults: ["libcutils_test_default"],
     static_libs: [
         "libc",
@@ -272,3 +345,16 @@
         },
     },
 }
+
+cc_test {
+    name: "libcutils_test_static",
+    test_suites: ["device-tests"],
+    defaults: ["libcutils_test_static_defaults"],
+}
+
+cc_test {
+    name: "KernelLibcutilsTest",
+    test_suites: ["general-tests", "vts-core"],
+    defaults: ["libcutils_test_static_defaults"],
+    test_config: "KernelLibcutilsTest.xml",
+}
diff --git a/libcutils/KernelLibcutilsTest.xml b/libcutils/KernelLibcutilsTest.xml
new file mode 100644
index 0000000..40e4ef4
--- /dev/null
+++ b/libcutils/KernelLibcutilsTest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs KernelLibcutilsTest.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-native" />
+
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="KernelLibcutilsTest->/data/local/tmp/KernelLibcutilsTest" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="KernelLibcutilsTest" />
+        <option name="include-filter" value="*AshmemTest*" />
+    </test>
+</configuration>
diff --git a/libcutils/ashmem-dev.cpp b/libcutils/ashmem-dev.cpp
index 340572c..8c232f0 100644
--- a/libcutils/ashmem-dev.cpp
+++ b/libcutils/ashmem-dev.cpp
@@ -203,19 +203,23 @@
 {
     static const std::string ashmem_device_path = get_ashmem_device_path();
 
-    int ret;
-    struct stat st;
-
     if (ashmem_device_path.empty()) {
         return -1;
     }
 
     int fd = TEMP_FAILURE_RETRY(open(ashmem_device_path.c_str(), O_RDWR | O_CLOEXEC));
+
+    // fallback for APEX w/ use_vendor on Q, which would have still used /dev/ashmem
+    if (fd < 0) {
+        fd = TEMP_FAILURE_RETRY(open("/dev/ashmem", O_RDWR | O_CLOEXEC));
+    }
+
     if (fd < 0) {
         return fd;
     }
 
-    ret = TEMP_FAILURE_RETRY(fstat(fd, &st));
+    struct stat st;
+    int ret = TEMP_FAILURE_RETRY(fstat(fd, &st));
     if (ret < 0) {
         int save_errno = errno;
         close(fd);
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index dc31b28..c4e4f85 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -74,6 +74,8 @@
     { 00775, AID_MEDIA_RW,     AID_MEDIA_RW,     0, "data/media" },
     { 00750, AID_ROOT,         AID_SHELL,        0, "data/nativetest" },
     { 00750, AID_ROOT,         AID_SHELL,        0, "data/nativetest64" },
+    { 00750, AID_ROOT,         AID_SHELL,        0, "data/benchmarktest" },
+    { 00750, AID_ROOT,         AID_SHELL,        0, "data/benchmarktest64" },
     { 00775, AID_ROOT,         AID_ROOT,         0, "data/preloads" },
     { 00771, AID_SYSTEM,       AID_SYSTEM,       0, "data" },
     { 00755, AID_ROOT,         AID_SYSTEM,       0, "mnt" },
@@ -145,6 +147,8 @@
     { 00640, AID_ROOT,      AID_SHELL,     0, "data/nativetest64/tests.txt" },
     { 00750, AID_ROOT,      AID_SHELL,     0, "data/nativetest/*" },
     { 00750, AID_ROOT,      AID_SHELL,     0, "data/nativetest64/*" },
+    { 00750, AID_ROOT,      AID_SHELL,     0, "data/benchmarktest/*" },
+    { 00750, AID_ROOT,      AID_SHELL,     0, "data/benchmarktest64/*" },
     { 00600, AID_ROOT,      AID_ROOT,      0, "default.prop" }, // legacy
     { 00600, AID_ROOT,      AID_ROOT,      0, "system/etc/prop.default" },
     { 00600, AID_ROOT,      AID_ROOT,      0, "odm/build.prop" }, // legacy; only for P release
@@ -196,10 +200,7 @@
     { 00750, AID_ROOT,      AID_SHELL,     CAP_MASK_LONG(CAP_SETUID) |
                                            CAP_MASK_LONG(CAP_SETGID),
                                               "system/bin/simpleperf_app_runner" },
-
-    // Support FIFO scheduling mode in SurfaceFlinger.
-    { 00755, AID_SYSTEM,    AID_GRAPHICS,  CAP_MASK_LONG(CAP_SYS_NICE),
-                                              "system/bin/surfaceflinger" },
+    { 00755, AID_ROOT,      AID_ROOT,      0, "first_stage_ramdisk/system/bin/e2fsck" },
     // generic defaults
     { 00755, AID_ROOT,      AID_ROOT,      0, "bin/*" },
     { 00640, AID_ROOT,      AID_SHELL,     0, "fstab.*" },
diff --git a/libcutils/include/cutils/android_reboot.h b/libcutils/include/cutils/android_reboot.h
index cd27eef..24e32d5 100644
--- a/libcutils/include/cutils/android_reboot.h
+++ b/libcutils/include/cutils/android_reboot.h
@@ -31,6 +31,7 @@
 
 /* Android reboot reason stored in this property */
 #define LAST_REBOOT_REASON_PROPERTY "persist.sys.boot.reason"
+#define LAST_REBOOT_REASON_FILE "/metadata/bootstat/" LAST_REBOOT_REASON_PROPERTY
 
 /* Reboot or shutdown the system.
  * This call uses ANDROID_RB_PROPERTY to request reboot to init process.
diff --git a/libcutils/include/cutils/trace.h b/libcutils/include/cutils/trace.h
index 79b4b35..e12c32c 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -25,7 +25,6 @@
 #include <sys/cdefs.h>
 #include <sys/types.h>
 #include <unistd.h>
-
 #include <cutils/compiler.h>
 
 __BEGIN_DECLS
@@ -89,6 +88,12 @@
 #error ATRACE_TAG must be defined to be one of the tags defined in cutils/trace.h
 #endif
 
+// Set this to 0 to revert to the old Binder-based atrace implementation.
+// This is only here in case rollbacks do not apply cleanly.
+// TODO(fmayer): Remove this once we are confident this won't need to be
+// rolled back, no later than 2020-03-01.
+#define ATRACE_SHMEM 1
+
 /**
  * Opens the trace file for writing and reads the property for initial tags.
  * The atrace.tags.enableflags property sets the tags to trace.
@@ -116,11 +121,15 @@
  * prevent tracing within the Zygote process.
  */
 void atrace_set_tracing_enabled(bool enabled);
-
 /**
- * Flag indicating whether setup has been completed, initialized to 0.
- * Nonzero indicates setup has completed.
- * Note: This does NOT indicate whether or not setup was successful.
+ * If !ATRACE_SHMEM:
+ *   Flag indicating whether setup has been completed, initialized to 0.
+ *   Nonzero indicates setup has completed.
+ *   Note: This does NOT indicate whether or not setup was successful.
+ * If ATRACE_SHMEM:
+ *   This is always set to false. This forces code that uses an old version
+ *   of this header to always call into atrace_setup, in which we call
+ *   atrace_init unconditionally.
  */
 extern atomic_bool atrace_is_ready;
 
@@ -143,6 +152,12 @@
  * This can be explicitly run to avoid setup delay on first trace function.
  */
 #define ATRACE_INIT() atrace_init()
+#define ATRACE_GET_ENABLED_TAGS() atrace_get_enabled_tags()
+
+#if ATRACE_SHMEM
+void atrace_init();
+uint64_t atrace_get_enabled_tags();
+#else
 static inline void atrace_init()
 {
     if (CC_UNLIKELY(!atomic_load_explicit(&atrace_is_ready, memory_order_acquire))) {
@@ -155,12 +170,12 @@
  * It can be used as a guard condition around more expensive trace calculations.
  * Every trace function calls this, which ensures atrace_init is run.
  */
-#define ATRACE_GET_ENABLED_TAGS() atrace_get_enabled_tags()
 static inline uint64_t atrace_get_enabled_tags()
 {
     atrace_init();
     return atrace_enabled_tags;
 }
+#endif
 
 /**
  * Test if a given tag is currently enabled.
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index e1e8230..a2d36ff 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -129,6 +129,10 @@
 #define AID_NETWORK_STACK 1073   /* network stack service */
 #define AID_GSID 1074            /* GSI service daemon */
 #define AID_FSVERITY_CERT 1075   /* fs-verity key ownership in keystore */
+#define AID_CREDSTORE 1076       /* identity credential manager service */
+#define AID_EXTERNAL_STORAGE 1077 /* Full external storage access including USB OTG volumes */
+#define AID_EXT_DATA_RW 1078      /* GID for app-private data directories on external storage */
+#define AID_EXT_OBB_RW 1079       /* GID for OBB directories on external storage */
 /* Changes to this file must be made in AOSP, *not* in internal branches. */
 
 #define AID_SHELL 2000 /* adb and debug shell user */
diff --git a/libcutils/include/private/android_projectid_config.h b/libcutils/include/private/android_projectid_config.h
new file mode 100644
index 0000000..7ef3854
--- /dev/null
+++ b/libcutils/include/private/android_projectid_config.h
@@ -0,0 +1,51 @@
+/*
+ * 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
+
+/*
+ * This file describes the project ID values we use for filesystem quota
+ * tracking.  It is used on devices that don't have the sdcardfs kernel module,
+ * which requires us to use filesystem project IDs for efficient quota
+ * calculation.
+ *
+ * These values are typically set on files and directories using extended
+ * attributes; see vold for examples.
+ */
+
+/* Default project ID for files on external storage. */
+#define PROJECT_ID_EXT_DEFAULT 1000
+/* Project ID for audio files on external storage. */
+#define PROJECT_ID_EXT_MEDIA_AUDIO 1001
+/* Project ID for video files on external storage. */
+#define PROJECT_ID_EXT_MEDIA_VIDEO 1002
+/* Project ID for image files on external storage. */
+#define PROJECT_ID_EXT_MEDIA_IMAGE 1003
+
+/* Start of project IDs for apps to mark external app data. */
+#define PROJECT_ID_EXT_DATA_START 20000
+/* End of project IDs for apps to mark external app data. */
+#define PROJECT_ID_EXT_DATA_END 29999
+
+/* Start of project IDs for apps to mark external cached data. */
+#define PROJECT_ID_EXT_CACHE_START 30000
+/* End of project IDs for apps to mark external cached data. */
+#define PROJECT_ID_EXT_CACHE_END 39999
+
+/* Start of project IDs for apps to mark external OBB data. */
+#define PROJECT_ID_EXT_OBB_START 40000
+/* End of project IDs for apps to mark external OBB data. */
+#define PROJECT_ID_EXT_OBB_END 49999
diff --git a/libcutils/trace-container.cpp b/libcutils/trace-container.cpp
index d981f8f..f7eed48 100644
--- a/libcutils/trace-container.cpp
+++ b/libcutils/trace-container.cpp
@@ -39,6 +39,11 @@
 static pthread_mutex_t  atrace_enabling_mutex        = PTHREAD_MUTEX_INITIALIZER;
 static pthread_rwlock_t atrace_container_sock_rwlock = PTHREAD_RWLOCK_INITIALIZER;
 
+static void atrace_seq_number_changed(uint32_t, uint32_t seq_no) {
+    pthread_once(&atrace_once_control, atrace_init_once);
+    atomic_store_explicit(&last_sequence_number, seq_no, memory_order_relaxed);
+}
+
 static bool atrace_init_container_sock()
 {
     pthread_rwlock_wrlock(&atrace_container_sock_rwlock);
@@ -82,24 +87,28 @@
 
 static void atrace_init_once()
 {
-    atrace_marker_fd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
+    atrace_marker_fd = open("/sys/kernel/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
     if (atrace_marker_fd < 0) {
-        // We're in container, ftrace may be disabled. In such case, we use the
-        // socket to write trace event.
+        // try debugfs
+        atrace_marker_fd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
+        if (atrace_marker_fd < 0) {
+            // We're in container, ftrace may be disabled. In such case, we use the
+            // socket to write trace event.
 
-        // Protect the initialization of container socket from
-        // atrace_set_tracing_enabled.
-        pthread_mutex_lock(&atrace_enabling_mutex);
-        atrace_use_container_sock = true;
-        bool success = false;
-        if (atomic_load_explicit(&atrace_is_enabled, memory_order_acquire)) {
-            success = atrace_init_container_sock();
-        }
-        pthread_mutex_unlock(&atrace_enabling_mutex);
+            // Protect the initialization of container socket from
+            // atrace_set_tracing_enabled.
+            pthread_mutex_lock(&atrace_enabling_mutex);
+            atrace_use_container_sock = true;
+            bool success = false;
+            if (atomic_load_explicit(&atrace_is_enabled, memory_order_acquire)) {
+                success = atrace_init_container_sock();
+            }
+            pthread_mutex_unlock(&atrace_enabling_mutex);
 
-        if (!success) {
-            atrace_enabled_tags = 0;
-            goto done;
+            if (!success) {
+                atrace_enabled_tags = 0;
+                goto done;
+            }
         }
     }
     atrace_enabled_tags = atrace_get_property();
diff --git a/libcutils/trace-dev.cpp b/libcutils/trace-dev.cpp
index bff16c1..9ca1729 100644
--- a/libcutils/trace-dev.cpp
+++ b/libcutils/trace-dev.cpp
@@ -32,17 +32,48 @@
 {
     atrace_marker_fd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
     if (atrace_marker_fd == -1) {
+        atrace_marker_fd = open("/sys/kernel/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
+    }
+
+    if (atrace_marker_fd == -1) {
         ALOGE("Error opening trace file: %s (%d)", strerror(errno), errno);
         atrace_enabled_tags = 0;
     } else {
       atrace_enabled_tags = atrace_get_property();
     }
+#if !ATRACE_SHMEM
     atomic_store_explicit(&atrace_is_ready, true, memory_order_release);
+#endif
+}
+
+static void atrace_seq_number_changed(uint32_t prev_seq_no, uint32_t seq_no) {
+    if (!atomic_load_explicit(&atrace_is_enabled, memory_order_acquire)) {
+        return;
+    }
+
+    // Someone raced us.
+    if (!atomic_compare_exchange_strong(&last_sequence_number, &prev_seq_no, seq_no)) {
+        return;
+    }
+
+    if (CC_UNLIKELY(prev_seq_no == kSeqNoNotInit)) {
+#if defined(__BIONIC__)
+        const prop_info* new_pi = __system_property_find("debug.atrace.tags.enableflags");
+        if (new_pi) atrace_property_info = new_pi;
+#endif
+        pthread_once(&atrace_once_control, atrace_init_once);
+    }
+
+    atrace_update_tags();
 }
 
 void atrace_setup()
 {
+#if ATRACE_SHMEM
+    atrace_init();
+#else
     pthread_once(&atrace_once_control, atrace_init_once);
+#endif
 }
 
 void atrace_begin_body(const char* name)
diff --git a/libcutils/trace-dev.inc b/libcutils/trace-dev.inc
index e3da77b..a57a4c5 100644
--- a/libcutils/trace-dev.inc
+++ b/libcutils/trace-dev.inc
@@ -34,6 +34,11 @@
 #include <log/log.h>
 #include <log/log_properties.h>
 
+#if defined(__BIONIC__)
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+#endif
+
 /**
  * Maximum size of a message that can be logged to the trace buffer.
  * Note this message includes a tag, the pid, and the string given as the name.
@@ -41,12 +46,57 @@
  */
 #define ATRACE_MESSAGE_LENGTH 1024
 
-atomic_bool             atrace_is_ready      = ATOMIC_VAR_INIT(false);
-int                     atrace_marker_fd     = -1;
-uint64_t                atrace_enabled_tags  = ATRACE_TAG_NOT_READY;
-static bool             atrace_is_debuggable = false;
-static atomic_bool      atrace_is_enabled    = ATOMIC_VAR_INIT(true);
-static pthread_mutex_t  atrace_tags_mutex    = PTHREAD_MUTEX_INITIALIZER;
+constexpr uint32_t kSeqNoNotInit = static_cast<uint32_t>(-1);
+
+atomic_bool              atrace_is_ready      = ATOMIC_VAR_INIT(false);
+int                      atrace_marker_fd     = -1;
+uint64_t                 atrace_enabled_tags  = ATRACE_TAG_NOT_READY;
+static bool              atrace_is_debuggable = false;
+static atomic_bool       atrace_is_enabled    = ATOMIC_VAR_INIT(true);
+static pthread_mutex_t   atrace_tags_mutex    = PTHREAD_MUTEX_INITIALIZER;
+
+/**
+ * Sequence number of debug.atrace.tags.enableflags the last time the enabled
+ * tags were reloaded.
+ **/
+static _Atomic(uint32_t) last_sequence_number = ATOMIC_VAR_INIT(kSeqNoNotInit);
+
+#if defined(__BIONIC__)
+// All zero prop_info that has a sequence number of 0. This is easier than
+// depending on implementation details of the property implementation.
+//
+// prop_info is static_assert-ed to be 96 bytes, which cannot change due to
+// ABI compatibility.
+alignas(uint64_t) static char empty_pi[96];
+static const prop_info* atrace_property_info = reinterpret_cast<const prop_info*>(empty_pi);
+#endif
+
+#if ATRACE_SHMEM
+
+/**
+ * This is called when the sequence number of debug.atrace.tags.enableflags
+ * changes and we need to reload the enabled tags.
+ **/
+static void atrace_seq_number_changed(uint32_t prev_seq_no, uint32_t seq_no);
+
+void atrace_init() {
+#if defined(__BIONIC__)
+    uint32_t seq_no = __system_property_serial(atrace_property_info);  // Acquire semantics.
+#else
+    uint32_t seq_no = 0;
+#endif
+    uint32_t prev_seq_no = atomic_load_explicit(&last_sequence_number, memory_order_relaxed);
+    if (CC_UNLIKELY(seq_no != prev_seq_no)) {
+        atrace_seq_number_changed(prev_seq_no, seq_no);
+    }
+}
+
+uint64_t atrace_get_enabled_tags()
+{
+    atrace_init();
+    return atrace_enabled_tags;
+}
+#endif
 
 // Set whether this process is debuggable, which determines whether
 // application-level tracing is allowed when the ro.debuggable system property
@@ -136,7 +186,7 @@
 void atrace_update_tags()
 {
     uint64_t tags;
-    if (CC_UNLIKELY(atomic_load_explicit(&atrace_is_ready, memory_order_acquire))) {
+    if (ATRACE_SHMEM || CC_UNLIKELY(atomic_load_explicit(&atrace_is_ready, memory_order_acquire))) {
         if (atomic_load_explicit(&atrace_is_enabled, memory_order_acquire)) {
             tags = atrace_get_property();
             pthread_mutex_lock(&atrace_tags_mutex);
diff --git a/libcutils/trace-host.cpp b/libcutils/trace-host.cpp
index d47cc18..c21d0ee 100644
--- a/libcutils/trace-host.cpp
+++ b/libcutils/trace-host.cpp
@@ -30,3 +30,10 @@
 void atrace_async_end_body(const char* /*name*/, int32_t /*cookie*/) {}
 void atrace_int_body(const char* /*name*/, int32_t /*value*/) {}
 void atrace_int64_body(const char* /*name*/, int64_t /*value*/) {}
+#if ATRACE_SHMEM
+void atrace_init() {}
+uint64_t atrace_get_enabled_tags()
+{
+    return ATRACE_TAG_NOT_READY;
+}
+#endif
diff --git a/liblog/Android.bp b/liblog/Android.bp
index de0c636..7f183c2 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -21,14 +21,11 @@
     "logger_read.cpp",
     "logger_write.cpp",
     "logprint.cpp",
-]
-liblog_host_sources = [
-    "fake_log_device.cpp",
+    "properties.cpp",
 ]
 liblog_target_sources = [
     "event_tag_map.cpp",
     "log_time.cpp",
-    "properties.cpp",
     "pmsg_reader.cpp",
     "pmsg_writer.cpp",
     "logd_reader.cpp",
@@ -39,6 +36,7 @@
     name: "liblog_headers",
     host_supported: true,
     vendor_available: true,
+    ramdisk_available: true,
     recovery_available: true,
     native_bridge_supported: true,
     export_include_dirs: ["include"],
@@ -62,15 +60,12 @@
 cc_library {
     name: "liblog",
     host_supported: true,
+    ramdisk_available: true,
     recovery_available: true,
     native_bridge_supported: true,
     srcs: liblog_sources,
 
     target: {
-        host: {
-            srcs: liblog_host_sources,
-            cflags: ["-DFAKE_LOG_DEVICE=1"],
-        },
         android: {
             version_script: "liblog.map.txt",
             srcs: liblog_target_sources,
@@ -95,12 +90,15 @@
         },
     },
 
-    header_libs: ["liblog_headers"],
+    header_libs: [
+        "libbase_headers",
+        "liblog_headers",
+    ],
     export_header_lib_headers: ["liblog_headers"],
 
     stubs: {
         symbol_file: "liblog.map.txt",
-        versions: ["10000"],
+        versions: ["29", "30"],
     },
 
     // TODO(tomcherry): Renable this before release branch is cut
@@ -123,6 +121,10 @@
     ],
     logtags: ["event.logtags"],
     compile_multilib: "both",
+    apex_available: [
+        "//apex_available:anyapex",
+        "//apex_available:platform",
+    ],
 }
 
 ndk_headers {
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index 2886289..51c5e60 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -36,7 +36,6 @@
 #include <utils/FastStrcmp.h>
 #include <utils/RWLock.h>
 
-#include "log_portability.h"
 #include "logd_reader.h"
 
 #define OUT_TAG "EventTagMap"
diff --git a/liblog/fake_log_device.cpp b/liblog/fake_log_device.cpp
deleted file mode 100644
index fb3b9bc..0000000
--- a/liblog/fake_log_device.cpp
+++ /dev/null
@@ -1,539 +0,0 @@
-/*
- * Copyright (C) 2008-2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/*
- * Intercepts log messages intended for the Android log device.
- * Messages are printed to stderr.
- */
-
-#include "fake_log_device.h"
-
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-
-#include <mutex>
-
-#include <android/log.h>
-#include <log/log_id.h>
-#include <log/logprint.h>
-
-#include "log_portability.h"
-#include "logger.h"
-
-#define kMaxTagLen 16 /* from the long-dead utils/Log.cpp */
-
-#define kTagSetSize 16 /* arbitrary */
-
-#if 0
-#define TRACE(...) printf("fake_log_device: " __VA_ARGS__)
-#else
-#define TRACE(...) ((void)0)
-#endif
-
-static void FakeClose();
-static int FakeWrite(log_id_t log_id, struct timespec* ts, struct iovec* vec, size_t nr);
-
-struct android_log_transport_write fakeLoggerWrite = {
-    .close = FakeClose,
-    .write = FakeWrite,
-};
-
-typedef struct LogState {
-  bool initialized = false;
-  /* global minimum priority */
-  int global_min_priority;
-
-  /* output format */
-  AndroidLogPrintFormat output_format;
-
-  /* tags and priorities */
-  struct {
-    char tag[kMaxTagLen];
-    int minPriority;
-  } tagSet[kTagSetSize];
-} LogState;
-
-static LogState log_state;
-static std::mutex fake_log_mutex;
-
-/*
- * Configure logging based on ANDROID_LOG_TAGS environment variable.  We
- * need to parse a string that looks like
- *
- *   *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i
- *
- * The tag (or '*' for the global level) comes first, followed by a colon
- * and a letter indicating the minimum priority level we're expected to log.
- * This can be used to reveal or conceal logs with specific tags.
- *
- * We also want to check ANDROID_PRINTF_LOG to determine how the output
- * will look.
- */
-void InitializeLogStateLocked() {
-  log_state.initialized = true;
-
-  /* global min priority defaults to "info" level */
-  log_state.global_min_priority = ANDROID_LOG_INFO;
-
-  /*
-   * This is based on the the long-dead utils/Log.cpp code.
-   */
-  const char* tags = getenv("ANDROID_LOG_TAGS");
-  TRACE("Found ANDROID_LOG_TAGS='%s'\n", tags);
-  if (tags != NULL) {
-    int entry = 0;
-
-    while (*tags != '\0') {
-      char tagName[kMaxTagLen];
-      int i, minPrio;
-
-      while (isspace(*tags)) tags++;
-
-      i = 0;
-      while (*tags != '\0' && !isspace(*tags) && *tags != ':' && i < kMaxTagLen) {
-        tagName[i++] = *tags++;
-      }
-      if (i == kMaxTagLen) {
-        TRACE("ERROR: env tag too long (%d chars max)\n", kMaxTagLen - 1);
-        return;
-      }
-      tagName[i] = '\0';
-
-      /* default priority, if there's no ":" part; also zero out '*' */
-      minPrio = ANDROID_LOG_VERBOSE;
-      if (tagName[0] == '*' && tagName[1] == '\0') {
-        minPrio = ANDROID_LOG_DEBUG;
-        tagName[0] = '\0';
-      }
-
-      if (*tags == ':') {
-        tags++;
-        if (*tags >= '0' && *tags <= '9') {
-          if (*tags >= ('0' + ANDROID_LOG_SILENT))
-            minPrio = ANDROID_LOG_VERBOSE;
-          else
-            minPrio = *tags - '\0';
-        } else {
-          switch (*tags) {
-            case 'v':
-              minPrio = ANDROID_LOG_VERBOSE;
-              break;
-            case 'd':
-              minPrio = ANDROID_LOG_DEBUG;
-              break;
-            case 'i':
-              minPrio = ANDROID_LOG_INFO;
-              break;
-            case 'w':
-              minPrio = ANDROID_LOG_WARN;
-              break;
-            case 'e':
-              minPrio = ANDROID_LOG_ERROR;
-              break;
-            case 'f':
-              minPrio = ANDROID_LOG_FATAL;
-              break;
-            case 's':
-              minPrio = ANDROID_LOG_SILENT;
-              break;
-            default:
-              minPrio = ANDROID_LOG_DEFAULT;
-              break;
-          }
-        }
-
-        tags++;
-        if (*tags != '\0' && !isspace(*tags)) {
-          TRACE("ERROR: garbage in tag env; expected whitespace\n");
-          TRACE("       env='%s'\n", tags);
-          return;
-        }
-      }
-
-      if (tagName[0] == 0) {
-        log_state.global_min_priority = minPrio;
-        TRACE("+++ global min prio %d\n", logState->globalMinPriority);
-      } else {
-        log_state.tagSet[entry].minPriority = minPrio;
-        strcpy(log_state.tagSet[entry].tag, tagName);
-        TRACE("+++ entry %d: %s:%d\n", entry, logState->tagSet[entry].tag,
-              logState->tagSet[entry].minPriority);
-        entry++;
-      }
-    }
-  }
-
-  /*
-   * Taken from the long-dead utils/Log.cpp
-   */
-  const char* fstr = getenv("ANDROID_PRINTF_LOG");
-  AndroidLogPrintFormat format;
-  if (fstr == NULL) {
-    format = FORMAT_BRIEF;
-  } else {
-    if (strcmp(fstr, "brief") == 0)
-      format = FORMAT_BRIEF;
-    else if (strcmp(fstr, "process") == 0)
-      format = FORMAT_PROCESS;
-    else if (strcmp(fstr, "tag") == 0)
-      format = FORMAT_PROCESS;
-    else if (strcmp(fstr, "thread") == 0)
-      format = FORMAT_PROCESS;
-    else if (strcmp(fstr, "raw") == 0)
-      format = FORMAT_PROCESS;
-    else if (strcmp(fstr, "time") == 0)
-      format = FORMAT_PROCESS;
-    else if (strcmp(fstr, "long") == 0)
-      format = FORMAT_PROCESS;
-    else
-      format = (AndroidLogPrintFormat)atoi(fstr);  // really?!
-  }
-
-  log_state.output_format = format;
-}
-
-/*
- * Return a human-readable string for the priority level.  Always returns
- * a valid string.
- */
-static const char* getPriorityString(int priority) {
-  /* the first character of each string should be unique */
-  static const char* priorityStrings[] = {"Verbose", "Debug", "Info", "Warn", "Error", "Assert"};
-  int idx;
-
-  idx = (int)priority - (int)ANDROID_LOG_VERBOSE;
-  if (idx < 0 || idx >= (int)(sizeof(priorityStrings) / sizeof(priorityStrings[0])))
-    return "?unknown?";
-  return priorityStrings[idx];
-}
-
-#if defined(_WIN32)
-/*
- * WIN32 does not have writev().
- * Make up something to replace it.
- */
-static ssize_t fake_writev(int fd, const struct iovec* iov, int iovcnt) {
-  ssize_t result = 0;
-  const struct iovec* end = iov + iovcnt;
-  for (; iov < end; iov++) {
-    ssize_t w = write(fd, iov->iov_base, iov->iov_len);
-    if (w != (ssize_t)iov->iov_len) {
-      if (w < 0) return w;
-      return result + w;
-    }
-    result += w;
-  }
-  return result;
-}
-
-#define writev fake_writev
-#endif
-
-/*
- * Write a filtered log message to stderr.
- *
- * Log format parsing taken from the long-dead utils/Log.cpp.
- */
-static void ShowLog(int logPrio, const char* tag, const char* msg) {
-#if !defined(_WIN32)
-  struct tm tmBuf;
-#endif
-  struct tm* ptm;
-  char timeBuf[32];
-  char prefixBuf[128], suffixBuf[128];
-  char priChar;
-  time_t when;
-#if !defined(_WIN32)
-  pid_t pid, tid;
-#else
-  uint32_t pid, tid;
-#endif
-
-  TRACE("LOG %d: %s %s", logPrio, tag, msg);
-
-  priChar = getPriorityString(logPrio)[0];
-  when = time(NULL);
-  pid = tid = getpid();  // find gettid()?
-
-/*
- * Get the current date/time in pretty form
- *
- * It's often useful when examining a log with "less" to jump to
- * a specific point in the file by searching for the date/time stamp.
- * For this reason it's very annoying to have regexp meta characters
- * in the time stamp.  Don't use forward slashes, parenthesis,
- * brackets, asterisks, or other special chars here.
- */
-#if !defined(_WIN32)
-  ptm = localtime_r(&when, &tmBuf);
-#else
-  ptm = localtime(&when);
-#endif
-  // strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm);
-  strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
-
-  /*
-   * Construct a buffer containing the log header and log message.
-   */
-  size_t prefixLen, suffixLen;
-
-  switch (log_state.output_format) {
-    case FORMAT_TAG:
-      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%c/%-8s: ", priChar, tag);
-      strcpy(suffixBuf, "\n");
-      suffixLen = 1;
-      break;
-    case FORMAT_PROCESS:
-      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%c(%5d) ", priChar, pid);
-      suffixLen = snprintf(suffixBuf, sizeof(suffixBuf), "  (%s)\n", tag);
-      break;
-    case FORMAT_THREAD:
-      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%c(%5d:%5d) ", priChar, pid, tid);
-      strcpy(suffixBuf, "\n");
-      suffixLen = 1;
-      break;
-    case FORMAT_RAW:
-      prefixBuf[0] = 0;
-      prefixLen = 0;
-      strcpy(suffixBuf, "\n");
-      suffixLen = 1;
-      break;
-    case FORMAT_TIME:
-      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%s %-8s\n\t", timeBuf, tag);
-      strcpy(suffixBuf, "\n");
-      suffixLen = 1;
-      break;
-    case FORMAT_THREADTIME:
-      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%s %5d %5d %c %-8s \n\t", timeBuf, pid,
-                           tid, priChar, tag);
-      strcpy(suffixBuf, "\n");
-      suffixLen = 1;
-      break;
-    case FORMAT_LONG:
-      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "[ %s %5d:%5d %c/%-8s ]\n", timeBuf, pid,
-                           tid, priChar, tag);
-      strcpy(suffixBuf, "\n\n");
-      suffixLen = 2;
-      break;
-    default:
-      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%c/%-8s(%5d): ", priChar, tag, pid);
-      strcpy(suffixBuf, "\n");
-      suffixLen = 1;
-      break;
-  }
-
-  /*
-   * Figure out how many lines there will be.
-   */
-  const char* end = msg + strlen(msg);
-  size_t numLines = 0;
-  const char* p = msg;
-  while (p < end) {
-    if (*p++ == '\n') numLines++;
-  }
-  if (p > msg && *(p - 1) != '\n') {
-    numLines++;
-  }
-
-  /*
-   * Create an array of iovecs large enough to write all of
-   * the lines with a prefix and a suffix.
-   */
-  const size_t INLINE_VECS = 64;
-  const size_t MAX_LINES = ((size_t)~0) / (3 * sizeof(struct iovec*));
-  struct iovec stackVec[INLINE_VECS];
-  struct iovec* vec = stackVec;
-  size_t numVecs;
-
-  if (numLines > MAX_LINES) numLines = MAX_LINES;
-
-  numVecs = numLines * 3;  // 3 iovecs per line.
-  if (numVecs > INLINE_VECS) {
-    vec = (struct iovec*)malloc(sizeof(struct iovec) * numVecs);
-    if (vec == NULL) {
-      msg = "LOG: write failed, no memory";
-      numVecs = INLINE_VECS;
-      numLines = numVecs / 3;
-      vec = stackVec;
-    }
-  }
-
-  /*
-   * Fill in the iovec pointers.
-   */
-  p = msg;
-  struct iovec* v = vec;
-  int totalLen = 0;
-  while (numLines > 0 && p < end) {
-    if (prefixLen > 0) {
-      v->iov_base = prefixBuf;
-      v->iov_len = prefixLen;
-      totalLen += prefixLen;
-      v++;
-    }
-    const char* start = p;
-    while (p < end && *p != '\n') {
-      p++;
-    }
-    if ((p - start) > 0) {
-      v->iov_base = (void*)start;
-      v->iov_len = p - start;
-      totalLen += p - start;
-      v++;
-    }
-    if (*p == '\n') p++;
-    if (suffixLen > 0) {
-      v->iov_base = suffixBuf;
-      v->iov_len = suffixLen;
-      totalLen += suffixLen;
-      v++;
-    }
-    numLines -= 1;
-  }
-
-  /*
-   * Write the entire message to the log file with a single writev() call.
-   * We need to use this rather than a collection of printf()s on a FILE*
-   * because of multi-threading and multi-process issues.
-   *
-   * If the file was not opened with O_APPEND, this will produce interleaved
-   * output when called on the same file from multiple processes.
-   *
-   * If the file descriptor is actually a network socket, the writev()
-   * call may return with a partial write.  Putting the writev() call in
-   * a loop can result in interleaved data.  This can be alleviated
-   * somewhat by wrapping the writev call in the Mutex.
-   */
-
-  for (;;) {
-    int cc = writev(fileno(stderr), vec, v - vec);
-
-    if (cc == totalLen) break;
-
-    if (cc < 0) {
-      if (errno == EINTR) continue;
-
-      /* can't really log the failure; for now, throw out a stderr */
-      fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno);
-      break;
-    } else {
-      /* shouldn't happen when writing to file or tty */
-      fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", cc, totalLen);
-      break;
-    }
-  }
-
-  /* if we allocated storage for the iovecs, free it */
-  if (vec != stackVec) free(vec);
-}
-
-/*
- * Receive a log message.  We happen to know that "vector" has three parts:
- *
- *  priority (1 byte)
- *  tag (N bytes -- null-terminated ASCII string)
- *  message (N bytes -- null-terminated ASCII string)
- */
-static int FakeWrite(log_id_t log_id, struct timespec*, struct iovec* vector, size_t count) {
-  /* Make sure that no-one frees the LogState while we're using it.
-   * Also guarantees that only one thread is in showLog() at a given
-   * time (if it matters).
-   */
-  auto lock = std::lock_guard{fake_log_mutex};
-
-  if (!log_state.initialized) {
-    InitializeLogStateLocked();
-  }
-
-  if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS || log_id == LOG_ID_SECURITY) {
-    TRACE("%s: ignoring binary log\n", android_log_id_to_name(log_id));
-    int len = 0;
-    for (size_t i = 0; i < count; ++i) {
-      len += vector[i].iov_len;
-    }
-    return len;
-  }
-
-  if (count != 3) {
-    TRACE("%s: writevLog with count=%d not expected\n", android_log_id_to_name(log_id), count);
-    return -1;
-  }
-
-  /* pull out the three fields */
-  int logPrio = *(const char*)vector[0].iov_base;
-  const char* tag = (const char*)vector[1].iov_base;
-  const char* msg = (const char*)vector[2].iov_base;
-
-  /* see if this log tag is configured */
-  int minPrio = log_state.global_min_priority;
-  for (size_t i = 0; i < kTagSetSize; i++) {
-    if (log_state.tagSet[i].minPriority == ANDROID_LOG_UNKNOWN)
-      break; /* reached end of configured values */
-
-    if (strcmp(log_state.tagSet[i].tag, tag) == 0) {
-      minPrio = log_state.tagSet[i].minPriority;
-      break;
-    }
-  }
-
-  if (logPrio >= minPrio) {
-    ShowLog(logPrio, tag, msg);
-  }
-
-  int len = 0;
-  for (size_t i = 0; i < count; ++i) {
-    len += vector[i].iov_len;
-  }
-  return len;
-}
-
-/*
- * Reset out state.
- *
- * The logger API has no means or need to 'stop' or 'close' using the logs,
- * and as such, there is no way for that 'stop' or 'close' to translate into
- * a close operation to the fake log handler. fakeLogClose is provided for
- * completeness only.
- *
- * We have no intention of adding a log close operation as it would complicate
- * every user of the logging API with no gain since the only valid place to
- * call is in the exit handler. Logging can continue in the exit handler to
- * help debug HOST tools ...
- */
-static void FakeClose() {
-  auto lock = std::lock_guard{fake_log_mutex};
-
-  memset(&log_state, 0, sizeof(log_state));
-}
-
-int __android_log_is_loggable(int prio, const char*, int def) {
-  int logLevel = def;
-  return logLevel >= 0 && prio >= logLevel;
-}
-
-int __android_log_is_loggable_len(int prio, const char*, size_t, int def) {
-  int logLevel = def;
-  return logLevel >= 0 && prio >= logLevel;
-}
-
-int __android_log_is_debuggable() {
-  return 1;
-}
diff --git a/liblog/fake_log_device.h b/liblog/fake_log_device.h
deleted file mode 100644
index bd2256c..0000000
--- a/liblog/fake_log_device.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <sys/types.h>
-
-#include "log_portability.h"
-#include "uio.h"
-
-struct iovec;
-
-__BEGIN_DECLS
-
-int fakeLogOpen(const char* pathName);
-int fakeLogClose(int fd);
-ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count);
-
-int __android_log_is_loggable(int prio, const char*, int def);
-int __android_log_is_loggable_len(int prio, const char*, size_t, int def);
-int __android_log_is_debuggable();
-
-__END_DECLS
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
index 7290789..c98455d 100644
--- a/liblog/include/android/log.h
+++ b/liblog/include/android/log.h
@@ -55,6 +55,7 @@
  */
 
 #include <stdarg.h>
+#include <stddef.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -152,6 +153,12 @@
 } log_id_t;
 
 /**
+ * Let the logging function choose the best log target.
+ * This is not part of the enum since adding either -1 or 0xFFFFFFFF forces the enum to be signed or
+ * unsigned, which breaks unfortunately common arithmetic against LOG_ID_MIN and LOG_ID_MAX. */
+#define LOG_ID_DEFAULT -1
+
+/**
  * Writes the constant string `text` to the log buffer `id`,
  * with priority `prio` and tag `tag`.
  *
@@ -170,6 +177,112 @@
 int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...)
     __attribute__((__format__(printf, 4, 5)));
 
+/**
+ * Logger data struct used for writing log messages to liblog via __android_log_write_logger_data()
+ * and sending log messages to user defined loggers specified in __android_log_set_logger().
+ */
+struct __android_logger_data {
+  size_t struct_size; /* Must be set to sizeof(__android_logger_data) and is used for versioning. */
+  int buffer_id;      /* log_id_t or -1 to represent 'default'. */
+  int priority;       /* android_LogPriority values. */
+  const char* tag;
+  const char* file;  /* Optional file name, may be set to nullptr. */
+  unsigned int line; /* Optional line number, ignore if file is nullptr. */
+};
+
+/**
+ * Writes the log message specified with logger_data and msg to the log.  logger_data includes
+ * additional file name and line number information that a logger may use.  logger_data is versioned
+ * for backwards compatibility.
+ * This assumes that loggability has already been checked through __android_log_is_loggable().
+ * Higher level logging libraries, such as libbase, first check loggability, then format their
+ * buffers, then pass the message to liblog via this function, and therefore we do not want to
+ * duplicate the loggability check here.
+ */
+void __android_log_write_logger_data(struct __android_logger_data* logger_data, const char* msg);
+
+/**
+ * Prototype for the 'logger' function that is called for every log message.
+ */
+typedef void (*__android_logger_function)(const struct __android_logger_data* logger_data,
+                                          const char* message);
+
+/**
+ * Sets a user defined logger function.  All log messages sent to liblog will be set to the
+ * function pointer specified by logger for processing.
+ */
+void __android_log_set_logger(__android_logger_function logger);
+
+/**
+ * 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.
+ */
+void __android_log_logd_logger(const struct __android_logger_data* logger_data, const char* msg);
+
+/**
+ * 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.
+ */
+void __android_log_stderr_logger(const struct __android_logger_data* logger_data,
+                                 const char* message);
+
+/**
+ * Prototype for the 'abort' function that is called when liblog will abort due to
+ * __android_log_assert() failures.
+ */
+typedef void (*__android_aborter_function)(const char* abort_message);
+
+/**
+ * Sets a user defined aborter function that is called for __android_log_assert() failures.
+ */
+void __android_log_set_aborter(__android_aborter_function aborter);
+
+/**
+ * Calls the stored aborter function.  This allows for other logging libraries to use the same
+ * aborter function by calling this function in liblog.
+ */
+void __android_log_call_aborter(const char* abort_message);
+
+/**
+ * Sets android_set_abort_message() on device then aborts().  This is the default aborter.
+ */
+void __android_log_default_aborter(const char* abort_message);
+
+/**
+ * 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.
+ *
+ * prio is ANDROID_LOG_VERBOSE to ANDROID_LOG_FATAL.
+ */
+int __android_log_is_loggable(int prio, const char* tag, int default_prio);
+int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio);
+
+/**
+ * Sets the minimum priority that will be logged for this process.
+ *
+ * This returns the previous set minimum priority, or ANDROID_LOG_DEFAULT if none was set.
+ */
+int __android_log_set_minimum_priority(int priority);
+
+/**
+ * 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.
+ */
+int __android_log_get_minimum_priority(void);
+
+/**
+ * Sets the default tag if no tag is provided when writing a log message.  Defaults to
+ * getprogname().  This truncates tag to the maximum log message size, though appropriate tags
+ * should be much smaller.
+ */
+void __android_log_set_default_tag(const char* tag);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/liblog/include/log/log_main.h b/liblog/include/log/log_main.h
index 64791c2..1bd1c8a 100644
--- a/liblog/include/log/log_main.h
+++ b/liblog/include/log/log_main.h
@@ -56,7 +56,7 @@
 /*
  * Use __VA_ARGS__ if running a static analyzer,
  * to avoid warnings of unused variables in __VA_ARGS__.
- * Use contexpr function in C++ mode, so these macros can be used
+ * Use constexpr function in C++ mode, so these macros can be used
  * in other constexpr functions without warning.
  */
 #ifdef __clang_analyzer__
@@ -131,10 +131,10 @@
  * is -inverted- from the normal assert() semantics.
  */
 #ifndef LOG_ALWAYS_FATAL_IF
-#define LOG_ALWAYS_FATAL_IF(cond, ...)                              \
-  ((__predict_false(cond))                                          \
-       ? ((void)android_printAssert(#cond, LOG_TAG, ##__VA_ARGS__)) \
-       : __FAKE_USE_VA_ARGS(__VA_ARGS__))
+#define LOG_ALWAYS_FATAL_IF(cond, ...)                                                    \
+  ((__predict_false(cond)) ? (__FAKE_USE_VA_ARGS(__VA_ARGS__),                            \
+                              ((void)android_printAssert(#cond, LOG_TAG, ##__VA_ARGS__))) \
+                           : ((void)0))
 #endif
 
 #ifndef LOG_ALWAYS_FATAL
@@ -213,9 +213,10 @@
 #if LOG_NDEBUG
 #define ALOGV_IF(cond, ...) __FAKE_USE_VA_ARGS(__VA_ARGS__)
 #else
-#define ALOGV_IF(cond, ...)                                                  \
-  ((__predict_false(cond)) ? ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
-                           : __FAKE_USE_VA_ARGS(__VA_ARGS__))
+#define ALOGV_IF(cond, ...)                                                               \
+  ((__predict_false(cond))                                                                \
+       ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
+       : ((void)0))
 #endif
 #endif
 
@@ -227,9 +228,10 @@
 #endif
 
 #ifndef ALOGD_IF
-#define ALOGD_IF(cond, ...)                                                \
-  ((__predict_false(cond)) ? ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
-                           : __FAKE_USE_VA_ARGS(__VA_ARGS__))
+#define ALOGD_IF(cond, ...)                                                             \
+  ((__predict_false(cond))                                                              \
+       ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
+       : ((void)0))
 #endif
 
 /*
@@ -240,9 +242,10 @@
 #endif
 
 #ifndef ALOGI_IF
-#define ALOGI_IF(cond, ...)                                               \
-  ((__predict_false(cond)) ? ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \
-                           : __FAKE_USE_VA_ARGS(__VA_ARGS__))
+#define ALOGI_IF(cond, ...)                                                            \
+  ((__predict_false(cond))                                                             \
+       ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \
+       : ((void)0))
 #endif
 
 /*
@@ -253,9 +256,10 @@
 #endif
 
 #ifndef ALOGW_IF
-#define ALOGW_IF(cond, ...)                                               \
-  ((__predict_false(cond)) ? ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \
-                           : __FAKE_USE_VA_ARGS(__VA_ARGS__))
+#define ALOGW_IF(cond, ...)                                                            \
+  ((__predict_false(cond))                                                             \
+       ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \
+       : ((void)0))
 #endif
 
 /*
@@ -266,9 +270,10 @@
 #endif
 
 #ifndef ALOGE_IF
-#define ALOGE_IF(cond, ...)                                                \
-  ((__predict_false(cond)) ? ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
-                           : __FAKE_USE_VA_ARGS(__VA_ARGS__))
+#define ALOGE_IF(cond, ...)                                                             \
+  ((__predict_false(cond))                                                              \
+       ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
+       : ((void)0))
 #endif
 
 /* --------------------------------------------------------------------- */
diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt
index 2dd8059..198cdae 100644
--- a/liblog/liblog.map.txt
+++ b/liblog/liblog.map.txt
@@ -54,18 +54,32 @@
     __android_log_is_debuggable; # apex llndk
 };
 
-LIBLOG_Q {
+LIBLOG_Q { # introduced=29
   global:
     __android_log_bswrite; # apex
     __android_log_btwrite; # apex
     __android_log_bwrite; # apex
     __android_log_close; # apex
     __android_log_security; # apex
-    __android_log_security_bswrite; # apex
     android_log_reset; # llndk
     android_log_parser_reset; # llndk
 };
 
+LIGLOG_R { # introduced=30
+  global:
+    __android_log_call_aborter;
+    __android_log_default_aborter;
+    __android_log_get_minimum_priority;
+    __android_log_logd_logger;
+    __android_log_security_bswrite; # apex
+    __android_log_set_aborter;
+    __android_log_set_default_tag;
+    __android_log_set_logger;
+    __android_log_set_minimum_priority;
+    __android_log_stderr_logger;
+    __android_log_write_logger_data;
+};
+
 LIBLOG_PRIVATE {
   global:
     __android_log_pmsg_file_read;
diff --git a/liblog/log_event_list.cpp b/liblog/log_event_list.cpp
index e9f4a32..cb70d48 100644
--- a/liblog/log_event_list.cpp
+++ b/liblog/log_event_list.cpp
@@ -25,8 +25,6 @@
 #include <log/log_event_list.h>
 #include <private/android_logger.h>
 
-#include "log_portability.h"
-
 #define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))
 
 enum ReadWriteFlag {
diff --git a/liblog/log_event_write.cpp b/liblog/log_event_write.cpp
index d04ba90..39afd0c 100644
--- a/liblog/log_event_write.cpp
+++ b/liblog/log_event_write.cpp
@@ -20,8 +20,6 @@
 #include <log/log.h>
 #include <log/log_event_list.h>
 
-#include "log_portability.h"
-
 #define MAX_SUBTAG_LEN 32
 
 int __android_log_error_write(int tag, const char* subTag, int32_t uid, const char* data,
diff --git a/liblog/log_portability.h b/liblog/log_portability.h
deleted file mode 100644
index b7279d1..0000000
--- a/liblog/log_portability.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <sys/cdefs.h>
-#include <unistd.h>
-
-/* possible missing definitions in sys/cdefs.h */
-
-/* DECLS */
-#ifndef __BEGIN_DECLS
-#if defined(__cplusplus)
-#define __BEGIN_DECLS extern "C" {
-#define __END_DECLS }
-#else
-#define __BEGIN_DECLS
-#define __END_DECLS
-#endif
-#endif
-
-/* possible missing definitions in unistd.h */
-
-#ifndef TEMP_FAILURE_RETRY
-/* Used to retry syscalls that can return EINTR. */
-#define TEMP_FAILURE_RETRY(exp)            \
-  ({                                       \
-    __typeof__(exp) _rc;                   \
-    do {                                   \
-      _rc = (exp);                         \
-    } while (_rc == -1 && errno == EINTR); \
-    _rc;                                   \
-  })
-#endif
diff --git a/liblog/log_time.cpp b/liblog/log_time.cpp
index 3ae250f..3fbe1cb 100644
--- a/liblog/log_time.cpp
+++ b/liblog/log_time.cpp
@@ -21,8 +21,6 @@
 
 #include <private/android_logger.h>
 
-#include "log_portability.h"
-
 const char log_time::default_format[] = "%m-%d %H:%M:%S.%q";
 const timespec log_time::EPOCH = {0, 0};
 
diff --git a/liblog/logd_reader.cpp b/liblog/logd_reader.cpp
index 6865c14..82ed6b2 100644
--- a/liblog/logd_reader.cpp
+++ b/liblog/logd_reader.cpp
@@ -35,8 +35,6 @@
 
 #include <string>
 
-#include <cutils/sockets.h>
-#include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
 #include "logger.h"
diff --git a/liblog/logd_reader.h b/liblog/logd_reader.h
index 2d032fa..68eef02 100644
--- a/liblog/logd_reader.h
+++ b/liblog/logd_reader.h
@@ -16,10 +16,10 @@
 
 #pragma once
 
+#include <sys/cdefs.h>
 #include <unistd.h>
 
 #include "log/log_read.h"
-#include "log_portability.h"
 
 __BEGIN_DECLS
 
diff --git a/liblog/logd_writer.cpp b/liblog/logd_writer.cpp
index 3c6eb69..67376f4 100644
--- a/liblog/logd_writer.cpp
+++ b/liblog/logd_writer.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "logd_writer.h"
+
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
@@ -32,23 +34,13 @@
 
 #include <shared_mutex>
 
-#include <cutils/sockets.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
-#include "log_portability.h"
 #include "logger.h"
 #include "rwlock.h"
 #include "uio.h"
 
-static int LogdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
-static void LogdClose();
-
-struct android_log_transport_write logdLoggerWrite = {
-    .close = LogdClose,
-    .write = LogdWrite,
-};
-
 static int logd_socket;
 static RwLock logd_socket_lock;
 
@@ -90,7 +82,7 @@
   OpenSocketLocked();
 }
 
-static void LogdClose() {
+void LogdClose() {
   auto lock = std::unique_lock{logd_socket_lock};
   if (logd_socket > 0) {
     close(logd_socket);
@@ -98,7 +90,7 @@
   logd_socket = 0;
 }
 
-static int LogdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
+int LogdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
   ssize_t ret;
   static const unsigned headerLength = 1;
   struct iovec newVec[nr + headerLength];
@@ -119,7 +111,7 @@
   }
 
   /* logd, after initialization and priv drop */
-  if (__android_log_uid() == AID_LOGD) {
+  if (getuid() == AID_LOGD) {
     /*
      * ignore log messages we send to ourself (logd).
      * Such log messages are often generated by libraries we depend on
diff --git a/liblog/logd_writer.h b/liblog/logd_writer.h
new file mode 100644
index 0000000..41197b5
--- /dev/null
+++ b/liblog/logd_writer.h
@@ -0,0 +1,24 @@
+/*
+ * 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 <stddef.h>
+
+#include <android/log.h>
+
+int LogdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
+void LogdClose();
diff --git a/liblog/logger.h b/liblog/logger.h
index 40d5fe5..ddff19d 100644
--- a/liblog/logger.h
+++ b/liblog/logger.h
@@ -17,22 +17,14 @@
 #pragma once
 
 #include <stdatomic.h>
+#include <sys/cdefs.h>
 
-#include <cutils/list.h>
 #include <log/log.h>
 
-#include "log_portability.h"
 #include "uio.h"
 
 __BEGIN_DECLS
 
-struct android_log_transport_write {
-  void (*close)(); /* free up resources */
-  /* write log to transport, returns number of bytes propagated, or -errno */
-  int (*write)(log_id_t logId, struct timespec* ts, struct iovec* vec,
-               size_t nr);
-};
-
 struct logger_list {
   atomic_int fd;
   int mode;
@@ -56,18 +48,4 @@
   return reinterpret_cast<uintptr_t>(logger) & LOGGER_LOGD;
 }
 
-/* OS specific dribs and drabs */
-
-#if defined(_WIN32)
-#include <private/android_filesystem_config.h>
-typedef uint32_t uid_t;
-static inline uid_t __android_log_uid() {
-  return AID_SYSTEM;
-}
-#else
-static inline uid_t __android_log_uid() {
-  return getuid();
-}
-#endif
-
 __END_DECLS
diff --git a/liblog/logger_name.cpp b/liblog/logger_name.cpp
index ece0550..7d676f4 100644
--- a/liblog/logger_name.cpp
+++ b/liblog/logger_name.cpp
@@ -19,8 +19,6 @@
 
 #include <log/log.h>
 
-#include "log_portability.h"
-
 /* In the future, we would like to make this list extensible */
 static const char* LOG_NAME[LOG_ID_MAX] = {
     /* clang-format off */
diff --git a/liblog/logger_read.cpp b/liblog/logger_read.cpp
index 0d383ff..4937042 100644
--- a/liblog/logger_read.cpp
+++ b/liblog/logger_read.cpp
@@ -27,10 +27,7 @@
 #include <unistd.h>
 
 #include <android/log.h>
-#include <cutils/list.h>
-#include <private/android_filesystem_config.h>
 
-#include "log_portability.h"
 #include "logd_reader.h"
 #include "logger.h"
 #include "pmsg_reader.h"
@@ -100,7 +97,7 @@
 
   int ret = 0;
 
-#if (FAKE_LOG_DEVICE == 0)
+#ifdef __ANDROID__
   if (logger_list->mode & ANDROID_LOG_PSTORE) {
     ret = PmsgRead(logger_list, log_msg);
   } else {
@@ -112,8 +109,8 @@
     return ret;
   }
 
-  if (ret > (int)sizeof(*log_msg)) {
-    ret = sizeof(*log_msg);
+  if (ret > LOGGER_ENTRY_MAX_LEN) {
+    ret = LOGGER_ENTRY_MAX_LEN;
   }
 
   if (ret < static_cast<int>(sizeof(log_msg->entry))) {
@@ -121,7 +118,7 @@
   }
 
   if (log_msg->entry.hdr_size < sizeof(log_msg->entry) ||
-      log_msg->entry.hdr_size >= sizeof(struct log_msg) - sizeof(log_msg->entry)) {
+      log_msg->entry.hdr_size >= LOGGER_ENTRY_MAX_LEN - sizeof(log_msg->entry)) {
     return -EINVAL;
   }
 
@@ -129,6 +126,8 @@
     return -EINVAL;
   }
 
+  log_msg->buf[log_msg->entry.len + log_msg->entry.hdr_size] = '\0';
+
   return ret;
 }
 
@@ -138,7 +137,7 @@
     return;
   }
 
-#if (FAKE_LOG_DEVICE == 0)
+#ifdef __ANDROID__
   if (logger_list->mode & ANDROID_LOG_PSTORE) {
     PmsgClose(logger_list);
   } else {
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
index d38b402..cf82e0f 100644
--- a/liblog/logger_write.cpp
+++ b/liblog/logger_write.cpp
@@ -14,8 +14,11 @@
  * limitations under the License.
  */
 
+#include "logger_write.h"
+
 #include <errno.h>
-#include <stdatomic.h>
+#include <inttypes.h>
+#include <libgen.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/time.h>
@@ -24,31 +27,39 @@
 #include <android/set_abort_message.h>
 #endif
 
+#include <shared_mutex>
+
+#include <android-base/errno_restorer.h>
+#include <android-base/macros.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
-#include "log_portability.h"
+#include "android/log.h"
+#include "log/log_read.h"
 #include "logger.h"
+#include "rwlock.h"
 #include "uio.h"
 
+#ifdef __ANDROID__
+#include "logd_writer.h"
+#include "pmsg_writer.h"
+#endif
+
+#if defined(__APPLE__)
+#include <pthread.h>
+#elif defined(__linux__) && !defined(__ANDROID__)
+#include <syscall.h>
+#elif defined(_WIN32)
+#include <windows.h>
+#endif
+
+using android::base::ErrnoRestorer;
+
 #define LOG_BUF_SIZE 1024
 
-#if (FAKE_LOG_DEVICE == 0)
-extern struct android_log_transport_write logdLoggerWrite;
-extern struct android_log_transport_write pmsgLoggerWrite;
-
-android_log_transport_write* android_log_write = &logdLoggerWrite;
-android_log_transport_write* android_log_persist_write = &pmsgLoggerWrite;
-#else
-extern android_log_transport_write fakeLoggerWrite;
-
-android_log_transport_write* android_log_write = &fakeLoggerWrite;
-android_log_transport_write* android_log_persist_write = nullptr;
-#endif
-
 #if defined(__ANDROID__)
 static int check_log_uid_permissions() {
-  uid_t uid = __android_log_uid();
+  uid_t uid = getuid();
 
   /* Matches clientHasLogCredentials() in logd */
   if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)) {
@@ -92,121 +103,268 @@
  * Release any logger resources. A new log write will immediately re-acquire.
  */
 void __android_log_close() {
-  if (android_log_write != nullptr) {
-    android_log_write->close();
-  }
-
-  if (android_log_persist_write != nullptr) {
-    android_log_persist_write->close();
-  }
-
+#ifdef __ANDROID__
+  LogdClose();
+  PmsgClose();
+#endif
 }
 
-static int write_to_log(log_id_t log_id, struct iovec* vec, size_t nr) {
-  int ret, save_errno;
-  struct timespec ts;
+#if defined(__GLIBC__) || defined(_WIN32)
+static const char* getprogname() {
+#if defined(__GLIBC__)
+  return program_invocation_short_name;
+#elif defined(_WIN32)
+  static bool first = true;
+  static char progname[MAX_PATH] = {};
 
-  save_errno = errno;
+  if (first) {
+    char path[PATH_MAX + 1];
+    DWORD result = GetModuleFileName(nullptr, path, sizeof(path) - 1);
+    if (result == 0 || result == sizeof(path) - 1) return "";
+    path[PATH_MAX - 1] = 0;
+
+    char* path_basename = basename(path);
+
+    snprintf(progname, sizeof(progname), "%s", path_basename);
+    first = false;
+  }
+
+  return progname;
+#endif
+}
+#endif
+
+// It's possible for logging to happen during static initialization before our globals are
+// initialized, so we place this std::string in a function such that it is initialized on the first
+// call.
+std::string& GetDefaultTag() {
+  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);
+}
+
+static int minimum_log_priority = ANDROID_LOG_DEFAULT;
+int __android_log_set_minimum_priority(int priority) {
+  int old_minimum_log_priority = minimum_log_priority;
+  minimum_log_priority = priority;
+  return old_minimum_log_priority;
+}
+
+int __android_log_get_minimum_priority() {
+  return minimum_log_priority;
+}
+
+#ifdef __ANDROID__
+static __android_logger_function logger_function = __android_log_logd_logger;
+#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;
+}
+
+void __android_log_default_aborter(const char* abort_message) {
+#ifdef __ANDROID__
+  android_set_abort_message(abort_message);
+#else
+  UNUSED(abort_message);
+#endif
+  abort();
+}
+
+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);
+}
+
+#ifdef __ANDROID__
+static int write_to_log(log_id_t log_id, struct iovec* vec, size_t nr) {
+  int ret;
+  struct timespec ts;
 
   if (log_id == LOG_ID_KERNEL) {
     return -EINVAL;
   }
 
-#if defined(__ANDROID__)
   clock_gettime(android_log_clockid(), &ts);
 
   if (log_id == LOG_ID_SECURITY) {
     if (vec[0].iov_len < 4) {
-      errno = save_errno;
       return -EINVAL;
     }
 
     ret = check_log_uid_permissions();
     if (ret < 0) {
-      errno = save_errno;
       return ret;
     }
     if (!__android_log_security()) {
       /* If only we could reset downstream logd counter */
-      errno = save_errno;
       return -EPERM;
     }
   } else if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) {
     if (vec[0].iov_len < 4) {
-      errno = save_errno;
       return -EINVAL;
     }
-  } else {
-    int prio = *static_cast<int*>(vec[0].iov_base);
-    const char* tag = static_cast<const char*>(vec[1].iov_base);
-    size_t len = vec[1].iov_len;
+  }
 
-    if (!__android_log_is_loggable_len(prio, tag, len - 1, ANDROID_LOG_VERBOSE)) {
-      errno = save_errno;
-      return -EPERM;
-    }
-  }
+  ret = LogdWrite(log_id, &ts, vec, nr);
+  PmsgWrite(log_id, &ts, vec, nr);
+
+  return ret;
+}
 #else
-  /* simulate clock_gettime(CLOCK_REALTIME, &ts); */
-  {
-    struct timeval tv;
-    gettimeofday(&tv, NULL);
-    ts.tv_sec = tv.tv_sec;
-    ts.tv_nsec = tv.tv_usec * 1000;
-  }
+static int write_to_log(log_id_t, struct iovec*, size_t) {
+  // Non-Android text logs should go to __android_log_stderr_logger, not here.
+  // Non-Android binary logs are always dropped.
+  return 1;
+}
 #endif
 
-  ret = 0;
+// Copied from base/threads.cpp
+static uint64_t GetThreadId() {
+#if defined(__BIONIC__)
+  return gettid();
+#elif defined(__APPLE__)
+  uint64_t tid;
+  pthread_threadid_np(NULL, &tid);
+  return tid;
+#elif defined(__linux__)
+  return syscall(__NR_gettid);
+#elif defined(_WIN32)
+  return GetCurrentThreadId();
+#endif
+}
 
-  if (android_log_write != nullptr) {
-    ssize_t retval;
-    retval = android_log_write->write(log_id, &ts, vec, nr);
-    if (ret >= 0) {
-      ret = retval;
-    }
+void __android_log_stderr_logger(const struct __android_logger_data* logger_data,
+                                 const char* message) {
+  struct tm now;
+  time_t t = time(nullptr);
+
+#if defined(_WIN32)
+  localtime_s(&now, &t);
+#else
+  localtime_r(&t, &now);
+#endif
+
+  char timestamp[32];
+  strftime(timestamp, sizeof(timestamp), "%m-%d %H:%M:%S", &now);
+
+  static const char log_characters[] = "XXVDIWEF";
+  static_assert(arraysize(log_characters) - 1 == ANDROID_LOG_SILENT,
+                "Mismatch in size of log_characters and values in android_LogPriority");
+  int priority =
+      logger_data->priority > ANDROID_LOG_SILENT ? ANDROID_LOG_FATAL : logger_data->priority;
+  char priority_char = log_characters[priority];
+  uint64_t tid = GetThreadId();
+
+  if (logger_data->file != nullptr) {
+    fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s:%u] %s\n",
+            logger_data->tag ? logger_data->tag : "nullptr", priority_char, timestamp, getpid(),
+            tid, logger_data->file, logger_data->line, message);
+  } else {
+    fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s\n",
+            logger_data->tag ? logger_data->tag : "nullptr", priority_char, timestamp, getpid(),
+            tid, message);
   }
+}
 
-  if (android_log_persist_write != nullptr) {
-    android_log_persist_write->write(log_id, &ts, vec, nr);
-  }
+void __android_log_logd_logger(const struct __android_logger_data* logger_data,
+                               const char* message) {
+  int buffer_id = logger_data->buffer_id == LOG_ID_DEFAULT ? LOG_ID_MAIN : logger_data->buffer_id;
 
-  errno = save_errno;
-  return ret;
+  struct iovec vec[3];
+  vec[0].iov_base =
+      const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(&logger_data->priority));
+  vec[0].iov_len = 1;
+  vec[1].iov_base = const_cast<void*>(static_cast<const void*>(logger_data->tag));
+  vec[1].iov_len = strlen(logger_data->tag) + 1;
+  vec[2].iov_base = const_cast<void*>(static_cast<const void*>(message));
+  vec[2].iov_len = strlen(message) + 1;
+
+  write_to_log(static_cast<log_id_t>(buffer_id), vec, 3);
 }
 
 int __android_log_write(int prio, const char* tag, const char* msg) {
   return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
 }
 
-int __android_log_buf_write(int bufID, int prio, const char* tag, const char* msg) {
-  if (!tag) tag = "";
+void __android_log_write_logger_data(__android_logger_data* logger_data, const char* msg) {
+  ErrnoRestorer errno_restorer;
+
+  if (logger_data->buffer_id != LOG_ID_DEFAULT && logger_data->buffer_id != LOG_ID_MAIN &&
+      logger_data->buffer_id != LOG_ID_SYSTEM && logger_data->buffer_id != LOG_ID_RADIO &&
+      logger_data->buffer_id != LOG_ID_CRASH) {
+    return;
+  }
+
+  auto tag_lock = std::shared_lock{default_tag_lock, std::defer_lock};
+  if (logger_data->tag == nullptr) {
+    tag_lock.lock();
+    logger_data->tag = GetDefaultTag().c_str();
+  }
 
 #if __BIONIC__
-  if (prio == ANDROID_LOG_FATAL) {
+  if (logger_data->priority == ANDROID_LOG_FATAL) {
     android_set_abort_message(msg);
   }
 #endif
 
-  struct iovec vec[3];
-  vec[0].iov_base = (unsigned char*)&prio;
-  vec[0].iov_len = 1;
-  vec[1].iov_base = (void*)tag;
-  vec[1].iov_len = strlen(tag) + 1;
-  vec[2].iov_base = (void*)msg;
-  vec[2].iov_len = strlen(msg) + 1;
+  auto lock = std::shared_lock{logger_function_lock};
+  logger_function(logger_data, msg);
+}
 
-  return write_to_log(static_cast<log_id_t>(bufID), vec, 3);
+int __android_log_buf_write(int bufID, int prio, const char* tag, const char* msg) {
+  ErrnoRestorer errno_restorer;
+
+  if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+    return 0;
+  }
+
+  __android_logger_data logger_data = {sizeof(__android_logger_data), bufID, prio, tag, nullptr, 0};
+  __android_log_write_logger_data(&logger_data, msg);
+  return 1;
 }
 
 int __android_log_vprint(int prio, const char* tag, const char* fmt, va_list ap) {
+  ErrnoRestorer errno_restorer;
+
+  if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+    return 0;
+  }
+
   char buf[LOG_BUF_SIZE];
 
   vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
 
-  return __android_log_write(prio, tag, buf);
+  __android_logger_data logger_data = {
+      sizeof(__android_logger_data), LOG_ID_MAIN, prio, tag, nullptr, 0};
+  __android_log_write_logger_data(&logger_data, buf);
+  return 1;
 }
 
 int __android_log_print(int prio, const char* tag, const char* fmt, ...) {
+  ErrnoRestorer errno_restorer;
+
+  if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+    return 0;
+  }
+
   va_list ap;
   char buf[LOG_BUF_SIZE];
 
@@ -214,10 +372,19 @@
   vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
   va_end(ap);
 
-  return __android_log_write(prio, tag, buf);
+  __android_logger_data logger_data = {
+      sizeof(__android_logger_data), LOG_ID_MAIN, prio, tag, nullptr, 0};
+  __android_log_write_logger_data(&logger_data, buf);
+  return 1;
 }
 
 int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...) {
+  ErrnoRestorer errno_restorer;
+
+  if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+    return 0;
+  }
+
   va_list ap;
   char buf[LOG_BUF_SIZE];
 
@@ -225,7 +392,9 @@
   vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
   va_end(ap);
 
-  return __android_log_buf_write(bufID, prio, tag, buf);
+  __android_logger_data logger_data = {sizeof(__android_logger_data), bufID, prio, tag, nullptr, 0};
+  __android_log_write_logger_data(&logger_data, buf);
+  return 1;
 }
 
 void __android_log_assert(const char* cond, const char* tag, const char* fmt, ...) {
@@ -253,11 +422,13 @@
   TEMP_FAILURE_RETRY(write(2, "\n", 1));
 
   __android_log_write(ANDROID_LOG_FATAL, tag, buf);
-  abort(); /* abort so we have a chance to debug the situation */
-           /* NOTREACHED */
+  __android_log_call_aborter(buf);
+  abort();
 }
 
 int __android_log_bwrite(int32_t tag, const void* payload, size_t len) {
+  ErrnoRestorer errno_restorer;
+
   struct iovec vec[2];
 
   vec[0].iov_base = &tag;
@@ -269,6 +440,8 @@
 }
 
 int __android_log_stats_bwrite(int32_t tag, const void* payload, size_t len) {
+  ErrnoRestorer errno_restorer;
+
   struct iovec vec[2];
 
   vec[0].iov_base = &tag;
@@ -280,6 +453,8 @@
 }
 
 int __android_log_security_bwrite(int32_t tag, const void* payload, size_t len) {
+  ErrnoRestorer errno_restorer;
+
   struct iovec vec[2];
 
   vec[0].iov_base = &tag;
@@ -296,6 +471,8 @@
  * handy if we just want to dump an integer into the log.
  */
 int __android_log_btwrite(int32_t tag, char type, const void* payload, size_t len) {
+  ErrnoRestorer errno_restorer;
+
   struct iovec vec[3];
 
   vec[0].iov_base = &tag;
@@ -313,6 +490,8 @@
  * event log.
  */
 int __android_log_bswrite(int32_t tag, const char* payload) {
+  ErrnoRestorer errno_restorer;
+
   struct iovec vec[4];
   char type = EVENT_TYPE_STRING;
   uint32_t len = strlen(payload);
@@ -334,6 +513,8 @@
  * security log.
  */
 int __android_log_security_bswrite(int32_t tag, const char* payload) {
+  ErrnoRestorer errno_restorer;
+
   struct iovec vec[4];
   char type = EVENT_TYPE_STRING;
   uint32_t len = strlen(payload);
diff --git a/liblog/logger_write.h b/liblog/logger_write.h
new file mode 100644
index 0000000..065fd55
--- /dev/null
+++ b/liblog/logger_write.h
@@ -0,0 +1,24 @@
+/*
+ * 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 <string>
+
+#include "rwlock.h"
+
+std::string& GetDefaultTag();  // Must read lock default_tag_lock
+extern RwLock default_tag_lock;
\ No newline at end of file
diff --git a/liblog/logprint.cpp b/liblog/logprint.cpp
index 4b61828..e32878a 100644
--- a/liblog/logprint.cpp
+++ b/liblog/logprint.cpp
@@ -40,8 +40,6 @@
 #include <log/logprint.h>
 #include <private/android_logger.h>
 
-#include "log_portability.h"
-
 #define MS_PER_NSEC 1000000
 #define US_PER_NSEC 1000
 
@@ -1522,12 +1520,7 @@
  * This code is Android specific, bionic guarantees that
  * calls to non-reentrant getpwuid() are thread safe.
  */
-#if !defined(__MINGW32__)
-#if (FAKE_LOG_DEVICE == 0)
-#ifndef __BIONIC__
-#warning "This code assumes that getpwuid is thread safe, only true with Bionic!"
-#endif
-#endif
+#ifdef __ANDROID__
       struct passwd* pwd = getpwuid(entry->uid);
       if (pwd && (strlen(pwd->pw_name) <= 5)) {
         snprintf(uid, sizeof(uid), "%5s:", pwd->pw_name);
diff --git a/liblog/pmsg_reader.cpp b/liblog/pmsg_reader.cpp
index 9390fec..64a92b7 100644
--- a/liblog/pmsg_reader.cpp
+++ b/liblog/pmsg_reader.cpp
@@ -23,7 +23,7 @@
 #include <string.h>
 #include <sys/types.h>
 
-#include <private/android_filesystem_config.h>
+#include <cutils/list.h>
 #include <private/android_logger.h>
 
 #include "logger.h"
diff --git a/liblog/pmsg_reader.h b/liblog/pmsg_reader.h
index 53746d8..b784f9f 100644
--- a/liblog/pmsg_reader.h
+++ b/liblog/pmsg_reader.h
@@ -16,10 +16,10 @@
 
 #pragma once
 
+#include <sys/cdefs.h>
 #include <unistd.h>
 
 #include "log/log_read.h"
-#include "log_portability.h"
 
 __BEGIN_DECLS
 
diff --git a/liblog/pmsg_writer.cpp b/liblog/pmsg_writer.cpp
index 4f45780..06e5e04 100644
--- a/liblog/pmsg_writer.cpp
+++ b/liblog/pmsg_writer.cpp
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-/*
- * pmsg write handler
- */
+#include "pmsg_writer.h"
 
 #include <errno.h>
 #include <fcntl.h>
@@ -28,22 +26,12 @@
 #include <shared_mutex>
 
 #include <log/log_properties.h>
-#include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
-#include "log_portability.h"
 #include "logger.h"
 #include "rwlock.h"
 #include "uio.h"
 
-static void PmsgClose();
-static int PmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
-
-struct android_log_transport_write pmsgLoggerWrite = {
-    .close = PmsgClose,
-    .write = PmsgWrite,
-};
-
 static int pmsg_fd;
 static RwLock pmsg_fd_lock;
 
@@ -57,7 +45,7 @@
   pmsg_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
 }
 
-static void PmsgClose() {
+void PmsgClose() {
   auto lock = std::unique_lock{pmsg_fd_lock};
   if (pmsg_fd > 0) {
     close(pmsg_fd);
@@ -65,7 +53,7 @@
   pmsg_fd = 0;
 }
 
-static int PmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
+int PmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
   static const unsigned headerLength = 2;
   struct iovec newVec[nr + headerLength];
   android_log_header_t header;
@@ -123,7 +111,7 @@
 
   pmsgHeader.magic = LOGGER_MAGIC;
   pmsgHeader.len = sizeof(pmsgHeader) + sizeof(header);
-  pmsgHeader.uid = __android_log_uid();
+  pmsgHeader.uid = getuid();
   pmsgHeader.pid = getpid();
 
   header.id = logId;
diff --git a/liblog/pmsg_writer.h b/liblog/pmsg_writer.h
new file mode 100644
index 0000000..d5e1a1c
--- /dev/null
+++ b/liblog/pmsg_writer.h
@@ -0,0 +1,24 @@
+/*
+ * 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 <stddef.h>
+
+#include <android/log.h>
+
+int PmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
+void PmsgClose();
diff --git a/liblog/properties.cpp b/liblog/properties.cpp
index 2e0a8c9..abd48fc 100644
--- a/liblog/properties.cpp
+++ b/liblog/properties.cpp
@@ -20,13 +20,18 @@
 #include <pthread.h>
 #include <stdlib.h>
 #include <string.h>
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-#include <sys/_system_properties.h>
 #include <unistd.h>
 
+#include <algorithm>
+#include <shared_mutex>
+
 #include <private/android_logger.h>
 
-#include "log_portability.h"
+#include "logger_write.h"
+
+#ifdef __ANDROID__
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
 
 static pthread_mutex_t lock_loggable = PTHREAD_MUTEX_INITIALIZER;
 
@@ -89,14 +94,21 @@
   }
 }
 
-static int __android_log_level(const char* tag, size_t len, int default_prio) {
+static int __android_log_level(const char* tag, size_t len) {
   /* sizeof() is used on this array below */
   static const char log_namespace[] = "persist.log.tag.";
   static const size_t base_offset = 8; /* skip "persist." */
-  /* calculate the size of our key temporary buffer */
-  const size_t taglen = tag ? len : 0;
+
+  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();
+  }
+
   /* sizeof(log_namespace) = strlen(log_namespace) + 1 */
-  char key[sizeof(log_namespace) + taglen];
+  char key[sizeof(log_namespace) + len];
   char* kp;
   size_t i;
   char c = 0;
@@ -146,7 +158,7 @@
     }
   }
 
-  if (taglen) {
+  if (len) {
     int local_change_detected = change_detected;
     if (!not_locked) {
       if (!last_tag || !last_tag[0] || (last_tag[0] != tag[0]) ||
@@ -258,20 +270,30 @@
     case 'F': /* FALLTHRU */ /* Not officially supported */
     case 'A': return ANDROID_LOG_FATAL;
     case BOOLEAN_FALSE: /* FALLTHRU */ /* Not Officially supported */
-    case 'S': return -1; /* ANDROID_LOG_SUPPRESS */
+    case 'S': return ANDROID_LOG_SILENT;
       /* clang-format on */
   }
-  return default_prio;
+  return -1;
 }
 
 int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio) {
-  int logLevel = __android_log_level(tag, len, default_prio);
-  return logLevel >= 0 && prio >= logLevel;
+  int minimum_log_priority = __android_log_get_minimum_priority();
+  int property_log_level = __android_log_level(tag, len);
+
+  if (property_log_level >= 0 && minimum_log_priority != ANDROID_LOG_DEFAULT) {
+    return prio >= std::min(property_log_level, minimum_log_priority);
+  } else if (property_log_level >= 0) {
+    return prio >= property_log_level;
+  } else if (minimum_log_priority != ANDROID_LOG_DEFAULT) {
+    return prio >= minimum_log_priority;
+  } else {
+    return prio >= default_prio;
+  }
 }
 
 int __android_log_is_loggable(int prio, const char* tag, int default_prio) {
-  int logLevel = __android_log_level(tag, (tag && *tag) ? strlen(tag) : 0, default_prio);
-  return logLevel >= 0 && prio >= logLevel;
+  auto len = tag ? strlen(tag) : 0;
+  return __android_log_is_loggable_len(prio, tag, len, default_prio);
 }
 
 int __android_log_is_debuggable() {
@@ -383,7 +405,7 @@
   static struct cache2_char security = {
       PTHREAD_MUTEX_INITIALIZER, 0,
       "persist.logd.security",   {{NULL, 0xFFFFFFFF}, BOOLEAN_FALSE},
-      "ro.device_owner",         {{NULL, 0xFFFFFFFF}, BOOLEAN_FALSE},
+      "ro.organization_owned",   {{NULL, 0xFFFFFFFF}, BOOLEAN_FALSE},
       evaluate_security};
 
   return do_cache2_char(&security);
@@ -627,3 +649,23 @@
 
   return property_size;
 }
+
+#else
+
+int __android_log_is_loggable(int prio, const char*, int) {
+  int minimum_priority = __android_log_get_minimum_priority();
+  if (minimum_priority == ANDROID_LOG_DEFAULT) {
+    minimum_priority = ANDROID_LOG_INFO;
+  }
+  return prio >= minimum_priority;
+}
+
+int __android_log_is_loggable_len(int prio, const char*, size_t, int def) {
+  return __android_log_is_loggable(prio, nullptr, def);
+}
+
+int __android_log_is_debuggable() {
+  return 1;
+}
+
+#endif
\ No newline at end of file
diff --git a/liblog/tests/Android.bp b/liblog/tests/Android.bp
index f58c524..b4bb77f 100644
--- a/liblog/tests/Android.bp
+++ b/liblog/tests/Android.bp
@@ -54,6 +54,8 @@
     ],
     srcs: [
         "libc_test.cpp",
+        "liblog_default_tag.cpp",
+        "liblog_global_state.cpp",
         "liblog_test.cpp",
         "log_id_test.cpp",
         "log_radio_test.cpp",
@@ -102,6 +104,11 @@
     name: "liblog-host-test",
     static_libs: ["liblog"],
     shared_libs: ["libbase"],
-    srcs: ["liblog_host_test.cpp"],
+    srcs: [
+        "liblog_host_test.cpp",
+        "liblog_default_tag.cpp",
+        "liblog_global_state.cpp",
+        "rwlock_test.cpp",
+    ],
     isolated: true,
 }
diff --git a/liblog/tests/AndroidTest.xml b/liblog/tests/AndroidTest.xml
index c167478..fcb46b1 100644
--- a/liblog/tests/AndroidTest.xml
+++ b/liblog/tests/AndroidTest.xml
@@ -18,6 +18,7 @@
     <option name="config-descriptor:metadata" key="component" value="systems" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
         <option name="push" value="CtsLiblogTestCases->/data/local/tmp/CtsLiblogTestCases" />
diff --git a/liblog/tests/liblog_default_tag.cpp b/liblog/tests/liblog_default_tag.cpp
new file mode 100644
index 0000000..31b7467
--- /dev/null
+++ b/liblog/tests/liblog_default_tag.cpp
@@ -0,0 +1,160 @@
+/*
+ * 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.
+ */
+
+// LOG_TAG must be unset for android-base's logging to use a default tag.
+#undef LOG_TAG
+
+#include <stdlib.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
+#include <android/log.h>
+
+#include <gtest/gtest.h>
+
+#ifndef __ANDROID__
+static const char* getprogname() {
+  return program_invocation_short_name;
+}
+#endif
+
+TEST(liblog_default_tag, no_default_tag_libbase_write_first) {
+  using namespace android::base;
+  bool message_seen = false;
+  std::string expected_tag = "";
+  SetLogger([&](LogId, LogSeverity, const char* tag, const char*, unsigned int, const char*) {
+    message_seen = true;
+    EXPECT_EQ(expected_tag, tag);
+  });
+
+  expected_tag = getprogname();
+  LOG(WARNING) << "message";
+  EXPECT_TRUE(message_seen);
+  message_seen = false;
+
+  __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_WARN, nullptr, "message");
+  EXPECT_TRUE(message_seen);
+}
+
+TEST(liblog_default_tag, no_default_tag_liblog_write_first) {
+  using namespace android::base;
+  bool message_seen = false;
+  std::string expected_tag = "";
+  SetLogger([&](LogId, LogSeverity, const char* tag, const char*, unsigned int, const char*) {
+    message_seen = true;
+    EXPECT_EQ(expected_tag, tag);
+  });
+
+  expected_tag = getprogname();
+  __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_WARN, nullptr, "message");
+  EXPECT_TRUE(message_seen);
+  message_seen = false;
+
+  LOG(WARNING) << "message";
+  EXPECT_TRUE(message_seen);
+}
+
+TEST(liblog_default_tag, libbase_sets_default_tag) {
+  using namespace android::base;
+  bool message_seen = false;
+  std::string expected_tag = "libbase_test_tag";
+  SetLogger([&](LogId, LogSeverity, const char* tag, const char*, unsigned int, const char*) {
+    message_seen = true;
+    EXPECT_EQ(expected_tag, tag);
+  });
+  SetDefaultTag(expected_tag);
+
+  __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_WARN, nullptr, "message");
+  EXPECT_TRUE(message_seen);
+  message_seen = false;
+
+  LOG(WARNING) << "message";
+  EXPECT_TRUE(message_seen);
+}
+
+TEST(liblog_default_tag, liblog_sets_default_tag) {
+  using namespace android::base;
+  bool message_seen = false;
+  std::string expected_tag = "liblog_test_tag";
+  SetLogger([&](LogId, LogSeverity, const char* tag, const char*, unsigned int, const char*) {
+    message_seen = true;
+    EXPECT_EQ(expected_tag, tag);
+  });
+  __android_log_set_default_tag(expected_tag.c_str());
+
+  __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_WARN, nullptr, "message");
+  EXPECT_TRUE(message_seen);
+  message_seen = false;
+
+  LOG(WARNING) << "message";
+  EXPECT_TRUE(message_seen);
+}
+
+TEST(liblog_default_tag, default_tag_plus_log_severity) {
+#ifdef __ANDROID__
+  using namespace android::base;
+  bool message_seen = false;
+  std::string expected_tag = "liblog_test_tag";
+  SetLogger([&](LogId, LogSeverity, const char* tag, const char*, unsigned int, const char*) {
+    message_seen = true;
+    EXPECT_EQ(expected_tag, tag);
+  });
+  __android_log_set_default_tag(expected_tag.c_str());
+
+  auto log_tag_property = "log.tag." + expected_tag;
+  SetProperty(log_tag_property, "V");
+  auto reset_tag_property_guard = make_scope_guard([=] { SetProperty(log_tag_property, ""); });
+
+  __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_VERBOSE, nullptr, "message");
+  EXPECT_TRUE(message_seen);
+  message_seen = false;
+
+  LOG(VERBOSE) << "message";
+  EXPECT_TRUE(message_seen);
+#else
+  GTEST_SKIP() << "No log tag properties on host";
+#endif
+}
+
+TEST(liblog_default_tag, generated_default_tag_plus_log_severity) {
+#ifdef __ANDROID__
+  using namespace android::base;
+  bool message_seen = false;
+  std::string expected_tag = getprogname();
+  SetLogger([&](LogId, LogSeverity, const char* tag, const char*, unsigned int, const char*) {
+    message_seen = true;
+    EXPECT_EQ(expected_tag, tag);
+  });
+
+  // Even without any calls to SetDefaultTag(), the first message that attempts to log, will
+  // generate a default tag from getprogname() and check log.tag.<default tag> for loggability. This
+  // case checks that we can log a Verbose message when log.tag.<getprogname()> is set to 'V'.
+  auto log_tag_property = "log.tag." + expected_tag;
+  SetProperty(log_tag_property, "V");
+  auto reset_tag_property_guard = make_scope_guard([=] { SetProperty(log_tag_property, ""); });
+
+  __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_VERBOSE, nullptr, "message");
+  EXPECT_TRUE(message_seen);
+  message_seen = false;
+
+  LOG(VERBOSE) << "message";
+  EXPECT_TRUE(message_seen);
+#else
+  GTEST_SKIP() << "No log tag properties on host";
+#endif
+}
\ No newline at end of file
diff --git a/liblog/tests/liblog_global_state.cpp b/liblog/tests/liblog_global_state.cpp
new file mode 100644
index 0000000..9a181ef
--- /dev/null
+++ b/liblog/tests/liblog_global_state.cpp
@@ -0,0 +1,251 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "global_state_test_tag"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android/log.h>
+
+#include <gtest/gtest.h>
+
+TEST(liblog_global_state, libbase_logs_with_libbase_SetLogger) {
+  using namespace android::base;
+  bool message_seen = false;
+  LogSeverity expected_severity = WARNING;
+  std::string expected_file = Basename(__FILE__);
+  unsigned int expected_line;
+  std::string expected_message = "libbase test message";
+
+  auto LoggerFunction = [&](LogId log_id, LogSeverity severity, const char* tag, const char* file,
+                            unsigned int line, const char* message) {
+    message_seen = true;
+    EXPECT_EQ(DEFAULT, log_id);
+    EXPECT_EQ(expected_severity, severity);
+    EXPECT_STREQ(LOG_TAG, tag);
+    EXPECT_EQ(expected_file, file);
+    EXPECT_EQ(expected_line, line);
+    EXPECT_EQ(expected_message, message);
+  };
+
+  SetLogger(LoggerFunction);
+
+  expected_line = __LINE__ + 1;
+  LOG(expected_severity) << expected_message;
+  EXPECT_TRUE(message_seen);
+}
+
+TEST(liblog_global_state, libbase_logs_with_liblog_set_logger) {
+  using namespace android::base;
+  // These must be static since they're used by the liblog logger function, which only accepts
+  // lambdas without captures.  The items used by the libbase logger are explicitly not static, to
+  // ensure that lambdas with captures do work there.
+  static bool message_seen = false;
+  static std::string expected_file = Basename(__FILE__);
+  static unsigned int expected_line;
+  static std::string expected_message = "libbase test message";
+
+  auto liblog_logger_function = [](const struct __android_logger_data* logger_data,
+                                   const char* message) {
+    message_seen = true;
+    EXPECT_EQ(sizeof(__android_logger_data), logger_data->struct_size);
+    EXPECT_EQ(LOG_ID_DEFAULT, logger_data->buffer_id);
+    EXPECT_EQ(ANDROID_LOG_WARN, logger_data->priority);
+    EXPECT_STREQ(LOG_TAG, logger_data->tag);
+    EXPECT_EQ(expected_file, logger_data->file);
+    EXPECT_EQ(expected_line, logger_data->line);
+    EXPECT_EQ(expected_message, message);
+  };
+
+  __android_log_set_logger(liblog_logger_function);
+
+  expected_line = __LINE__ + 1;
+  LOG(WARNING) << expected_message;
+  EXPECT_TRUE(message_seen);
+}
+
+TEST(liblog_global_state, liblog_logs_with_libbase_SetLogger) {
+  using namespace android::base;
+  bool message_seen = false;
+  std::string expected_message = "libbase test message";
+
+  auto LoggerFunction = [&](LogId log_id, LogSeverity severity, const char* tag, const char* file,
+                            unsigned int line, const char* message) {
+    message_seen = true;
+    EXPECT_EQ(MAIN, log_id);
+    EXPECT_EQ(WARNING, severity);
+    EXPECT_STREQ(LOG_TAG, tag);
+    EXPECT_EQ(nullptr, file);
+    EXPECT_EQ(0U, line);
+    EXPECT_EQ(expected_message, message);
+  };
+
+  SetLogger(LoggerFunction);
+
+  __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_WARN, LOG_TAG, expected_message.c_str());
+  EXPECT_TRUE(message_seen);
+  message_seen = false;
+}
+
+TEST(liblog_global_state, liblog_logs_with_liblog_set_logger) {
+  using namespace android::base;
+  // These must be static since they're used by the liblog logger function, which only accepts
+  // lambdas without captures.  The items used by the libbase logger are explicitly not static, to
+  // ensure that lambdas with captures do work there.
+  static bool message_seen = false;
+  static int expected_buffer_id = LOG_ID_MAIN;
+  static int expected_priority = ANDROID_LOG_WARN;
+  static std::string expected_message = "libbase test message";
+
+  auto liblog_logger_function = [](const struct __android_logger_data* logger_data,
+                                   const char* message) {
+    message_seen = true;
+    EXPECT_EQ(sizeof(__android_logger_data), logger_data->struct_size);
+    EXPECT_EQ(expected_buffer_id, logger_data->buffer_id);
+    EXPECT_EQ(expected_priority, logger_data->priority);
+    EXPECT_STREQ(LOG_TAG, logger_data->tag);
+    EXPECT_STREQ(nullptr, logger_data->file);
+    EXPECT_EQ(0U, logger_data->line);
+    EXPECT_EQ(expected_message, message);
+  };
+
+  __android_log_set_logger(liblog_logger_function);
+
+  __android_log_buf_write(expected_buffer_id, expected_priority, LOG_TAG, expected_message.c_str());
+  EXPECT_TRUE(message_seen);
+}
+
+TEST(liblog_global_state, SetAborter_with_liblog) {
+  using namespace android::base;
+
+  std::string expected_message = "libbase test message";
+  static bool message_seen = false;
+  auto aborter_function = [&](const char* message) {
+    message_seen = true;
+    EXPECT_EQ(expected_message, message);
+  };
+
+  SetAborter(aborter_function);
+  LOG(FATAL) << expected_message;
+  EXPECT_TRUE(message_seen);
+  message_seen = false;
+
+  static std::string expected_message_static = "libbase test message";
+  auto liblog_aborter_function = [](const char* message) {
+    message_seen = true;
+    EXPECT_EQ(expected_message_static, message);
+  };
+  __android_log_set_aborter(liblog_aborter_function);
+  LOG(FATAL) << expected_message_static;
+  EXPECT_TRUE(message_seen);
+  message_seen = false;
+}
+
+TEST(liblog_global_state, is_loggable_both_default) {
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+}
+
+TEST(liblog_global_state, is_loggable_minimum_log_priority_only) {
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  EXPECT_EQ(ANDROID_LOG_DEFAULT, __android_log_set_minimum_priority(ANDROID_LOG_DEBUG));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  EXPECT_EQ(ANDROID_LOG_DEBUG, __android_log_set_minimum_priority(ANDROID_LOG_WARN));
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  EXPECT_EQ(android::base::WARNING, android::base::SetMinimumLogSeverity(android::base::DEBUG));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  EXPECT_EQ(android::base::DEBUG, android::base::SetMinimumLogSeverity(android::base::WARNING));
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+}
+
+TEST(liblog_global_state, is_loggable_tag_log_priority_only) {
+#ifdef __ANDROID__
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  auto log_tag_property = std::string("log.tag.") + LOG_TAG;
+  android::base::SetProperty(log_tag_property, "d");
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  android::base::SetProperty(log_tag_property, "w");
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  android::base::SetProperty(log_tag_property, "");
+#else
+  GTEST_SKIP() << "No log tag properties on host";
+#endif
+}
+
+TEST(liblog_global_state, is_loggable_both_set) {
+#ifdef __ANDROID__
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  // When both a tag and a minimum priority are set, we use the lower value of the two.
+
+  // tag = warning, minimum_priority = debug, expect 'debug'
+  auto log_tag_property = std::string("log.tag.") + LOG_TAG;
+  android::base::SetProperty(log_tag_property, "w");
+  EXPECT_EQ(ANDROID_LOG_DEFAULT, __android_log_set_minimum_priority(ANDROID_LOG_DEBUG));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  // tag = warning, minimum_priority = warning, expect 'warning'
+  EXPECT_EQ(ANDROID_LOG_DEBUG, __android_log_set_minimum_priority(ANDROID_LOG_WARN));
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  // tag = debug, minimum_priority = warning, expect 'debug'
+  android::base::SetProperty(log_tag_property, "d");
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  // tag = debug, minimum_priority = debug, expect 'debug'
+  EXPECT_EQ(ANDROID_LOG_WARN, __android_log_set_minimum_priority(ANDROID_LOG_DEBUG));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  android::base::SetProperty(log_tag_property, "");
+#else
+  GTEST_SKIP() << "No log tag properties on host";
+#endif
+}
diff --git a/liblog/tests/liblog_host_test.cpp b/liblog/tests/liblog_host_test.cpp
index 377550f..ec186d4 100644
--- a/liblog/tests/liblog_host_test.cpp
+++ b/liblog/tests/liblog_host_test.cpp
@@ -20,15 +20,37 @@
 #include <stdlib.h>
 #include <unistd.h>
 
+#include <regex>
+#include <string>
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
 #include <android-base/stringprintf.h>
-#include <android-base/strings.h>
 #include <android-base/test_utils.h>
 #include <gtest/gtest.h>
 
+using android::base::InitLogging;
+using android::base::StderrLogger;
 using android::base::StringPrintf;
-using android::base::StringReplace;
 
-void GenerateLogContent() {
+static std::string MakeLogPattern(int priority, const char* tag, const char* message) {
+  static const char log_characters[] = "XXVDIWEF";
+  static_assert(arraysize(log_characters) - 1 == ANDROID_LOG_SILENT,
+                "Mismatch in size of log_characters and values in android_LogPriority");
+  priority = priority > ANDROID_LOG_SILENT ? ANDROID_LOG_FATAL : priority;
+  char log_char = log_characters[priority];
+
+  return StringPrintf("%s %c \\d+-\\d+ \\d+:\\d+:\\d+ \\s*\\d+ \\s*\\d+ %s", tag, log_char,
+                      message);
+}
+
+static void CheckMessage(bool expected, const std::string& output, int priority, const char* tag,
+                         const char* message) {
+  std::regex message_regex(MakeLogPattern(priority, tag, message));
+  EXPECT_EQ(expected, std::regex_search(output, message_regex)) << message;
+}
+
+static void GenerateLogContent() {
   __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_VERBOSE, "tag", "verbose main");
   __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO, "tag", "info main");
   __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_ERROR, "tag", "error main");
@@ -52,137 +74,86 @@
 }
 
 TEST(liblog, default_write) {
-  setenv("ANDROID_PRINTF_LOG", "brief", true);
   CapturedStderr captured_stderr;
+  InitLogging(nullptr, StderrLogger);
 
   GenerateLogContent();
 
-  std::string expected_output = StringReplace(R"init(I/tag     (<pid>): info main
-E/tag     (<pid>): error main
-I/tag     (<pid>): info radio
-E/tag     (<pid>): error radio
-I/tag     (<pid>): info system
-E/tag     (<pid>): error system
-I/tag     (<pid>): info crash
-E/tag     (<pid>): error crash
-)init",
-                                              "<pid>", GetPidString(), true);
+  CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose main");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info main");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error main");
 
-  EXPECT_EQ(expected_output, captured_stderr.str());
+  CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose radio");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info radio");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error radio");
+
+  CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose system");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info system");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error system");
+
+  CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose crash");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info crash");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error crash");
 }
 
-TEST(liblog, format) {
-  setenv("ANDROID_PRINTF_LOG", "process", true);
+TEST(liblog, verbose_write) {
+  setenv("ANDROID_LOG_TAGS", "*:v", true);
   CapturedStderr captured_stderr;
+  InitLogging(nullptr, StderrLogger);
 
   GenerateLogContent();
 
-  std::string expected_output = StringReplace(R"init(I(<pid>) info main  (tag)
-E(<pid>) error main  (tag)
-I(<pid>) info radio  (tag)
-E(<pid>) error radio  (tag)
-I(<pid>) info system  (tag)
-E(<pid>) error system  (tag)
-I(<pid>) info crash  (tag)
-E(<pid>) error crash  (tag)
-)init",
-                                              "<pid>", GetPidString(), true);
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose main");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info main");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error main");
 
-  EXPECT_EQ(expected_output, captured_stderr.str());
-  captured_stderr.Stop();
-  captured_stderr.Reset();
-  captured_stderr.Start();
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose radio");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info radio");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error radio");
 
-  // Changing the environment after starting writing doesn't change the format.
-  setenv("ANDROID_PRINTF_LOG", "brief", true);
-  GenerateLogContent();
-  EXPECT_EQ(expected_output, captured_stderr.str());
-  captured_stderr.Stop();
-  captured_stderr.Reset();
-  captured_stderr.Start();
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose system");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info system");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error system");
 
-  // However calling __android_log_close() does reset logging and allow changing the format.
-  __android_log_close();
-  GenerateLogContent();
-
-  expected_output = StringReplace(R"init(I/tag     (<pid>): info main
-E/tag     (<pid>): error main
-I/tag     (<pid>): info radio
-E/tag     (<pid>): error radio
-I/tag     (<pid>): info system
-E/tag     (<pid>): error system
-I/tag     (<pid>): info crash
-E/tag     (<pid>): error crash
-)init",
-                                  "<pid>", GetPidString(), true);
-
-  EXPECT_EQ(expected_output, captured_stderr.str());
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose crash");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info crash");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error crash");
 }
 
-TEST(liblog, filter) {
-  setenv("ANDROID_PRINTF_LOG", "brief", true);
-  setenv("ANDROID_LOG_TAGS", "*:w verbose_tag:v debug_tag:d", true);
-  CapturedStderr captured_stderr;
-
-  auto generate_logs = [](log_id_t log_id) {
-    // Check that we show verbose logs when requesting for a given tag.
-    __android_log_buf_print(log_id, ANDROID_LOG_VERBOSE, "verbose_tag", "verbose verbose_tag");
-    __android_log_buf_print(log_id, ANDROID_LOG_ERROR, "verbose_tag", "error verbose_tag");
-
-    // Check that we don't show verbose logs when explicitly requesting debug+ for a given tag.
-    __android_log_buf_print(log_id, ANDROID_LOG_VERBOSE, "debug_tag", "verbose debug_tag");
-    __android_log_buf_print(log_id, ANDROID_LOG_DEBUG, "debug_tag", "debug debug_tag");
-    __android_log_buf_print(log_id, ANDROID_LOG_ERROR, "debug_tag", "error debug_tag");
-
-    // Check that we don't show info logs when requesting globally warn+.
-    __android_log_buf_print(log_id, ANDROID_LOG_INFO, "default_tag", "info default_tag");
-    __android_log_buf_print(log_id, ANDROID_LOG_WARN, "default_tag", "warn default_tag");
-    __android_log_buf_print(log_id, ANDROID_LOG_ERROR, "default_tag", "error default_tag");
-  };
-
-  auto expected_output = StringReplace(R"init(V/verbose_tag(<pid>): verbose verbose_tag
-E/verbose_tag(<pid>): error verbose_tag
-D/debug_tag(<pid>): debug debug_tag
-E/debug_tag(<pid>): error debug_tag
-W/default_tag(<pid>): warn default_tag
-E/default_tag(<pid>): error default_tag
-)init",
-                                       "<pid>", GetPidString(), true);
-
-  auto test_all_logs = [&] {
-    for (auto log_id : {LOG_ID_MAIN, LOG_ID_SYSTEM, LOG_ID_RADIO, LOG_ID_CRASH}) {
-      generate_logs(log_id);
-      EXPECT_EQ(expected_output, captured_stderr.str());
-      captured_stderr.Stop();
-      captured_stderr.Reset();
-      captured_stderr.Start();
-    }
-  };
-
-  test_all_logs();
-
-  // Changing the environment after starting writing doesn't change the filter.
+TEST(liblog, error_write) {
   setenv("ANDROID_LOG_TAGS", "*:e", true);
-  test_all_logs();
+  CapturedStderr captured_stderr;
+  InitLogging(nullptr, StderrLogger);
 
-  // However calling __android_log_close() does reset logging and allow changing the format.
-  __android_log_close();
-  expected_output = StringReplace(R"init(E/verbose_tag(<pid>): error verbose_tag
-E/debug_tag(<pid>): error debug_tag
-E/default_tag(<pid>): error default_tag
-)init",
-                                  "<pid>", GetPidString(), true);
-  test_all_logs();
+  GenerateLogContent();
+
+  CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose main");
+  CheckMessage(false, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info main");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error main");
+
+  CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose radio");
+  CheckMessage(false, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info radio");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error radio");
+
+  CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose system");
+  CheckMessage(false, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info system");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error system");
+
+  CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose crash");
+  CheckMessage(false, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info crash");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error crash");
 }
 
 TEST(liblog, kernel_no_write) {
   CapturedStderr captured_stderr;
+  InitLogging(nullptr, StderrLogger);
   __android_log_buf_print(LOG_ID_KERNEL, ANDROID_LOG_ERROR, "tag", "kernel error");
   EXPECT_EQ("", captured_stderr.str());
 }
 
 TEST(liblog, binary_no_write) {
   CapturedStderr captured_stderr;
+  InitLogging(nullptr, StderrLogger);
   __android_log_buf_print(LOG_ID_EVENTS, ANDROID_LOG_ERROR, "tag", "error events");
   __android_log_buf_print(LOG_ID_STATS, ANDROID_LOG_ERROR, "tag", "error stats");
   __android_log_buf_print(LOG_ID_SECURITY, ANDROID_LOG_ERROR, "tag", "error security");
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index c402e20..a60d2df 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -1074,7 +1074,6 @@
 
     // Once we've found our expected entry, break.
     if (len == LOGGER_ENTRY_MAX_PAYLOAD - sizeof(big_payload_tag)) {
-      EXPECT_EQ(ret, len + static_cast<ssize_t>(sizeof(big_payload_tag)));
       *found = true;
     }
   };
@@ -1259,14 +1258,10 @@
     int level;
     char type;
   } levels[] = {
-    { ANDROID_LOG_VERBOSE, 'v' },
-    { ANDROID_LOG_DEBUG, 'd' },
-    { ANDROID_LOG_INFO, 'i' },
-    { ANDROID_LOG_WARN, 'w' },
-    { ANDROID_LOG_ERROR, 'e' },
-    { ANDROID_LOG_FATAL, 'a' },
-    { -1, 's' },
-    { -2, 'g' },  // Illegal value, resort to default
+      {ANDROID_LOG_VERBOSE, 'v'}, {ANDROID_LOG_DEBUG, 'd'},
+      {ANDROID_LOG_INFO, 'i'},    {ANDROID_LOG_WARN, 'w'},
+      {ANDROID_LOG_ERROR, 'e'},   {ANDROID_LOG_FATAL, 'a'},
+      {ANDROID_LOG_SILENT, 's'},  {-2, 'g'},  // Illegal value, resort to default
   };
 
   // Set up initial test condition
@@ -1642,7 +1637,7 @@
 TEST(liblog, __security) {
 #ifdef __ANDROID__
   static const char persist_key[] = "persist.logd.security";
-  static const char readonly_key[] = "ro.device_owner";
+  static const char readonly_key[] = "ro.organization_owned";
   // A silly default value that can never be in readonly_key so
   // that it can be determined the property is not set.
   static const char nothing_val[] = "_NOTHING_TO_SEE_HERE_";
@@ -1662,7 +1657,7 @@
   if (!strcmp(readonly, nothing_val)) {
     // Lets check if we can set the value (we should not be allowed to do so)
     EXPECT_FALSE(__android_log_security());
-    fprintf(stderr, "WARNING: setting ro.device_owner to a domain\n");
+    fprintf(stderr, "WARNING: setting ro.organization_owned to a domain\n");
     static const char domain[] = "com.google.android.SecOps.DeviceOwner";
     EXPECT_NE(0, property_set(readonly_key, domain));
     useconds_t total_time = 0;
diff --git a/libmetricslogger/Android.bp b/libmetricslogger/Android.bp
deleted file mode 100644
index 7d7554b..0000000
--- a/libmetricslogger/Android.bp
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2017 The Android Open Source Project
-
-metricslogger_lib_src_files = [
-    "metrics_logger.cpp",
-]
-
-cc_defaults {
-    name: "metricslogger_defaults",
-
-    host_supported: true,
-
-    export_include_dirs: ["include"],
-    local_include_dirs: ["include"],
-    shared_libs: [
-        "libbase",
-        "liblog",
-        "libstatssocket",
-    ],
-    whole_static_libs: ["libgtest_prod"],
-
-    cflags: [
-        "-Wall",
-        "-Wextra",
-        "-Werror",
-    ],
-}
-
-// metricslogger shared library
-// -----------------------------------------------------------------------------
-cc_library {
-    name: "libmetricslogger",
-    srcs: metricslogger_lib_src_files,
-    defaults: ["metricslogger_defaults"],
-    export_shared_lib_headers: ["libstatssocket"],
-}
-
-// metricslogger shared library, debug
-// -----------------------------------------------------------------------------
-cc_library_shared {
-    name: "libmetricslogger_debug",
-    srcs: metricslogger_lib_src_files,
-    defaults: ["metricslogger_defaults"],
-
-    target: {
-        host: {
-            cflags: ["-UNDEBUG"],
-        },
-    },
-}
-
-// Native tests
-// -----------------------------------------------------------------------------
-cc_test {
-    name: "metricslogger_tests",
-    isolated: true,
-    defaults: ["metricslogger_defaults"],
-    shared_libs: [
-        "libbase",
-        "libmetricslogger_debug",
-    ],
-    srcs: [
-        "metrics_logger_test.cpp",
-    ],
-}
diff --git a/libmetricslogger/OWNERS b/libmetricslogger/OWNERS
deleted file mode 100644
index 6a6fba2..0000000
--- a/libmetricslogger/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-cwren@google.com
-jhawkins@google.com
diff --git a/libmetricslogger/include/metricslogger/metrics_logger.h b/libmetricslogger/include/metricslogger/metrics_logger.h
deleted file mode 100644
index 71c04a6..0000000
--- a/libmetricslogger/include/metricslogger/metrics_logger.h
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <log/log_event_list.h>
-#include <stats_event_list.h>
-#include <cstdint>
-#include <string>
-
-namespace android {
-namespace metricslogger {
-
-// Logs a Tron histogram metric named |event| containing |data| to the Tron log
-// buffer.
-void LogHistogram(const std::string& event, int32_t data);
-
-// Logs a Tron counter metric named |name| containing |val| count to the Tron
-// log buffer.
-void LogCounter(const std::string& name, int32_t val);
-
-// Logs a Tron multi_action with category|category| containing the string
-// |value| in the field |field|.
-void LogMultiAction(int32_t category, int32_t field, const std::string& value);
-
-// Logs a Tron complex event.
-//
-// A complex event can include data in a structure not suppored by the other
-// log event types above.
-//
-// Note that instances of this class are single use. You must call Record()
-// to write the event to the event log.
-class ComplexEventLogger {
-  private:
-    android_log_event_list logger;
-    stats_event_list stats_logger;
-
-  public:
-    // Create a complex event with category|category|.
-    explicit ComplexEventLogger(int category);
-    // Set the package name that this event originates from.
-    void SetPackageName(const std::string& package_name);
-    // Add tagged data to the event, with the given tag and integer value.
-    void AddTaggedData(int tag, int32_t value);
-    // Add tagged data to the event, with the given tag and string value.
-    void AddTaggedData(int tag, const std::string& value);
-    // Add tagged data to the event, with the given tag and integer value.
-    void AddTaggedData(int tag, int64_t value);
-    // Add tagged data to the event, with the given tag and float value.
-    void AddTaggedData(int tag, float value);
-    // Record this event. This method can only be used once per instance
-    // of ComplexEventLogger. Do not made any subsequent calls to AddTaggedData
-    // after recording an event.
-    void Record();
-};
-
-// TODO: replace these with the metric_logger.proto definitions
-enum {
-    LOGBUILDER_CATEGORY = 757,
-    LOGBUILDER_TYPE = 758,
-    LOGBUILDER_NAME = 799,
-    LOGBUILDER_BUCKET = 801,
-    LOGBUILDER_VALUE = 802,
-    LOGBUILDER_COUNTER = 803,
-    LOGBUILDER_HISTOGRAM = 804,
-    LOGBUILDER_PACKAGENAME = 806,
-
-    ACTION_BOOT = 1098,
-    FIELD_PLATFORM_REASON = 1099,
-
-    FIELD_DURATION_MILLIS = 1304,
-
-    FIELD_END_BATTERY_PERCENT = 1308,
-
-    ACTION_HIDDEN_API_ACCESSED = 1391,
-    FIELD_HIDDEN_API_ACCESS_METHOD = 1392,
-    FIELD_HIDDEN_API_ACCESS_DENIED = 1393,
-    FIELD_HIDDEN_API_SIGNATURE = 1394,
-
-    ACTION_USB_CONNECTOR_CONNECTED = 1422,
-    ACTION_USB_CONNECTOR_DISCONNECTED = 1423,
-    ACTION_USB_AUDIO_CONNECTED = 1424,
-    FIELD_USB_AUDIO_VIDPID = 1425,
-    ACTION_USB_AUDIO_DISCONNECTED = 1426,
-    ACTION_HARDWARE_FAILED = 1427,
-    FIELD_HARDWARE_TYPE = 1428,
-    FIELD_HARDWARE_FAILURE_CODE = 1429,
-    ACTION_PHYSICAL_DROP = 1430,
-    FIELD_CONFIDENCE_PERCENT = 1431,
-    FIELD_ACCEL_MILLI_G = 1432,
-    ACTION_BATTERY_HEALTH = 1433,
-    FIELD_BATTERY_HEALTH_SNAPSHOT_TYPE = 1434,
-    FIELD_BATTERY_TEMPERATURE_DECI_C = 1435,
-    FIELD_BATTERY_VOLTAGE_UV = 1436,
-    FIELD_BATTERY_OPEN_CIRCUIT_VOLTAGE_UV = 1437,
-    ACTION_BATTERY_CHARGE_CYCLES = 1438,
-    FIELD_BATTERY_CHARGE_CYCLES = 1439,
-
-    ACTION_SLOW_IO = 1442,
-    FIELD_IO_OPERATION_TYPE = 1443,
-    FIELD_IO_OPERATION_COUNT = 1444,
-    ACTION_SPEAKER_IMPEDANCE = 1445,
-    FIELD_SPEAKER_IMPEDANCE_MILLIOHMS = 1446,
-    FIELD_SPEAKER_LOCATION = 1447,
-    FIELD_BATTERY_RESISTANCE_UOHMS = 1448,
-    FIELD_BATTERY_CURRENT_UA = 1449,
-    FIELD_HARDWARE_LOCATION = 1450,
-    ACTION_BATTERY_CAUSED_SHUTDOWN = 1451,
-};
-
-enum {
-    TYPE_ACTION = 4,
-};
-
-enum {
-    ACCESS_METHOD_NONE = 0,
-    ACCESS_METHOD_REFLECTION = 1,
-    ACCESS_METHOD_JNI = 2,
-    ACCESS_METHOD_LINKING = 3,
-};
-
-enum HardwareType {
-    HARDWARE_UNKNOWN = 0,
-    HARDWARE_MICROPHONE = 1,
-    HARDWARE_CODEC = 2,
-    HARDWARE_SPEAKER = 3,
-    HARDWARE_FINGERPRINT = 4,
-};
-
-enum HardwareFailureCode {
-    HARDWARE_FAILURE_UNKNOWN = 0,
-    HARDWARE_FAILURE_COMPLETE = 1,
-    HARDWARE_FAILURE_SPEAKER_HIGH_Z = 2,
-    HARDWARE_FAILURE_SPEAKER_SHORT = 3,
-    HARDWARE_FAILURE_FINGERPRINT_SENSOR_BROKEN = 4,
-    HARDWARE_FAILURE_FINGERPRINT_TOO_MANY_DEAD_PIXELS = 5,
-};
-
-enum IoOperation {
-    IOOP_UNKNOWN = 0,
-    IOOP_READ = 1,
-    IOOP_WRITE = 2,
-    IOOP_UNMAP = 3,
-    IOOP_SYNC = 4,
-};
-
-}  // namespace metricslogger
-}  // namespace android
diff --git a/libmetricslogger/metrics_logger.cpp b/libmetricslogger/metrics_logger.cpp
deleted file mode 100644
index 2a1b137..0000000
--- a/libmetricslogger/metrics_logger.cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "metricslogger/metrics_logger.h"
-
-#include <cstdlib>
-
-#include <android-base/chrono_utils.h>
-#include <log/event_tag_map.h>
-
-using namespace android;
-
-namespace {
-
-const static int kStatsEventTag = 1937006964;
-const static int kKeyValuePairAtomId = 83;
-#ifdef __ANDROID__
-EventTagMap* kEventTagMap = android_openEventTagMap(nullptr);
-const int kSysuiMultiActionTag = android_lookupEventTagNum(
-    kEventTagMap, "sysui_multi_action", "(content|4)", ANDROID_LOG_UNKNOWN);
-#else
-// android_openEventTagMap does not work on host builds.
-const int kSysuiMultiActionTag = 0;
-#endif
-
-int64_t getElapsedTimeNanoSinceBoot() {
-    return std::chrono::duration_cast<std::chrono::nanoseconds>(
-                   android::base::boot_clock::now().time_since_epoch())
-            .count();
-}
-
-}  // namespace
-
-namespace android {
-namespace metricslogger {
-
-// Mirror com.android.internal.logging.MetricsLogger#histogram().
-void LogHistogram(const std::string& event, int32_t data) {
-    android_log_event_list log(kSysuiMultiActionTag);
-    log << LOGBUILDER_CATEGORY << LOGBUILDER_HISTOGRAM << LOGBUILDER_NAME << event
-        << LOGBUILDER_BUCKET << data << LOGBUILDER_VALUE << 1 << LOG_ID_EVENTS;
-
-    stats_event_list stats_log(kStatsEventTag);
-    stats_log << getElapsedTimeNanoSinceBoot() << kKeyValuePairAtomId << LOGBUILDER_CATEGORY
-              << LOGBUILDER_HISTOGRAM << LOGBUILDER_NAME << event << LOGBUILDER_BUCKET << data
-              << LOGBUILDER_VALUE << 1;
-    stats_log.write(LOG_ID_STATS);
-}
-
-// Mirror com.android.internal.logging.MetricsLogger#count().
-void LogCounter(const std::string& name, int32_t val) {
-    android_log_event_list log(kSysuiMultiActionTag);
-    log << LOGBUILDER_CATEGORY << LOGBUILDER_COUNTER << LOGBUILDER_NAME << name << LOGBUILDER_VALUE
-        << val << LOG_ID_EVENTS;
-
-    stats_event_list stats_log(kStatsEventTag);
-    stats_log << getElapsedTimeNanoSinceBoot() << kKeyValuePairAtomId << LOGBUILDER_CATEGORY
-              << LOGBUILDER_COUNTER << LOGBUILDER_NAME << name << LOGBUILDER_VALUE << val;
-    stats_log.write(LOG_ID_STATS);
-}
-
-// Mirror com.android.internal.logging.MetricsLogger#action().
-void LogMultiAction(int32_t category, int32_t field, const std::string& value) {
-    android_log_event_list log(kSysuiMultiActionTag);
-    log << LOGBUILDER_CATEGORY << category << LOGBUILDER_TYPE << TYPE_ACTION
-        << field << value << LOG_ID_EVENTS;
-
-    stats_event_list stats_log(kStatsEventTag);
-    stats_log << getElapsedTimeNanoSinceBoot() << kKeyValuePairAtomId << LOGBUILDER_CATEGORY
-              << category << LOGBUILDER_TYPE << TYPE_ACTION << field << value;
-    stats_log.write(LOG_ID_STATS);
-}
-
-ComplexEventLogger::ComplexEventLogger(int category)
-    : logger(kSysuiMultiActionTag), stats_logger(kStatsEventTag) {
-    logger << LOGBUILDER_CATEGORY << category;
-    stats_logger << getElapsedTimeNanoSinceBoot() << kKeyValuePairAtomId << LOGBUILDER_CATEGORY
-                 << category;
-}
-
-void ComplexEventLogger::SetPackageName(const std::string& package_name) {
-    logger << LOGBUILDER_PACKAGENAME << package_name;
-    stats_logger << LOGBUILDER_PACKAGENAME << package_name;
-}
-
-void ComplexEventLogger::AddTaggedData(int tag, int32_t value) {
-    logger << tag << value;
-    stats_logger << tag << value;
-}
-
-void ComplexEventLogger::AddTaggedData(int tag, const std::string& value) {
-    logger << tag << value;
-    stats_logger << tag << value;
-}
-
-void ComplexEventLogger::AddTaggedData(int tag, int64_t value) {
-    logger << tag << value;
-    stats_logger << tag << value;
-}
-
-void ComplexEventLogger::AddTaggedData(int tag, float value) {
-    logger << tag << value;
-    stats_logger << tag << value;
-}
-
-void ComplexEventLogger::Record() {
-    logger << LOG_ID_EVENTS;
-    stats_logger.write(LOG_ID_STATS);
-}
-
-}  // namespace metricslogger
-}  // namespace android
diff --git a/libmetricslogger/metrics_logger_test.cpp b/libmetricslogger/metrics_logger_test.cpp
deleted file mode 100644
index 440645c..0000000
--- a/libmetricslogger/metrics_logger_test.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "metricslogger/metrics_logger.h"
-
-#include <gtest/gtest.h>
-
-TEST(MetricsLoggerTest, AddSingleBootEvent) {
-    android::metricslogger::LogHistogram("test_event", 42);
-    // TODO(jhawkins): Verify the EventLog is updated.
-}
-
-TEST(MetricsLoggerTest, AddCounterVal) {
-    android::metricslogger::LogCounter("test_count", 10);
-}
diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h
index 333fc55..ee6ae7a 100644
--- a/libmodprobe/include/modprobe/modprobe.h
+++ b/libmodprobe/include/modprobe/modprobe.h
@@ -44,6 +44,9 @@
     bool Rmmod(const std::string& module_name);
     std::vector<std::string> GetDependencies(const std::string& module);
     bool ModuleExists(const std::string& module_name);
+    void AddOption(const std::string& module_name, const std::string& option_name,
+                   const std::string& value);
+    std::string GetKernelCmdline();
 
     bool ParseDepCallback(const std::string& base_path, const std::vector<std::string>& args);
     bool ParseAliasCallback(const std::vector<std::string>& args);
@@ -51,6 +54,7 @@
     bool ParseLoadCallback(const std::vector<std::string>& args);
     bool ParseOptionsCallback(const std::vector<std::string>& args);
     bool ParseBlacklistCallback(const std::vector<std::string>& args);
+    void ParseKernelCmdlineOptions();
     void ParseCfg(const std::string& cfg, std::function<bool(const std::vector<std::string>&)> f);
 
     std::vector<std::pair<std::string, std::string>> module_aliases_;
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
index 6b9107f..f22bbf1 100644
--- a/libmodprobe/libmodprobe.cpp
+++ b/libmodprobe/libmodprobe.cpp
@@ -238,6 +238,80 @@
     return;
 }
 
+void Modprobe::AddOption(const std::string& module_name, const std::string& option_name,
+                         const std::string& value) {
+    auto canonical_name = MakeCanonical(module_name);
+    auto options_iter = module_options_.find(canonical_name);
+    auto option_str = option_name + "=" + value;
+    if (options_iter != module_options_.end()) {
+        options_iter->second = options_iter->second + " " + option_str;
+    } else {
+        module_options_.emplace(canonical_name, option_str);
+    }
+}
+
+void Modprobe::ParseKernelCmdlineOptions(void) {
+    std::string cmdline = GetKernelCmdline();
+    std::string module_name = "";
+    std::string option_name = "";
+    std::string value = "";
+    bool in_module = true;
+    bool in_option = false;
+    bool in_value = false;
+    bool in_quotes = false;
+    int start = 0;
+
+    for (int i = 0; i < cmdline.size(); i++) {
+        if (cmdline[i] == '"') {
+            in_quotes = !in_quotes;
+        }
+
+        if (in_quotes) continue;
+
+        if (cmdline[i] == ' ') {
+            if (in_value) {
+                value = cmdline.substr(start, i - start);
+                if (!module_name.empty() && !option_name.empty()) {
+                    AddOption(module_name, option_name, value);
+                }
+            }
+            module_name = "";
+            option_name = "";
+            value = "";
+            in_value = false;
+            start = i + 1;
+            in_module = true;
+            continue;
+        }
+
+        if (cmdline[i] == '.') {
+            if (in_module) {
+                module_name = cmdline.substr(start, i - start);
+                start = i + 1;
+                in_module = false;
+            }
+            in_option = true;
+            continue;
+        }
+
+        if (cmdline[i] == '=') {
+            if (in_option) {
+                option_name = cmdline.substr(start, i - start);
+                start = i + 1;
+                in_option = false;
+            }
+            in_value = true;
+            continue;
+        }
+    }
+    if (in_value && !in_quotes) {
+        value = cmdline.substr(start, cmdline.size() - start);
+        if (!module_name.empty() && !option_name.empty()) {
+            AddOption(module_name, option_name, value);
+        }
+    }
+}
+
 Modprobe::Modprobe(const std::vector<std::string>& base_paths) {
     using namespace std::placeholders;
 
@@ -261,6 +335,7 @@
         ParseCfg(base_path + "/modules.blacklist", blacklist_callback);
     }
 
+    ParseKernelCmdlineOptions();
     android::base::SetMinimumLogSeverity(android::base::INFO);
 }
 
diff --git a/libmodprobe/libmodprobe_ext.cpp b/libmodprobe/libmodprobe_ext.cpp
index 8bebe4c..99472c1 100644
--- a/libmodprobe/libmodprobe_ext.cpp
+++ b/libmodprobe/libmodprobe_ext.cpp
@@ -17,11 +17,20 @@
 #include <sys/stat.h>
 #include <sys/syscall.h>
 
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 
 #include <modprobe/modprobe.h>
 
+std::string Modprobe::GetKernelCmdline(void) {
+    std::string cmdline;
+    if (!android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
+        return "";
+    }
+    return cmdline;
+}
+
 bool Modprobe::Insmod(const std::string& path_name, const std::string& parameters) {
     android::base::unique_fd fd(
             TEMP_FAILURE_RETRY(open(path_name.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
diff --git a/libmodprobe/libmodprobe_ext_test.cpp b/libmodprobe/libmodprobe_ext_test.cpp
index 7d817b1..057dea3 100644
--- a/libmodprobe/libmodprobe_ext_test.cpp
+++ b/libmodprobe/libmodprobe_ext_test.cpp
@@ -29,6 +29,10 @@
 
 #include "libmodprobe_test.h"
 
+std::string Modprobe::GetKernelCmdline(void) {
+    return kernel_cmdline;
+}
+
 bool Modprobe::Insmod(const std::string& path_name, const std::string& parameters) {
     auto deps = GetDependencies(MakeCanonical(path_name));
     if (deps.empty()) {
@@ -57,7 +61,7 @@
 
 bool Modprobe::Rmmod(const std::string& module_name) {
     for (auto it = modules_loaded.begin(); it != modules_loaded.end(); it++) {
-        if (*it == module_name) {
+        if (*it == module_name || android::base::StartsWith(*it, module_name + " ")) {
             modules_loaded.erase(it);
             return true;
         }
diff --git a/libmodprobe/libmodprobe_test.cpp b/libmodprobe/libmodprobe_test.cpp
index a711631..879c7f2 100644
--- a/libmodprobe/libmodprobe_test.cpp
+++ b/libmodprobe/libmodprobe_test.cpp
@@ -31,7 +31,13 @@
 // Used by libmodprobe_ext_test to report which modules would have been loaded.
 std::vector<std::string> modules_loaded;
 
+// Used by libmodprobe_ext_test to fake a kernel commandline
+std::string kernel_cmdline;
+
 TEST(libmodprobe, Test) {
+    kernel_cmdline =
+            "flag1 flag2 test1.option1=50 test4.option3=\"set x\" test1.option2=60 "
+            "test8. test5.option1= test10.option1=1";
     test_modules = {
             "/test1.ko",  "/test2.ko",  "/test3.ko",  "/test4.ko",  "/test5.ko",
             "/test6.ko",  "/test7.ko",  "/test8.ko",  "/test9.ko",  "/test10.ko",
@@ -42,25 +48,33 @@
             "/test14.ko",
             "/test15.ko",
             "/test3.ko",
-            "/test4.ko",
-            "/test1.ko",
+            "/test4.ko option3=\"set x\"",
+            "/test1.ko option1=50 option2=60",
             "/test6.ko",
             "/test2.ko",
-            "/test5.ko",
+            "/test5.ko option1=",
             "/test8.ko",
             "/test7.ko param1=4",
             "/test9.ko param_x=1 param_y=2 param_z=3",
-            "/test10.ko",
+            "/test10.ko option1=1",
             "/test12.ko",
             "/test11.ko",
             "/test13.ko",
     };
 
     std::vector<std::string> expected_after_remove = {
-            "/test14.ko", "/test15.ko",         "/test1.ko",
-            "/test6.ko",  "/test2.ko",          "/test5.ko",
-            "/test8.ko",  "/test7.ko param1=4", "/test9.ko param_x=1 param_y=2 param_z=3",
-            "/test10.ko", "/test12.ko",         "/test11.ko",
+            "/test14.ko",
+            "/test15.ko",
+            "/test1.ko option1=50 option2=60",
+            "/test6.ko",
+            "/test2.ko",
+            "/test5.ko option1=",
+            "/test8.ko",
+            "/test7.ko param1=4",
+            "/test9.ko param_x=1 param_y=2 param_z=3",
+            "/test10.ko option1=1",
+            "/test12.ko",
+            "/test11.ko",
             "/test13.ko",
     };
 
diff --git a/libmodprobe/libmodprobe_test.h b/libmodprobe/libmodprobe_test.h
index a001b69..e7b949f 100644
--- a/libmodprobe/libmodprobe_test.h
+++ b/libmodprobe/libmodprobe_test.h
@@ -19,5 +19,6 @@
 #include <string>
 #include <vector>
 
+extern std::string kernel_cmdline;
 extern std::vector<std::string> test_modules;
 extern std::vector<std::string> modules_loaded;
diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c
index 8212eba..5999e39 100644
--- a/libnetutils/ifc_utils.c
+++ b/libnetutils/ifc_utils.c
@@ -257,8 +257,8 @@
  *
  * Returns zero on success and negative errno on failure.
  */
-int ifc_act_on_address(int action, const char *name, const char *address,
-                       int prefixlen) {
+int ifc_act_on_address(int action, const char* name, const char* address, int prefixlen,
+                       bool nodad) {
     int ifindex, s, len, ret;
     struct sockaddr_storage ss;
     int saved_errno;
@@ -311,6 +311,7 @@
 
     // Interface address message header.
     req.r.ifa_family = ss.ss_family;
+    req.r.ifa_flags = nodad ? IFA_F_NODAD : 0;
     req.r.ifa_prefixlen = prefixlen;
     req.r.ifa_index = ifindex;
 
@@ -363,12 +364,12 @@
 
 // Returns zero on success and negative errno on failure.
 int ifc_add_address(const char *name, const char *address, int prefixlen) {
-    return ifc_act_on_address(RTM_NEWADDR, name, address, prefixlen);
+    return ifc_act_on_address(RTM_NEWADDR, name, address, prefixlen, /*nodad*/ false);
 }
 
 // Returns zero on success and negative errno on failure.
 int ifc_del_address(const char *name, const char * address, int prefixlen) {
-    return ifc_act_on_address(RTM_DELADDR, name, address, prefixlen);
+    return ifc_act_on_address(RTM_DELADDR, name, address, prefixlen, /*nodad*/ false);
 }
 
 /*
diff --git a/libnetutils/include/netutils/ifc.h b/libnetutils/include/netutils/ifc.h
index 3b27234..ee896ac 100644
--- a/libnetutils/include/netutils/ifc.h
+++ b/libnetutils/include/netutils/ifc.h
@@ -17,8 +17,9 @@
 #ifndef _NETUTILS_IFC_H_
 #define _NETUTILS_IFC_H_
 
-#include <sys/cdefs.h>
 #include <arpa/inet.h>
+#include <stdbool.h>
+#include <sys/cdefs.h>
 
 __BEGIN_DECLS
 
@@ -42,6 +43,8 @@
 
 extern int ifc_get_addr(const char *name, in_addr_t *addr);
 extern int ifc_set_addr(const char *name, in_addr_t addr);
+extern int ifc_act_on_address(int action, const char* name, const char* address, int prefixlen,
+                              bool nodad);
 extern int ifc_add_address(const char *name, const char *address,
                            int prefixlen);
 extern int ifc_del_address(const char *name, const char *address,
diff --git a/libprocessgroup/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp
index 20ae2be..2fc920b 100644
--- a/libprocessgroup/cgroup_map.cpp
+++ b/libprocessgroup/cgroup_map.cpp
@@ -71,7 +71,12 @@
     if (!HasValue()) return false;
 
     if (state_ == UNKNOWN) {
-        state_ = access(GetProcsFilePath("", 0, 0).c_str(), F_OK) == 0 ? USABLE : MISSING;
+        if (ACgroupController_getFlags != nullptr) {
+            uint32_t flags = ACgroupController_getFlags(controller_);
+            state_ = (flags & CGROUPRC_CONTROLLER_FLAG_MOUNTED) != 0 ? USABLE : MISSING;
+        } else {
+            state_ = access(GetProcsFilePath("", 0, 0).c_str(), F_OK) == 0 ? USABLE : MISSING;
+        }
     }
 
     return state_ == USABLE;
@@ -161,9 +166,16 @@
     auto controller_count = ACgroupFile_getControllerCount();
     for (uint32_t i = 0; i < controller_count; ++i) {
         const ACgroupController* controller = ACgroupFile_getController(i);
-        LOG(INFO) << "\t" << ACgroupController_getName(controller) << " ver "
-                  << ACgroupController_getVersion(controller) << " path "
-                  << ACgroupController_getPath(controller);
+        if (ACgroupController_getFlags != nullptr) {
+            LOG(INFO) << "\t" << ACgroupController_getName(controller) << " ver "
+                      << ACgroupController_getVersion(controller) << " path "
+                      << ACgroupController_getPath(controller) << " flags "
+                      << ACgroupController_getFlags(controller);
+        } else {
+            LOG(INFO) << "\t" << ACgroupController_getName(controller) << " ver "
+                      << ACgroupController_getVersion(controller) << " path "
+                      << ACgroupController_getPath(controller);
+        }
     }
 }
 
diff --git a/libprocessgroup/cgrouprc/cgroup_controller.cpp b/libprocessgroup/cgrouprc/cgroup_controller.cpp
index d064d31..5a326e5 100644
--- a/libprocessgroup/cgrouprc/cgroup_controller.cpp
+++ b/libprocessgroup/cgrouprc/cgroup_controller.cpp
@@ -27,6 +27,11 @@
     return controller->version();
 }
 
+uint32_t ACgroupController_getFlags(const ACgroupController* controller) {
+    CHECK(controller != nullptr);
+    return controller->flags();
+}
+
 const char* ACgroupController_getName(const ACgroupController* controller) {
     CHECK(controller != nullptr);
     return controller->name();
diff --git a/libprocessgroup/cgrouprc/include/android/cgrouprc.h b/libprocessgroup/cgrouprc/include/android/cgrouprc.h
index 0f6a9cd..0ce5123 100644
--- a/libprocessgroup/cgrouprc/include/android/cgrouprc.h
+++ b/libprocessgroup/cgrouprc/include/android/cgrouprc.h
@@ -66,10 +66,21 @@
         __INTRODUCED_IN(29);
 
 /**
- * Flag bitmask to be used when ACgroupController_getFlags can be exported
+ * Flag bitmask used in ACgroupController_getFlags
  */
 #define CGROUPRC_CONTROLLER_FLAG_MOUNTED 0x1
 
+#if __ANDROID_API__ >= __ANDROID_API_R__
+
+/**
+ * Returns the flags bitmask of the given controller.
+ * If the given controller is null, return 0.
+ */
+__attribute__((warn_unused_result, weak)) uint32_t ACgroupController_getFlags(
+        const ACgroupController*) __INTRODUCED_IN(30);
+
+#endif
+
 /**
  * Returns the name of the given controller.
  * If the given controller is null, return nullptr.
diff --git a/libprocessgroup/cgrouprc/libcgrouprc.llndk.txt b/libprocessgroup/cgrouprc/libcgrouprc.llndk.txt
index 91df392..ce5c419 100644
--- a/libprocessgroup/cgrouprc/libcgrouprc.llndk.txt
+++ b/libprocessgroup/cgrouprc/libcgrouprc.llndk.txt
@@ -9,3 +9,10 @@
   local:
     *;
 };
+
+LIBCGROUPRC_30 { # introduced=30
+  global:
+    ACgroupController_getFlags;
+  local:
+    *;
+};
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index 0b38b6b..4aa439a 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -30,8 +30,7 @@
 bool CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path);
 
 bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache = false);
-bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles,
-                        bool use_fd_cache = false);
+bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles);
 
 #ifndef __ANDROID_VNDK__
 
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 6272664..d669ebe 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -115,9 +115,8 @@
     TaskProfiles::GetInstance().DropResourceCaching();
 }
 
-bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles,
-                        bool use_fd_cache) {
-    return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles, use_fd_cache);
+bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles) {
+    return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles);
 }
 
 bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache) {
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 3f3dbd7..3f08535 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -43,12 +43,12 @@
     {
       "Name": "UClampMin",
       "Controller": "cpu",
-      "File": "cpu.util.min"
+      "File": "cpu.uclamp.min"
     },
     {
       "Name": "UClampMax",
       "Controller": "cpu",
-      "File": "cpu.util.max"
+      "File": "cpu.uclamp.max"
     }
   ],
 
diff --git a/libprocessgroup/sched_policy.cpp b/libprocessgroup/sched_policy.cpp
index 16339d3..698e74d 100644
--- a/libprocessgroup/sched_policy.cpp
+++ b/libprocessgroup/sched_policy.cpp
@@ -138,8 +138,17 @@
     return enabled;
 }
 
+static bool schedtune_enabled() {
+    return (CgroupMap::GetInstance().FindController("schedtune").IsUsable());
+}
+
+static bool cpuctl_enabled() {
+    return (CgroupMap::GetInstance().FindController("cpu").IsUsable());
+}
+
 bool schedboost_enabled() {
-    static bool enabled = (CgroupMap::GetInstance().FindController("schedtune").IsUsable());
+    static bool enabled = schedtune_enabled() || cpuctl_enabled();
+
     return enabled;
 }
 
@@ -162,7 +171,9 @@
 
     std::string group;
     if (schedboost_enabled()) {
-        if (getCGroupSubsys(tid, "schedtune", group) < 0) return -1;
+        if ((getCGroupSubsys(tid, "schedtune", group) < 0) &&
+            (getCGroupSubsys(tid, "cpu", group) < 0))
+		return -1;
     }
     if (group.empty() && cpusets_enabled()) {
         if (getCGroupSubsys(tid, "cpuset", group) < 0) return -1;
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 9447f86..4af4589 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -201,22 +201,6 @@
 }
 
 bool SetCgroupAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
-    std::lock_guard<std::mutex> lock(fd_mutex_);
-    if (IsFdValid()) {
-        // fd is cached, reuse it
-        if (!AddTidToCgroup(pid, fd_)) {
-            LOG(ERROR) << "Failed to add task into cgroup";
-            return false;
-        }
-        return true;
-    }
-
-    if (fd_ == FDS_INACCESSIBLE) {
-        // no permissions to access the file, ignore
-        return true;
-    }
-
-    // this is app-dependent path and fd is not cached or cached fd can't be used
     std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid);
     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(procs_path.c_str(), O_WRONLY | O_CLOEXEC)));
     if (tmp_fd < 0) {
@@ -270,7 +254,6 @@
 
 bool ApplyProfileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
     for (const auto& profile : profiles_) {
-        profile->EnableResourceCaching();
         if (!profile->ExecuteForProcess(uid, pid)) {
             PLOG(WARNING) << "ExecuteForProcess failed for aggregate profile";
         }
@@ -280,7 +263,6 @@
 
 bool ApplyProfileAction::ExecuteForTask(int tid) const {
     for (const auto& profile : profiles_) {
-        profile->EnableResourceCaching();
         if (!profile->ExecuteForTask(tid)) {
             PLOG(WARNING) << "ExecuteForTask failed for aggregate profile";
         }
@@ -288,6 +270,23 @@
     return true;
 }
 
+void ApplyProfileAction::EnableResourceCaching() {
+    for (const auto& profile : profiles_) {
+        profile->EnableResourceCaching();
+    }
+}
+
+void ApplyProfileAction::DropResourceCaching() {
+    for (const auto& profile : profiles_) {
+        profile->DropResourceCaching();
+    }
+}
+
+void TaskProfile::MoveTo(TaskProfile* profile) {
+    profile->elements_ = std::move(elements_);
+    profile->res_cached_ = res_cached_;
+}
+
 bool TaskProfile::ExecuteForProcess(uid_t uid, pid_t pid) const {
     for (const auto& element : elements_) {
         if (!element->ExecuteForProcess(uid, pid)) {
@@ -458,7 +457,15 @@
                 LOG(WARNING) << "Unknown profile action: " << action_name;
             }
         }
-        profiles_[profile_name] = profile;
+        auto iter = profiles_.find(profile_name);
+        if (iter == profiles_.end()) {
+            profiles_[profile_name] = profile;
+        } else {
+            // Move the content rather that replace the profile because old profile might be
+            // referenced from an aggregate profile if vendor overrides task profiles
+            profile->MoveTo(iter->second.get());
+            profile.reset();
+        }
     }
 
     const Json::Value& aggregateprofiles_val = root["AggregateProfiles"];
@@ -514,13 +521,10 @@
 }
 
 bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid,
-                                      const std::vector<std::string>& profiles, bool use_fd_cache) {
+                                      const std::vector<std::string>& profiles) {
     for (const auto& name : profiles) {
         TaskProfile* profile = GetProfile(name);
         if (profile != nullptr) {
-            if (use_fd_cache) {
-                profile->EnableResourceCaching();
-            }
             if (!profile->ExecuteForProcess(uid, pid)) {
                 PLOG(WARNING) << "Failed to apply " << name << " process profile";
             }
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index 9f2308c..28bc00c 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -143,6 +143,7 @@
     TaskProfile() : res_cached_(false) {}
 
     void Add(std::unique_ptr<ProfileAction> e) { elements_.push_back(std::move(e)); }
+    void MoveTo(TaskProfile* profile);
 
     bool ExecuteForProcess(uid_t uid, pid_t pid) const;
     bool ExecuteForTask(int tid) const;
@@ -162,6 +163,8 @@
 
     virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
     virtual bool ExecuteForTask(int tid) const;
+    virtual void EnableResourceCaching();
+    virtual void DropResourceCaching();
 
   private:
     std::vector<std::shared_ptr<TaskProfile>> profiles_;
@@ -175,8 +178,7 @@
     TaskProfile* GetProfile(const std::string& name) const;
     const ProfileAttribute* GetAttribute(const std::string& name) const;
     void DropResourceCaching() const;
-    bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles,
-                            bool use_fd_cache);
+    bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles);
     bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache);
 
   private:
diff --git a/libprocinfo/process.cpp b/libprocinfo/process.cpp
index 9194cf3..2efd49c 100644
--- a/libprocinfo/process.cpp
+++ b/libprocinfo/process.cpp
@@ -59,7 +59,6 @@
     case 'Z':
       return kProcessStateZombie;
     default:
-      LOG(ERROR) << "unknown process state: " << *state;
       return kProcessStateUnknown;
   }
 }
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index 88146e9..135904b 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -3,6 +3,7 @@
 cc_library {
     name: "libsparse",
     host_supported: true,
+    ramdisk_available: true,
     recovery_available: true,
     unique_host_soname: true,
     srcs: [
diff --git a/libstats/socket/Android.bp b/libstats/socket/Android.bp
index 94c405d..9fd9fbc 100644
--- a/libstats/socket/Android.bp
+++ b/libstats/socket/Android.bp
@@ -40,6 +40,14 @@
         "libcutils",
         "liblog",
     ],
+
+    // enumerate stable entry points for APEX use
+    stubs: {
+        symbol_file: "libstatssocket.map.txt",
+        versions: [
+            "1",
+        ],
+    }
 }
 
 cc_library_headers {
@@ -67,3 +75,22 @@
         "libgtest_prod",
     ],
 }
+
+cc_test {
+    name: "libstatssocket_test",
+    srcs: ["tests/stats_event_test.cpp"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    static_libs: [
+        "libgmock",
+        "libstatssocket",
+    ],
+    shared_libs: [
+        "libcutils",
+        "liblog",
+        "libutils",
+    ],
+    test_suites: ["device_tests"],
+}
diff --git a/libstats/socket/benchmark/stats_event_benchmark.cpp b/libstats/socket/benchmark/stats_event_benchmark.cpp
index b487c4d..9488168 100644
--- a/libstats/socket/benchmark/stats_event_benchmark.cpp
+++ b/libstats/socket/benchmark/stats_event_benchmark.cpp
@@ -22,7 +22,8 @@
     stats_event_set_atom_id(event, 100);
 
     // randomly sample atom size
-    for (int i = 0; i < rand() % 800; i++) {
+    int numElements = rand() % 800;
+    for (int i = 0; i < numElements; i++) {
         stats_event_write_int32(event, i);
     }
 
diff --git a/libstats/socket/libstatssocket.map.txt b/libstats/socket/libstatssocket.map.txt
new file mode 100644
index 0000000..55bfbda
--- /dev/null
+++ b/libstats/socket/libstatssocket.map.txt
@@ -0,0 +1,23 @@
+LIBSTATSSOCKET {
+    global:
+        stats_event_obtain; # apex # introduced=1
+        stats_event_build; # apex # introduced=1
+        stats_event_write; # apex # introduced=1
+        stats_event_release; # apex # introduced=1
+        stats_event_set_atom_id; # apex # introduced=1
+        stats_event_write_int32; # apex # introduced=1
+        stats_event_write_int64; # apex # introduced=1
+        stats_event_write_float; # apex # introduced=1
+        stats_event_write_bool; # apex # introduced=1
+        stats_event_write_byte_array; # apex # introduced=1
+        stats_event_write_string8; # apex # introduced=1
+        stats_event_write_attribution_chain; # apex # introduced=1
+        stats_event_write_key_value_pairs; # apex # introduced=1
+        stats_event_add_bool_annotation; # apex # introduced=1
+        stats_event_add_int32_annotation; # apex # introduced=1
+        stats_event_get_atom_id; # apex # introduced=1
+        stats_event_get_buffer; # apex # introduced=1
+        stats_event_get_errors; # apex # introduced=1
+    local:
+        *;
+};
diff --git a/libstats/socket/tests/stats_event_test.cpp b/libstats/socket/tests/stats_event_test.cpp
new file mode 100644
index 0000000..cf0592c
--- /dev/null
+++ b/libstats/socket/tests/stats_event_test.cpp
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "stats_event.h"
+#include <gtest/gtest.h>
+#include <utils/SystemClock.h>
+
+using std::string;
+using std::vector;
+
+// Side-effect: this function moves the start of the buffer past the read value
+template <class T>
+T readNext(uint8_t** buffer) {
+    T value = *(T*)(*buffer);
+    *buffer += sizeof(T);
+    return value;
+}
+
+void checkTypeHeader(uint8_t** buffer, uint8_t typeId, uint8_t numAnnotations = 0) {
+    uint8_t typeHeader = (numAnnotations << 4) | typeId;
+    EXPECT_EQ(readNext<uint8_t>(buffer), typeHeader);
+}
+
+template <class T>
+void checkScalar(uint8_t** buffer, T expectedValue) {
+    EXPECT_EQ(readNext<T>(buffer), expectedValue);
+}
+
+void checkString(uint8_t** buffer, const string& expectedString) {
+    uint32_t size = readNext<uint32_t>(buffer);
+    string parsedString((char*)(*buffer), size);
+    EXPECT_EQ(parsedString, expectedString);
+    *buffer += size;  // move buffer past string we just read
+}
+
+void checkByteArray(uint8_t** buffer, const vector<uint8_t>& expectedByteArray) {
+    uint32_t size = readNext<uint32_t>(buffer);
+    vector<uint8_t> parsedByteArray(*buffer, *buffer + size);
+    EXPECT_EQ(parsedByteArray, expectedByteArray);
+    *buffer += size;  // move buffer past byte array we just read
+}
+
+template <class T>
+void checkAnnotation(uint8_t** buffer, uint8_t annotationId, uint8_t typeId, T annotationValue) {
+    EXPECT_EQ(readNext<uint8_t>(buffer), annotationId);
+    EXPECT_EQ(readNext<uint8_t>(buffer), typeId);
+    checkScalar<T>(buffer, annotationValue);
+}
+
+void checkMetadata(uint8_t** buffer, uint8_t numElements, int64_t startTime, int64_t endTime,
+                   uint32_t atomId) {
+    // All events start with OBJECT_TYPE id.
+    checkTypeHeader(buffer, OBJECT_TYPE);
+
+    // We increment by 2 because the number of elements listed in the
+    // serialization accounts for the timestamp and atom id as well.
+    checkScalar(buffer, static_cast<uint8_t>(numElements + 2));
+
+    // Check timestamp
+    checkTypeHeader(buffer, INT64_TYPE);
+    int64_t timestamp = readNext<int64_t>(buffer);
+    EXPECT_GE(timestamp, startTime);
+    EXPECT_LE(timestamp, endTime);
+
+    // Check atom id
+    checkTypeHeader(buffer, INT32_TYPE);
+    checkScalar(buffer, atomId);
+}
+
+TEST(StatsEventTest, TestScalars) {
+    uint32_t atomId = 100;
+    int32_t int32Value = -5;
+    int64_t int64Value = -2 * android::elapsedRealtimeNano();
+    float floatValue = 2.0;
+    bool boolValue = false;
+
+    int64_t startTime = android::elapsedRealtimeNano();
+    struct stats_event* event = stats_event_obtain();
+    stats_event_set_atom_id(event, atomId);
+    stats_event_write_int32(event, int32Value);
+    stats_event_write_int64(event, int64Value);
+    stats_event_write_float(event, floatValue);
+    stats_event_write_bool(event, boolValue);
+    stats_event_build(event);
+    int64_t endTime = android::elapsedRealtimeNano();
+
+    size_t bufferSize;
+    uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
+    uint8_t* bufferEnd = buffer + bufferSize;
+
+    checkMetadata(&buffer, /*numElements=*/4, startTime, endTime, atomId);
+
+    // check int32 element
+    checkTypeHeader(&buffer, INT32_TYPE);
+    checkScalar(&buffer, int32Value);
+
+    // check int64 element
+    checkTypeHeader(&buffer, INT64_TYPE);
+    checkScalar(&buffer, int64Value);
+
+    // check float element
+    checkTypeHeader(&buffer, FLOAT_TYPE);
+    checkScalar(&buffer, floatValue);
+
+    // check bool element
+    checkTypeHeader(&buffer, BOOL_TYPE);
+    checkScalar(&buffer, boolValue);
+
+    EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
+    EXPECT_EQ(stats_event_get_errors(event), 0);
+    stats_event_release(event);
+}
+
+TEST(StatsEventTest, TestStrings) {
+    uint32_t atomId = 100;
+    string str = "test_string";
+
+    int64_t startTime = android::elapsedRealtimeNano();
+    struct stats_event* event = stats_event_obtain();
+    stats_event_set_atom_id(event, atomId);
+    stats_event_write_string8(event, str.c_str());
+    stats_event_build(event);
+    int64_t endTime = android::elapsedRealtimeNano();
+
+    size_t bufferSize;
+    uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
+    uint8_t* bufferEnd = buffer + bufferSize;
+
+    checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
+
+    checkTypeHeader(&buffer, STRING_TYPE);
+    checkString(&buffer, str);
+
+    EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
+    EXPECT_EQ(stats_event_get_errors(event), 0);
+    stats_event_release(event);
+}
+
+TEST(StatsEventTest, TestByteArrays) {
+    uint32_t atomId = 100;
+    vector<uint8_t> message = {'b', 'y', 't', '\0', 'e', 's'};
+
+    int64_t startTime = android::elapsedRealtimeNano();
+    struct stats_event* event = stats_event_obtain();
+    stats_event_set_atom_id(event, atomId);
+    stats_event_write_byte_array(event, message.data(), message.size());
+    stats_event_build(event);
+    int64_t endTime = android::elapsedRealtimeNano();
+
+    size_t bufferSize;
+    uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
+    uint8_t* bufferEnd = buffer + bufferSize;
+
+    checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
+
+    checkTypeHeader(&buffer, BYTE_ARRAY_TYPE);
+    checkByteArray(&buffer, message);
+
+    EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
+    EXPECT_EQ(stats_event_get_errors(event), 0);
+    stats_event_release(event);
+}
+
+TEST(StatsEventTest, TestAttributionChains) {
+    uint32_t atomId = 100;
+
+    uint8_t numNodes = 50;
+    uint32_t uids[numNodes];
+    vector<string> tags(numNodes);  // storage that cTag elements point to
+    const char* cTags[numNodes];
+    for (int i = 0; i < (int)numNodes; i++) {
+        uids[i] = i;
+        tags.push_back("test" + std::to_string(i));
+        cTags[i] = tags[i].c_str();
+    }
+
+    int64_t startTime = android::elapsedRealtimeNano();
+    struct stats_event* event = stats_event_obtain();
+    stats_event_set_atom_id(event, atomId);
+    stats_event_write_attribution_chain(event, uids, cTags, numNodes);
+    stats_event_build(event);
+    int64_t endTime = android::elapsedRealtimeNano();
+
+    size_t bufferSize;
+    uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
+    uint8_t* bufferEnd = buffer + bufferSize;
+
+    checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
+
+    checkTypeHeader(&buffer, ATTRIBUTION_CHAIN_TYPE);
+    checkScalar(&buffer, numNodes);
+    for (int i = 0; i < numNodes; i++) {
+        checkScalar(&buffer, uids[i]);
+        checkString(&buffer, tags[i]);
+    }
+
+    EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
+    EXPECT_EQ(stats_event_get_errors(event), 0);
+    stats_event_release(event);
+}
+
+TEST(StatsEventTest, TestKeyValuePairs) {
+    uint32_t atomId = 100;
+
+    uint8_t numPairs = 4;
+    struct key_value_pair pairs[numPairs];
+    pairs[0] = {.key = 0, .valueType = INT32_TYPE, .int32Value = -1};
+    pairs[1] = {.key = 1, .valueType = INT64_TYPE, .int64Value = 0x123456789};
+    pairs[2] = {.key = 2, .valueType = FLOAT_TYPE, .floatValue = 5.5};
+    string str = "test_key_value_pair_string";
+    pairs[3] = {.key = 3, .valueType = STRING_TYPE, .stringValue = str.c_str()};
+
+    int64_t startTime = android::elapsedRealtimeNano();
+    struct stats_event* event = stats_event_obtain();
+    stats_event_set_atom_id(event, atomId);
+    stats_event_write_key_value_pairs(event, pairs, numPairs);
+    stats_event_build(event);
+    int64_t endTime = android::elapsedRealtimeNano();
+
+    size_t bufferSize;
+    uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
+    uint8_t* bufferEnd = buffer + bufferSize;
+
+    checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
+
+    checkTypeHeader(&buffer, KEY_VALUE_PAIRS_TYPE);
+    checkScalar(&buffer, numPairs);
+
+    // first pair
+    checkScalar(&buffer, pairs[0].key);
+    checkTypeHeader(&buffer, pairs[0].valueType);
+    checkScalar(&buffer, pairs[0].int32Value);
+
+    // second pair
+    checkScalar(&buffer, pairs[1].key);
+    checkTypeHeader(&buffer, pairs[1].valueType);
+    checkScalar(&buffer, pairs[1].int64Value);
+
+    // third pair
+    checkScalar(&buffer, pairs[2].key);
+    checkTypeHeader(&buffer, pairs[2].valueType);
+    checkScalar(&buffer, pairs[2].floatValue);
+
+    // fourth pair
+    checkScalar(&buffer, pairs[3].key);
+    checkTypeHeader(&buffer, pairs[3].valueType);
+    checkString(&buffer, str);
+
+    EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
+    EXPECT_EQ(stats_event_get_errors(event), 0);
+    stats_event_release(event);
+}
+
+TEST(StatsEventTest, TestAnnotations) {
+    uint32_t atomId = 100;
+
+    // first element information
+    bool boolValue = false;
+    uint8_t boolAnnotation1Id = 1;
+    uint8_t boolAnnotation2Id = 2;
+    bool boolAnnotation1Value = true;
+    int32_t boolAnnotation2Value = 3;
+
+    // second element information
+    float floatValue = -5.0;
+    uint8_t floatAnnotation1Id = 3;
+    uint8_t floatAnnotation2Id = 4;
+    int32_t floatAnnotation1Value = 8;
+    bool floatAnnotation2Value = false;
+
+    int64_t startTime = android::elapsedRealtimeNano();
+    struct stats_event* event = stats_event_obtain();
+    stats_event_set_atom_id(event, 100);
+    stats_event_write_bool(event, boolValue);
+    stats_event_add_bool_annotation(event, boolAnnotation1Id, boolAnnotation1Value);
+    stats_event_add_int32_annotation(event, boolAnnotation2Id, boolAnnotation2Value);
+    stats_event_write_float(event, floatValue);
+    stats_event_add_int32_annotation(event, floatAnnotation1Id, floatAnnotation1Value);
+    stats_event_add_bool_annotation(event, floatAnnotation2Id, floatAnnotation2Value);
+    stats_event_build(event);
+    int64_t endTime = android::elapsedRealtimeNano();
+
+    size_t bufferSize;
+    uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
+    uint8_t* bufferEnd = buffer + bufferSize;
+
+    checkMetadata(&buffer, /*numElements=*/2, startTime, endTime, atomId);
+
+    // check first element
+    checkTypeHeader(&buffer, BOOL_TYPE, /*numAnnotations=*/2);
+    checkScalar(&buffer, boolValue);
+    checkAnnotation(&buffer, boolAnnotation1Id, BOOL_TYPE, boolAnnotation1Value);
+    checkAnnotation(&buffer, boolAnnotation2Id, INT32_TYPE, boolAnnotation2Value);
+
+    // check second element
+    checkTypeHeader(&buffer, FLOAT_TYPE, /*numAnnotations=*/2);
+    checkScalar(&buffer, floatValue);
+    checkAnnotation(&buffer, floatAnnotation1Id, INT32_TYPE, floatAnnotation1Value);
+    checkAnnotation(&buffer, floatAnnotation2Id, BOOL_TYPE, floatAnnotation2Value);
+
+    EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
+    EXPECT_EQ(stats_event_get_errors(event), 0);
+    stats_event_release(event);
+}
+
+TEST(StatsEventTest, TestNoAtomIdError) {
+    struct stats_event* event = stats_event_obtain();
+    // Don't set the atom id in order to trigger the error.
+    stats_event_build(event);
+
+    uint32_t errors = stats_event_get_errors(event);
+    EXPECT_NE(errors | ERROR_NO_ATOM_ID, 0);
+
+    stats_event_release(event);
+}
+
+TEST(StatsEventTest, TestOverflowError) {
+    struct stats_event* event = stats_event_obtain();
+    stats_event_set_atom_id(event, 100);
+    // Add 1000 int32s to the event. Each int32 takes 5 bytes so this will
+    // overflow the 4068 byte buffer.
+    for (int i = 0; i < 1000; i++) {
+        stats_event_write_int32(event, 0);
+    }
+    stats_event_build(event);
+
+    uint32_t errors = stats_event_get_errors(event);
+    EXPECT_NE(errors | ERROR_OVERFLOW, 0);
+
+    stats_event_release(event);
+}
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
index ccda5d1..627f0d4 100644
--- a/libsysutils/Android.bp
+++ b/libsysutils/Android.bp
@@ -39,6 +39,10 @@
         "clang-analyzer-security*",
         "android-*",
     ],
+    apex_available: [
+        "//apex_available:anyapex",
+        "//apex_available:platform",
+    ],
 }
 
 cc_test {
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 9cd3d65..3695f72 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -240,6 +240,7 @@
         "tests/files/offline/debug_frame_load_bias_arm/*",
         "tests/files/offline/eh_frame_bias_x86/*",
         "tests/files/offline/eh_frame_hdr_begin_x86_64/*",
+        "tests/files/offline/empty_arm64/*",
         "tests/files/offline/invalid_elf_offset_arm/*",
         "tests/files/offline/jit_debug_arm/*",
         "tests/files/offline/jit_debug_x86/*",
diff --git a/libunwindstack/Global.cpp b/libunwindstack/Global.cpp
index ec977e1..ee6c8a5 100644
--- a/libunwindstack/Global.cpp
+++ b/libunwindstack/Global.cpp
@@ -70,30 +70,28 @@
   // This also works:
   //   f0000-f2000 0 r-- /system/lib/libc.so
   //   f2000-f3000 2000 rw- /system/lib/libc.so
-  MapInfo* map_start = nullptr;
+  // It is also possible to see empty maps after the read-only like so:
+  //   f0000-f1000 0 r-- /system/lib/libc.so
+  //   f1000-f2000 0 ---
+  //   f2000-f3000 1000 r-x /system/lib/libc.so
+  //   f3000-f4000 2000 rw- /system/lib/libc.so
+  MapInfo* map_zero = nullptr;
   for (const auto& info : *maps) {
-    if (map_start != nullptr && map_start->name == info->name) {
-      if (info->offset != 0 &&
-          (info->flags & (PROT_READ | PROT_WRITE)) == (PROT_READ | PROT_WRITE)) {
-        Elf* elf = map_start->GetElf(memory_, arch());
-        uint64_t ptr;
-        if (elf->GetGlobalVariableOffset(variable, &ptr) && ptr != 0) {
-          uint64_t offset_end = info->offset + info->end - info->start;
-          if (ptr >= info->offset && ptr < offset_end) {
-            ptr = info->start + ptr - info->offset;
-            if (ReadVariableData(ptr)) {
-              break;
-            }
+    if (info->offset != 0 && (info->flags & (PROT_READ | PROT_WRITE)) == (PROT_READ | PROT_WRITE) &&
+        map_zero != nullptr && Searchable(info->name) && info->name == map_zero->name) {
+      Elf* elf = map_zero->GetElf(memory_, arch());
+      uint64_t ptr;
+      if (elf->GetGlobalVariableOffset(variable, &ptr) && ptr != 0) {
+        uint64_t offset_end = info->offset + info->end - info->start;
+        if (ptr >= info->offset && ptr < offset_end) {
+          ptr = info->start + ptr - info->offset;
+          if (ReadVariableData(ptr)) {
+            break;
           }
         }
-        map_start = nullptr;
       }
-    } else {
-      map_start = nullptr;
-    }
-    if (map_start == nullptr && (info->flags & PROT_READ) && info->offset == 0 &&
-        Searchable(info->name)) {
-      map_start = info.get();
+    } else if (info->offset == 0 && !info->name.empty()) {
+      map_zero = info.get();
     }
   }
 }
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index f2dad84..31f3144 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -37,12 +37,12 @@
 bool MapInfo::InitFileMemoryFromPreviousReadOnlyMap(MemoryFileAtOffset* memory) {
   // One last attempt, see if the previous map is read-only with the
   // same name and stretches across this map.
-  if (prev_map == nullptr || prev_map->flags != PROT_READ) {
+  if (prev_real_map == nullptr || prev_real_map->flags != PROT_READ) {
     return false;
   }
 
-  uint64_t map_size = end - prev_map->end;
-  if (!memory->Init(name, prev_map->offset, map_size)) {
+  uint64_t map_size = end - prev_real_map->end;
+  if (!memory->Init(name, prev_real_map->offset, map_size)) {
     return false;
   }
 
@@ -51,12 +51,12 @@
     return false;
   }
 
-  if (!memory->Init(name, prev_map->offset, max_size)) {
+  if (!memory->Init(name, prev_real_map->offset, max_size)) {
     return false;
   }
 
-  elf_offset = offset - prev_map->offset;
-  elf_start_offset = prev_map->offset;
+  elf_offset = offset - prev_real_map->offset;
+  elf_start_offset = prev_real_map->offset;
   return true;
 }
 
@@ -112,8 +112,8 @@
     // Need to check how to set the elf start offset. If this map is not
     // the r-x map of a r-- map, then use the real offset value. Otherwise,
     // use 0.
-    if (prev_map == nullptr || prev_map->offset != 0 || prev_map->flags != PROT_READ ||
-        prev_map->name != name) {
+    if (prev_real_map == nullptr || prev_real_map->offset != 0 ||
+        prev_real_map->flags != PROT_READ || prev_real_map->name != name) {
       elf_start_offset = offset;
     }
     return memory.release();
@@ -172,20 +172,20 @@
   // doesn't guarantee that this invariant will always be true. However,
   // if that changes, there is likely something else that will change and
   // break something.
-  if (offset == 0 || name.empty() || prev_map == nullptr || prev_map->name != name ||
-      prev_map->offset >= offset) {
+  if (offset == 0 || name.empty() || prev_real_map == nullptr || prev_real_map->name != name ||
+      prev_real_map->offset >= offset) {
     return nullptr;
   }
 
   // Make sure that relative pc values are corrected properly.
-  elf_offset = offset - prev_map->offset;
+  elf_offset = offset - prev_real_map->offset;
   // Use this as the elf start offset, otherwise, you always get offsets into
   // the r-x section, which is not quite the right information.
-  elf_start_offset = prev_map->offset;
+  elf_start_offset = prev_real_map->offset;
 
   MemoryRanges* ranges = new MemoryRanges;
-  ranges->Insert(
-      new MemoryRange(process_memory, prev_map->start, prev_map->end - prev_map->start, 0));
+  ranges->Insert(new MemoryRange(process_memory, prev_real_map->start,
+                                 prev_real_map->end - prev_real_map->start, 0));
   ranges->Insert(new MemoryRange(process_memory, start, end - start, elf_offset));
 
   memory_backed_elf = true;
@@ -236,15 +236,15 @@
 
   if (!elf->valid()) {
     elf_start_offset = offset;
-  } else if (prev_map != nullptr && elf_start_offset != offset &&
-             prev_map->offset == elf_start_offset && prev_map->name == name) {
+  } else if (prev_real_map != nullptr && elf_start_offset != offset &&
+             prev_real_map->offset == elf_start_offset && prev_real_map->name == name) {
     // If there is a read-only map then a read-execute map that represents the
     // same elf object, make sure the previous map is using the same elf
     // object if it hasn't already been set.
-    std::lock_guard<std::mutex> guard(prev_map->mutex_);
-    if (prev_map->elf.get() == nullptr) {
-      prev_map->elf = elf;
-      prev_map->memory_backed_elf = memory_backed_elf;
+    std::lock_guard<std::mutex> guard(prev_real_map->mutex_);
+    if (prev_real_map->elf.get() == nullptr) {
+      prev_real_map->elf = elf;
+      prev_real_map->memory_backed_elf = memory_backed_elf;
     }
   }
   return elf.get();
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index 0ab68db..8f49ad9 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -60,6 +60,8 @@
 }
 
 bool Maps::Parse() {
+  MapInfo* prev_map = nullptr;
+  MapInfo* prev_real_map = nullptr;
   return android::procinfo::ReadMapFile(
       GetMapsFile(),
       [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t, const char* name) {
@@ -67,17 +69,24 @@
         if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
           flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
         }
-        maps_.emplace_back(
-            new MapInfo(maps_.empty() ? nullptr : maps_.back().get(), start, end, pgoff,
-                        flags, name));
+        maps_.emplace_back(new MapInfo(prev_map, prev_real_map, start, end, pgoff, flags, name));
+        prev_map = maps_.back().get();
+        if (!prev_map->IsBlank()) {
+          prev_real_map = prev_map;
+        }
       });
 }
 
 void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
                const std::string& name, uint64_t load_bias) {
+  MapInfo* prev_map = maps_.empty() ? nullptr : maps_.back().get();
+  MapInfo* prev_real_map = prev_map;
+  while (prev_real_map != nullptr && prev_real_map->IsBlank()) {
+    prev_real_map = prev_real_map->prev_map;
+  }
+
   auto map_info =
-      std::make_unique<MapInfo>(maps_.empty() ? nullptr : maps_.back().get(), start, end, offset,
-                                flags, name);
+      std::make_unique<MapInfo>(prev_map, prev_real_map, start, end, offset, flags, name);
   map_info->load_bias = load_bias;
   maps_.emplace_back(std::move(map_info));
 }
@@ -89,14 +98,21 @@
 
   // Set the prev_map values on the info objects.
   MapInfo* prev_map = nullptr;
+  MapInfo* prev_real_map = nullptr;
   for (const auto& map_info : maps_) {
     map_info->prev_map = prev_map;
+    map_info->prev_real_map = prev_real_map;
     prev_map = map_info.get();
+    if (!prev_map->IsBlank()) {
+      prev_real_map = prev_map;
+    }
   }
 }
 
 bool BufferMaps::Parse() {
   std::string content(buffer_);
+  MapInfo* prev_map = nullptr;
+  MapInfo* prev_real_map = nullptr;
   return android::procinfo::ReadMapFileContent(
       &content[0],
       [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t, const char* name) {
@@ -104,9 +120,11 @@
         if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
           flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
         }
-        maps_.emplace_back(
-            new MapInfo(maps_.empty() ? nullptr : maps_.back().get(), start, end, pgoff,
-                        flags, name));
+        maps_.emplace_back(new MapInfo(prev_map, prev_real_map, start, end, pgoff, flags, name));
+        prev_map = maps_.back().get();
+        if (!prev_map->IsBlank()) {
+          prev_real_map = prev_map;
+        }
       });
 }
 
diff --git a/libunwindstack/RegsArm64.cpp b/libunwindstack/RegsArm64.cpp
index 1df1dff..00b3367 100644
--- a/libunwindstack/RegsArm64.cpp
+++ b/libunwindstack/RegsArm64.cpp
@@ -100,8 +100,8 @@
   fn("x27", regs_[ARM64_REG_R27]);
   fn("x28", regs_[ARM64_REG_R28]);
   fn("x29", regs_[ARM64_REG_R29]);
-  fn("sp", regs_[ARM64_REG_SP]);
   fn("lr", regs_[ARM64_REG_LR]);
+  fn("sp", regs_[ARM64_REG_SP]);
   fn("pc", regs_[ARM64_REG_PC]);
   fn("pst", regs_[ARM64_REG_PSTATE]);
 }
@@ -110,10 +110,10 @@
   arm64_user_regs* user = reinterpret_cast<arm64_user_regs*>(remote_data);
 
   RegsArm64* regs = new RegsArm64();
-  memcpy(regs->RawData(), &user->regs[0], (ARM64_REG_R31 + 1) * sizeof(uint64_t));
+  memcpy(regs->RawData(), &user->regs[0], (ARM64_REG_R30 + 1) * sizeof(uint64_t));
   uint64_t* reg_data = reinterpret_cast<uint64_t*>(regs->RawData());
-  reg_data[ARM64_REG_PC] = user->pc;
   reg_data[ARM64_REG_SP] = user->sp;
+  reg_data[ARM64_REG_PC] = user->pc;
   reg_data[ARM64_REG_PSTATE] = user->pstate;
   return regs;
 }
diff --git a/libunwindstack/TEST_MAPPING b/libunwindstack/TEST_MAPPING
index 55771c0..909f897 100644
--- a/libunwindstack/TEST_MAPPING
+++ b/libunwindstack/TEST_MAPPING
@@ -2,6 +2,9 @@
   "presubmit": [
     {
       "name": "libunwindstack_unit_test"
+    },
+    {
+      "name": "CtsSimpleperfTestCases"
     }
   ]
 }
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index 8f0c516..052e79f 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -31,24 +31,26 @@
 class MemoryFileAtOffset;
 
 struct MapInfo {
-  MapInfo(MapInfo* map_info, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
-          const char* name)
+  MapInfo(MapInfo* prev_map, MapInfo* prev_real_map, uint64_t start, uint64_t end, uint64_t offset,
+          uint64_t flags, const char* name)
       : start(start),
         end(end),
         offset(offset),
         flags(flags),
         name(name),
-        prev_map(map_info),
+        prev_map(prev_map),
+        prev_real_map(prev_real_map),
         load_bias(INT64_MAX),
         build_id(0) {}
-  MapInfo(MapInfo* map_info, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
-          const std::string& name)
+  MapInfo(MapInfo* prev_map, MapInfo* prev_real_map, uint64_t start, uint64_t end, uint64_t offset,
+          uint64_t flags, const std::string& name)
       : start(start),
         end(end),
         offset(offset),
         flags(flags),
         name(name),
-        prev_map(map_info),
+        prev_map(prev_map),
+        prev_real_map(prev_real_map),
         load_bias(INT64_MAX),
         build_id(0) {}
   ~MapInfo();
@@ -71,6 +73,14 @@
   uint64_t elf_start_offset = 0;
 
   MapInfo* prev_map = nullptr;
+  // This is the previous map that is not empty with a 0 offset. For
+  // example, this set of maps:
+  //  1000-2000  r--p 000000 00:00 0 libc.so
+  //  2000-3000  ---p 000000 00:00 0 libc.so
+  //  3000-4000  r-xp 003000 00:00 0 libc.so
+  // The last map's prev_map would point to the 2000-3000 map, while the
+  // prev_real_map would point to the 1000-2000 map.
+  MapInfo* prev_real_map = nullptr;
 
   std::atomic_int64_t load_bias;
 
@@ -97,6 +107,8 @@
   // Returns the printable version of the build id (hex dump of raw data).
   std::string GetPrintableBuildID();
 
+  inline bool IsBlank() { return offset == 0 && flags == 0 && name.empty(); }
+
  private:
   MapInfo(const MapInfo&) = delete;
   void operator=(const MapInfo&) = delete;
diff --git a/libunwindstack/tests/DexFileTest.cpp b/libunwindstack/tests/DexFileTest.cpp
index 0149a42..1b54da6 100644
--- a/libunwindstack/tests/DexFileTest.cpp
+++ b/libunwindstack/tests/DexFileTest.cpp
@@ -105,7 +105,7 @@
             static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
 
   MemoryFake memory;
-  MapInfo info(nullptr, 0, 0x10000, 0, 0x5, tf.path);
+  MapInfo info(nullptr, nullptr, 0, 0x10000, 0, 0x5, tf.path);
   EXPECT_TRUE(DexFile::Create(0x500, &memory, &info) != nullptr);
 }
 
@@ -118,7 +118,7 @@
             static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
 
   MemoryFake memory;
-  MapInfo info(nullptr, 0x100, 0x10000, 0, 0x5, tf.path);
+  MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0, 0x5, tf.path);
   EXPECT_TRUE(DexFile::Create(0x600, &memory, &info) != nullptr);
 }
 
@@ -131,21 +131,21 @@
             static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
 
   MemoryFake memory;
-  MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, tf.path);
+  MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, tf.path);
   EXPECT_TRUE(DexFile::Create(0x400, &memory, &info) != nullptr);
 }
 
 TEST(DexFileTest, create_using_memory_empty_file) {
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
-  MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "");
+  MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, "");
   EXPECT_TRUE(DexFile::Create(0x4000, &memory, &info) != nullptr);
 }
 
 TEST(DexFileTest, create_using_memory_file_does_not_exist) {
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
-  MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "/does/not/exist");
+  MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, "/does/not/exist");
   EXPECT_TRUE(DexFile::Create(0x4000, &memory, &info) != nullptr);
 }
 
@@ -158,7 +158,7 @@
 
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
-  MapInfo info(nullptr, 0x4000, 0x10000, 0x200, 0x5, "/does/not/exist");
+  MapInfo info(nullptr, nullptr, 0x4000, 0x10000, 0x200, 0x5, "/does/not/exist");
   std::unique_ptr<DexFile> dex_file = DexFile::Create(0x4000, &memory, &info);
   ASSERT_TRUE(dex_file != nullptr);
 
@@ -171,7 +171,7 @@
 TEST(DexFileTest, get_method) {
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
-  MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "");
+  MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, "");
   std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
   ASSERT_TRUE(dex_file != nullptr);
 
@@ -189,7 +189,7 @@
 TEST(DexFileTest, get_method_empty) {
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
-  MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "");
+  MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, "");
   std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
   ASSERT_TRUE(dex_file != nullptr);
 
diff --git a/libunwindstack/tests/DexFilesTest.cpp b/libunwindstack/tests/DexFilesTest.cpp
index 0dd3af6..477cf8e 100644
--- a/libunwindstack/tests/DexFilesTest.cpp
+++ b/libunwindstack/tests/DexFilesTest.cpp
@@ -64,7 +64,11 @@
                        "f000-11000 r--p 00000000 00:00 0 /fake/elf3\n"
                        "100000-110000 rw-p 00f1000 00:00 0 /fake/elf3\n"
                        "200000-210000 rw-p 0002000 00:00 0 /fake/elf3\n"
-                       "300000-400000 rw-p 0003000 00:00 0 /fake/elf3\n"));
+                       "300000-400000 rw-p 0003000 00:00 0 /fake/elf3\n"
+                       "500000-501000 r--p 0000000 00:00 0 /fake/elf4\n"
+                       "501000-502000 ---p 0000000 00:00 0\n"
+                       "503000-510000 rw-p 0003000 00:00 0 /fake/elf4\n"
+                       "510000-520000 rw-p 0010000 00:00 0 /fake/elf4\n"));
     ASSERT_TRUE(maps_->Parse());
 
     // Global variable in a section that is not readable.
@@ -81,6 +85,11 @@
     map_info = maps_->Get(kMapGlobal);
     ASSERT_TRUE(map_info != nullptr);
     CreateFakeElf(map_info, 0xf1800, 0xf1000, 0xf1000, 0x10000);
+
+    // Global variable set in this map, but there is an empty map before rw map.
+    map_info = maps_->Get(kMapGlobalAfterEmpty);
+    ASSERT_TRUE(map_info != nullptr);
+    CreateFakeElf(map_info, 0x3800, 0x3000, 0x3000, 0xd000);
   }
 
   void SetUp() override {
@@ -102,6 +111,8 @@
   static constexpr size_t kMapGlobalRw = 6;
   static constexpr size_t kMapDexFileEntries = 7;
   static constexpr size_t kMapDexFiles = 8;
+  static constexpr size_t kMapGlobalAfterEmpty = 9;
+  static constexpr size_t kMapDexFilesAfterEmpty = 12;
 
   std::shared_ptr<Memory> process_memory_;
   MemoryFake* memory_;
@@ -328,4 +339,18 @@
   EXPECT_EQ(0x123U, method_offset);
 }
 
+TEST_F(DexFilesTest, get_method_information_with_empty_map) {
+  std::string method_name = "nothing";
+  uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFilesAfterEmpty);
+
+  WriteDescriptor32(0x503800, 0x506000);
+  WriteEntry32(0x506000, 0, 0, 0x510000);
+  WriteDex(0x510000);
+
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x510100, &method_name, &method_offset);
+  EXPECT_EQ("Main.<init>", method_name);
+  EXPECT_EQ(0U, method_offset);
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfCacheTest.cpp b/libunwindstack/tests/ElfCacheTest.cpp
index 5735858..5f13546 100644
--- a/libunwindstack/tests/ElfCacheTest.cpp
+++ b/libunwindstack/tests/ElfCacheTest.cpp
@@ -78,8 +78,8 @@
 
   uint64_t start = 0x1000;
   uint64_t end = 0x20000;
-  MapInfo info1(nullptr, start, end, 0, 0x5, tf.path);
-  MapInfo info2(nullptr, start, end, 0, 0x5, tf.path);
+  MapInfo info1(nullptr, nullptr, start, end, 0, 0x5, tf.path);
+  MapInfo info2(nullptr, nullptr, start, end, 0, 0x5, tf.path);
 
   Elf* elf1 = info1.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf1->valid());
@@ -119,17 +119,17 @@
   uint64_t start = 0x1000;
   uint64_t end = 0x20000;
   // Will have an elf at offset 0 in file.
-  MapInfo info0_1(nullptr, start, end, 0, 0x5, tf.path);
-  MapInfo info0_2(nullptr, start, end, 0, 0x5, tf.path);
+  MapInfo info0_1(nullptr, nullptr, start, end, 0, 0x5, tf.path);
+  MapInfo info0_2(nullptr, nullptr, start, end, 0, 0x5, tf.path);
   // Will have an elf at offset 0x100 in file.
-  MapInfo info100_1(nullptr, start, end, 0x100, 0x5, tf.path);
-  MapInfo info100_2(nullptr, start, end, 0x100, 0x5, tf.path);
+  MapInfo info100_1(nullptr, nullptr, start, end, 0x100, 0x5, tf.path);
+  MapInfo info100_2(nullptr, nullptr, start, end, 0x100, 0x5, tf.path);
   // Will have an elf at offset 0x200 in file.
-  MapInfo info200_1(nullptr, start, end, 0x200, 0x5, tf.path);
-  MapInfo info200_2(nullptr, start, end, 0x200, 0x5, tf.path);
+  MapInfo info200_1(nullptr, nullptr, start, end, 0x200, 0x5, tf.path);
+  MapInfo info200_2(nullptr, nullptr, start, end, 0x200, 0x5, tf.path);
   // Will have an elf at offset 0 in file.
-  MapInfo info300_1(nullptr, start, end, 0x300, 0x5, tf.path);
-  MapInfo info300_2(nullptr, start, end, 0x300, 0x5, tf.path);
+  MapInfo info300_1(nullptr, nullptr, start, end, 0x300, 0x5, tf.path);
+  MapInfo info300_2(nullptr, nullptr, start, end, 0x300, 0x5, tf.path);
 
   Elf* elf0_1 = info0_1.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf0_1->valid());
@@ -216,10 +216,10 @@
   uint64_t start = 0x1000;
   uint64_t end = 0x20000;
   // Multiple info sections at different offsets will have non-zero elf offsets.
-  MapInfo info300_1(nullptr, start, end, 0x300, 0x5, tf.path);
-  MapInfo info300_2(nullptr, start, end, 0x300, 0x5, tf.path);
-  MapInfo info400_1(nullptr, start, end, 0x400, 0x5, tf.path);
-  MapInfo info400_2(nullptr, start, end, 0x400, 0x5, tf.path);
+  MapInfo info300_1(nullptr, nullptr, start, end, 0x300, 0x5, tf.path);
+  MapInfo info300_2(nullptr, nullptr, start, end, 0x300, 0x5, tf.path);
+  MapInfo info400_1(nullptr, nullptr, start, end, 0x400, 0x5, tf.path);
+  MapInfo info400_2(nullptr, nullptr, start, end, 0x400, 0x5, tf.path);
 
   Elf* elf300_1 = info300_1.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf300_1->valid());
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 4866345..1f3ed81 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -275,7 +275,7 @@
   elf.FakeSetInterface(interface);
 
   elf.FakeSetValid(true);
-  MapInfo map_info(nullptr, 0x1000, 0x2000, 0, 0, "");
+  MapInfo map_info(nullptr, nullptr, 0x1000, 0x2000, 0, 0, "");
 
   ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
 
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index 6c1cfa2..6d8d58e 100644
--- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -89,7 +89,7 @@
 };
 
 TEST_F(MapInfoCreateMemoryTest, end_le_start) {
-  MapInfo info(nullptr, 0x100, 0x100, 0, 0, elf_.path);
+  MapInfo info(nullptr, nullptr, 0x100, 0x100, 0, 0, elf_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() == nullptr);
@@ -108,7 +108,7 @@
 // Verify that if the offset is non-zero but there is no elf at the offset,
 // that the full file is used.
 TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) {
-  MapInfo info(nullptr, 0x100, 0x200, 0x100, 0, elf_.path);
+  MapInfo info(nullptr, nullptr, 0x100, 0x200, 0x100, 0, elf_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
@@ -129,8 +129,9 @@
 
   // Now verify the elf start offset is set correctly based on the previous
   // info.
-  MapInfo prev_info(nullptr, 0, 0x100, 0x10, 0, "");
+  MapInfo prev_info(nullptr, nullptr, 0, 0x100, 0x10, 0, "");
   info.prev_map = &prev_info;
+  info.prev_real_map = &prev_info;
 
   // No preconditions met, change each one until it should set the elf start
   // offset to zero.
@@ -177,7 +178,7 @@
 // Verify that if the offset is non-zero and there is an elf at that
 // offset, that only part of the file is used.
 TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) {
-  MapInfo info(nullptr, 0x100, 0x200, 0x1000, 0, elf_at_1000_.path);
+  MapInfo info(nullptr, nullptr, 0x100, 0x200, 0x1000, 0, elf_at_1000_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
@@ -202,7 +203,7 @@
 // embedded elf is bigger than the initial map, the new object is larger
 // than the original map size. Do this for a 32 bit elf and a 64 bit elf.
 TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
-  MapInfo info(nullptr, 0x5000, 0x6000, 0x1000, 0, elf32_at_map_.path);
+  MapInfo info(nullptr, nullptr, 0x5000, 0x6000, 0x1000, 0, elf32_at_map_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
@@ -220,7 +221,7 @@
 }
 
 TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
-  MapInfo info(nullptr, 0x7000, 0x8000, 0x2000, 0, elf64_at_map_.path);
+  MapInfo info(nullptr, nullptr, 0x7000, 0x8000, 0x2000, 0, elf64_at_map_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
@@ -243,14 +244,14 @@
   // be returned if the file mapping fails, but the device check is incorrect.
   std::vector<uint8_t> buffer(1024);
   uint64_t start = reinterpret_cast<uint64_t>(buffer.data());
-  MapInfo info(nullptr, start, start + buffer.size(), 0, 0x8000, "/dev/something");
+  MapInfo info(nullptr, nullptr, start, start + buffer.size(), 0, 0x8000, "/dev/something");
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() == nullptr);
 }
 
 TEST_F(MapInfoCreateMemoryTest, process_memory) {
-  MapInfo info(nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
+  MapInfo info(nullptr, nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
 
   Elf32_Ehdr ehdr = {};
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
diff --git a/libunwindstack/tests/MapInfoGetBuildIDTest.cpp b/libunwindstack/tests/MapInfoGetBuildIDTest.cpp
index 16451d1..6953e26 100644
--- a/libunwindstack/tests/MapInfoGetBuildIDTest.cpp
+++ b/libunwindstack/tests/MapInfoGetBuildIDTest.cpp
@@ -50,7 +50,8 @@
     elf_interface_ = new ElfInterfaceFake(memory_);
     elf_->FakeSetInterface(elf_interface_);
     elf_container_.reset(elf_);
-    map_info_.reset(new MapInfo(nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, tf_->path));
+    map_info_.reset(
+        new MapInfo(nullptr, nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, tf_->path));
   }
 
   void MultipleThreadTest(std::string expected_build_id);
@@ -64,7 +65,7 @@
 };
 
 TEST_F(MapInfoGetBuildIDTest, no_elf_and_no_valid_elf_in_memory) {
-  MapInfo info(nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
+  MapInfo info(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
 
   EXPECT_EQ("", info.GetBuildID());
   EXPECT_EQ("", info.GetPrintableBuildID());
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp
index d60b8b1..7f97814 100644
--- a/libunwindstack/tests/MapInfoGetElfTest.cpp
+++ b/libunwindstack/tests/MapInfoGetElfTest.cpp
@@ -68,7 +68,7 @@
 };
 
 TEST_F(MapInfoGetElfTest, invalid) {
-  MapInfo info(nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
+  MapInfo info(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
 
   // The map is empty, but this should still create an invalid elf object.
   Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
@@ -77,7 +77,7 @@
 }
 
 TEST_F(MapInfoGetElfTest, valid32) {
-  MapInfo info(nullptr, 0x3000, 0x4000, 0, PROT_READ, "");
+  MapInfo info(nullptr, nullptr, 0x3000, 0x4000, 0, PROT_READ, "");
 
   Elf32_Ehdr ehdr;
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
@@ -97,7 +97,7 @@
 }
 
 TEST_F(MapInfoGetElfTest, valid64) {
-  MapInfo info(nullptr, 0x8000, 0x9000, 0, PROT_READ, "");
+  MapInfo info(nullptr, nullptr, 0x8000, 0x9000, 0, PROT_READ, "");
 
   Elf64_Ehdr ehdr;
   TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
@@ -111,7 +111,7 @@
 }
 
 TEST_F(MapInfoGetElfTest, invalid_arch_mismatch) {
-  MapInfo info(nullptr, 0x3000, 0x4000, 0, PROT_READ, "");
+  MapInfo info(nullptr, nullptr, 0x3000, 0x4000, 0, PROT_READ, "");
 
   Elf32_Ehdr ehdr;
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
@@ -123,7 +123,7 @@
 }
 
 TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) {
-  MapInfo info(nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
+  MapInfo info(nullptr, nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
 
   TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, true,
                                                [&](uint64_t offset, const void* ptr, size_t size) {
@@ -139,7 +139,7 @@
 }
 
 TEST_F(MapInfoGetElfTest, gnu_debugdata_init64) {
-  MapInfo info(nullptr, 0x5000, 0x8000, 0, PROT_READ, "");
+  MapInfo info(nullptr, nullptr, 0x5000, 0x8000, 0, PROT_READ, "");
 
   TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, true,
                                                [&](uint64_t offset, const void* ptr, size_t size) {
@@ -155,7 +155,7 @@
 }
 
 TEST_F(MapInfoGetElfTest, end_le_start) {
-  MapInfo info(nullptr, 0x1000, 0x1000, 0, PROT_READ, elf_.path);
+  MapInfo info(nullptr, nullptr, 0x1000, 0x1000, 0, PROT_READ, elf_.path);
 
   Elf32_Ehdr ehdr;
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
@@ -182,7 +182,7 @@
 // Verify that if the offset is non-zero but there is no elf at the offset,
 // that the full file is used.
 TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_full_file) {
-  MapInfo info(nullptr, 0x1000, 0x2000, 0x100, PROT_READ, elf_.path);
+  MapInfo info(nullptr, nullptr, 0x1000, 0x2000, 0x100, PROT_READ, elf_.path);
 
   std::vector<uint8_t> buffer(0x1000);
   memset(buffer.data(), 0, buffer.size());
@@ -211,7 +211,7 @@
 // Verify that if the offset is non-zero and there is an elf at that
 // offset, that only part of the file is used.
 TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file) {
-  MapInfo info(nullptr, 0x1000, 0x2000, 0x2000, PROT_READ, elf_.path);
+  MapInfo info(nullptr, nullptr, 0x1000, 0x2000, 0x2000, PROT_READ, elf_.path);
 
   std::vector<uint8_t> buffer(0x4000);
   memset(buffer.data(), 0, buffer.size());
@@ -241,7 +241,7 @@
 // embedded elf is bigger than the initial map, the new object is larger
 // than the original map size. Do this for a 32 bit elf and a 64 bit elf.
 TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
-  MapInfo info(nullptr, 0x5000, 0x6000, 0x1000, PROT_READ, elf_.path);
+  MapInfo info(nullptr, nullptr, 0x5000, 0x6000, 0x1000, PROT_READ, elf_.path);
 
   std::vector<uint8_t> buffer(0x4000);
   memset(buffer.data(), 0, buffer.size());
@@ -269,7 +269,7 @@
 }
 
 TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
-  MapInfo info(nullptr, 0x7000, 0x8000, 0x1000, PROT_READ, elf_.path);
+  MapInfo info(nullptr, nullptr, 0x7000, 0x8000, 0x1000, PROT_READ, elf_.path);
 
   std::vector<uint8_t> buffer(0x4000);
   memset(buffer.data(), 0, buffer.size());
@@ -297,7 +297,7 @@
 }
 
 TEST_F(MapInfoGetElfTest, check_device_maps) {
-  MapInfo info(nullptr, 0x7000, 0x8000, 0x1000, PROT_READ | MAPS_FLAGS_DEVICE_MAP,
+  MapInfo info(nullptr, nullptr, 0x7000, 0x8000, 0x1000, PROT_READ | MAPS_FLAGS_DEVICE_MAP,
                "/dev/something");
 
   // Create valid elf data in process memory for this to verify that only
@@ -343,7 +343,7 @@
   wait = true;
   // Create all of the threads and have them do the GetElf at the same time
   // to make it likely that a race will occur.
-  MapInfo info(nullptr, 0x7000, 0x8000, 0x1000, PROT_READ, "");
+  MapInfo info(nullptr, nullptr, 0x7000, 0x8000, 0x1000, PROT_READ, "");
   for (size_t i = 0; i < kNumConcurrentThreads; i++) {
     std::thread* thread = new std::thread([i, this, &wait, &info, &elf_in_threads]() {
       while (wait)
@@ -373,8 +373,8 @@
 
 // Verify that previous maps don't automatically get the same elf object.
 TEST_F(MapInfoGetElfTest, prev_map_elf_not_set) {
-  MapInfo info1(nullptr, 0x1000, 0x2000, 0, PROT_READ, "/not/present");
-  MapInfo info2(&info1, 0x2000, 0x3000, 0, PROT_READ, elf_.path);
+  MapInfo info1(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, "/not/present");
+  MapInfo info2(&info1, &info1, 0x2000, 0x3000, 0, PROT_READ, elf_.path);
 
   Elf32_Ehdr ehdr;
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
@@ -389,8 +389,25 @@
 // Verify that a read-only map followed by a read-execute map will result
 // in the same elf object in both maps.
 TEST_F(MapInfoGetElfTest, read_only_followed_by_read_exec_share_elf) {
-  MapInfo r_info(nullptr, 0x1000, 0x2000, 0, PROT_READ, elf_.path);
-  MapInfo rw_info(&r_info, 0x2000, 0x3000, 0x1000, PROT_READ | PROT_EXEC, elf_.path);
+  MapInfo r_info(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, elf_.path);
+  MapInfo rw_info(&r_info, &r_info, 0x2000, 0x3000, 0x1000, PROT_READ | PROT_EXEC, elf_.path);
+
+  Elf32_Ehdr ehdr;
+  TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+  memory_->SetMemory(0x1000, &ehdr, sizeof(ehdr));
+  Elf* elf = rw_info.GetElf(process_memory_, ARCH_ARM);
+  ASSERT_TRUE(elf != nullptr);
+  ASSERT_TRUE(elf->valid());
+
+  ASSERT_EQ(elf, r_info.GetElf(process_memory_, ARCH_ARM));
+}
+
+// Verify that a read-only map followed by an empty map, then followed by
+// a read-execute map will result in the same elf object in both maps.
+TEST_F(MapInfoGetElfTest, read_only_followed_by_empty_then_read_exec_share_elf) {
+  MapInfo r_info(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, elf_.path);
+  MapInfo empty(&r_info, &r_info, 0x2000, 0x3000, 0, 0, "");
+  MapInfo rw_info(&empty, &r_info, 0x3000, 0x4000, 0x2000, PROT_READ | PROT_EXEC, elf_.path);
 
   Elf32_Ehdr ehdr;
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
diff --git a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
index da3dbf2..971d452 100644
--- a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
+++ b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
@@ -50,7 +50,7 @@
     process_memory_.reset(memory_);
     elf_ = new ElfFake(new MemoryFake);
     elf_container_.reset(elf_);
-    map_info_.reset(new MapInfo(nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, ""));
+    map_info_.reset(new MapInfo(nullptr, nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, ""));
   }
 
   void MultipleThreadTest(uint64_t expected_load_bias);
@@ -63,7 +63,7 @@
 };
 
 TEST_F(MapInfoGetLoadBiasTest, no_elf_and_no_valid_elf_in_memory) {
-  MapInfo info(nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
+  MapInfo info(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
 
   EXPECT_EQ(0U, info.GetLoadBias(process_memory_));
 }
diff --git a/libunwindstack/tests/MapInfoTest.cpp b/libunwindstack/tests/MapInfoTest.cpp
index ef76b1b..98edc0e 100644
--- a/libunwindstack/tests/MapInfoTest.cpp
+++ b/libunwindstack/tests/MapInfoTest.cpp
@@ -26,8 +26,8 @@
 namespace unwindstack {
 
 TEST(MapInfoTest, maps_constructor_const_char) {
-  MapInfo prev_map(nullptr, 0, 0, 0, 0, "");
-  MapInfo map_info(&prev_map, 1, 2, 3, 4, "map");
+  MapInfo prev_map(nullptr, nullptr, 0, 0, 0, 0, "");
+  MapInfo map_info(&prev_map, &prev_map, 1, 2, 3, 4, "map");
 
   EXPECT_EQ(&prev_map, map_info.prev_map);
   EXPECT_EQ(1UL, map_info.start);
@@ -42,8 +42,8 @@
 
 TEST(MapInfoTest, maps_constructor_string) {
   std::string name("string_map");
-  MapInfo prev_map(nullptr, 0, 0, 0, 0, "");
-  MapInfo map_info(&prev_map, 1, 2, 3, 4, name);
+  MapInfo prev_map(nullptr, nullptr, 0, 0, 0, 0, "");
+  MapInfo map_info(&prev_map, &prev_map, 1, 2, 3, 4, name);
 
   EXPECT_EQ(&prev_map, map_info.prev_map);
   EXPECT_EQ(1UL, map_info.start);
@@ -62,7 +62,7 @@
   elf->FakeSetInterface(interface);
   interface->FakePushFunctionData(FunctionData("function", 1000));
 
-  MapInfo map_info(nullptr, 1, 2, 3, 4, "");
+  MapInfo map_info(nullptr, nullptr, 1, 2, 3, 4, "");
   map_info.elf.reset(elf);
 
   std::string name;
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
index 9e7a6ab..724eeb5 100644
--- a/libunwindstack/tests/MapsTest.cpp
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -82,7 +82,7 @@
 }
 
 TEST(MapsTest, verify_parse_line) {
-  MapInfo info(nullptr, 0, 0, 0, 0, "");
+  MapInfo info(nullptr, nullptr, 0, 0, 0, 0, "");
 
   VerifyLine("01-02 rwxp 03 04:05 06\n", &info);
   EXPECT_EQ(1U, info.start);
@@ -155,7 +155,7 @@
 }
 
 TEST(MapsTest, verify_large_values) {
-  MapInfo info(nullptr, 0, 0, 0, 0, "");
+  MapInfo info(nullptr, nullptr, 0, 0, 0, 0, "");
 #if defined(__LP64__)
   VerifyLine("fabcdef012345678-f12345678abcdef8 rwxp f0b0d0f010305070 00:00 0\n", &info);
   EXPECT_EQ(0xfabcdef012345678UL, info.start);
diff --git a/libunwindstack/tests/RegsIterateTest.cpp b/libunwindstack/tests/RegsIterateTest.cpp
index bc95851..47e605a 100644
--- a/libunwindstack/tests/RegsIterateTest.cpp
+++ b/libunwindstack/tests/RegsIterateTest.cpp
@@ -111,8 +111,8 @@
   result.push_back({"x27", ARM64_REG_R27});
   result.push_back({"x28", ARM64_REG_R28});
   result.push_back({"x29", ARM64_REG_R29});
-  result.push_back({"sp", ARM64_REG_SP});
   result.push_back({"lr", ARM64_REG_LR});
+  result.push_back({"sp", ARM64_REG_SP});
   result.push_back({"pc", ARM64_REG_PC});
   result.push_back({"pst", ARM64_REG_PSTATE});
   return result;
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 472d1cf..0a33e2f 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -182,7 +182,7 @@
   RegsX86_64 regs_x86_64;
   RegsMips regs_mips;
   RegsMips64 regs_mips64;
-  MapInfo map_info(nullptr, 0x1000, 0x2000, 0, 0, "");
+  MapInfo map_info(nullptr, nullptr, 0x1000, 0x2000, 0, 0, "");
   Elf* invalid_elf = new Elf(nullptr);
   map_info.elf.reset(invalid_elf);
 
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index 364101a..c2bd836 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -169,15 +169,18 @@
 };
 
 std::unordered_map<std::string, uint32_t> UnwindOfflineTest::arm64_regs_ = {
-    {"x0", ARM64_REG_R0},   {"x1", ARM64_REG_R1},   {"x2", ARM64_REG_R2},   {"x3", ARM64_REG_R3},
-    {"x4", ARM64_REG_R4},   {"x5", ARM64_REG_R5},   {"x6", ARM64_REG_R6},   {"x7", ARM64_REG_R7},
-    {"x8", ARM64_REG_R8},   {"x9", ARM64_REG_R9},   {"x10", ARM64_REG_R10}, {"x11", ARM64_REG_R11},
-    {"x12", ARM64_REG_R12}, {"x13", ARM64_REG_R13}, {"x14", ARM64_REG_R14}, {"x15", ARM64_REG_R15},
-    {"x16", ARM64_REG_R16}, {"x17", ARM64_REG_R17}, {"x18", ARM64_REG_R18}, {"x19", ARM64_REG_R19},
-    {"x20", ARM64_REG_R20}, {"x21", ARM64_REG_R21}, {"x22", ARM64_REG_R22}, {"x23", ARM64_REG_R23},
-    {"x24", ARM64_REG_R24}, {"x25", ARM64_REG_R25}, {"x26", ARM64_REG_R26}, {"x27", ARM64_REG_R27},
-    {"x28", ARM64_REG_R28}, {"x29", ARM64_REG_R29}, {"sp", ARM64_REG_SP},   {"lr", ARM64_REG_LR},
-    {"pc", ARM64_REG_PC},
+    {"x0", ARM64_REG_R0},      {"x1", ARM64_REG_R1},   {"x2", ARM64_REG_R2},
+    {"x3", ARM64_REG_R3},      {"x4", ARM64_REG_R4},   {"x5", ARM64_REG_R5},
+    {"x6", ARM64_REG_R6},      {"x7", ARM64_REG_R7},   {"x8", ARM64_REG_R8},
+    {"x9", ARM64_REG_R9},      {"x10", ARM64_REG_R10}, {"x11", ARM64_REG_R11},
+    {"x12", ARM64_REG_R12},    {"x13", ARM64_REG_R13}, {"x14", ARM64_REG_R14},
+    {"x15", ARM64_REG_R15},    {"x16", ARM64_REG_R16}, {"x17", ARM64_REG_R17},
+    {"x18", ARM64_REG_R18},    {"x19", ARM64_REG_R19}, {"x20", ARM64_REG_R20},
+    {"x21", ARM64_REG_R21},    {"x22", ARM64_REG_R22}, {"x23", ARM64_REG_R23},
+    {"x24", ARM64_REG_R24},    {"x25", ARM64_REG_R25}, {"x26", ARM64_REG_R26},
+    {"x27", ARM64_REG_R27},    {"x28", ARM64_REG_R28}, {"x29", ARM64_REG_R29},
+    {"sp", ARM64_REG_SP},      {"lr", ARM64_REG_LR},   {"pc", ARM64_REG_PC},
+    {"pst", ARM64_REG_PSTATE},
 };
 
 std::unordered_map<std::string, uint32_t> UnwindOfflineTest::x86_regs_ = {
@@ -1697,4 +1700,40 @@
   EXPECT_EQ(0xffe67d10ULL, unwinder.frames()[16].sp);
 }
 
+TEST_F(UnwindOfflineTest, empty_arm64) {
+  ASSERT_NO_FATAL_FAILURE(Init("empty_arm64/", ARCH_ARM64));
+
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.Unwind();
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(7U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 00000000000963a4  libc.so (__ioctl+4)\n"
+      "  #01 pc 000000000005344c  libc.so (ioctl+140)\n"
+      "  #02 pc 0000000000050ce4  libbinder.so "
+      "(android::IPCThreadState::talkWithDriver(bool)+308)\n"
+      "  #03 pc 0000000000050e98  libbinder.so "
+      "(android::IPCThreadState::getAndExecuteCommand()+24)\n"
+      "  #04 pc 00000000000516ac  libbinder.so (android::IPCThreadState::joinThreadPool(bool)+60)\n"
+      "  #05 pc 00000000000443b0  netd (main+1056)\n"
+      "  #06 pc 0000000000045594  libc.so (__libc_init+108)\n",
+      frame_info);
+
+  EXPECT_EQ(0x72a02203a4U, unwinder.frames()[0].pc);
+  EXPECT_EQ(0x7ffb6c0b50U, unwinder.frames()[0].sp);
+  EXPECT_EQ(0x72a01dd44cU, unwinder.frames()[1].pc);
+  EXPECT_EQ(0x7ffb6c0b50U, unwinder.frames()[1].sp);
+  EXPECT_EQ(0x729f759ce4U, unwinder.frames()[2].pc);
+  EXPECT_EQ(0x7ffb6c0c50U, unwinder.frames()[2].sp);
+  EXPECT_EQ(0x729f759e98U, unwinder.frames()[3].pc);
+  EXPECT_EQ(0x7ffb6c0ce0U, unwinder.frames()[3].sp);
+  EXPECT_EQ(0x729f75a6acU, unwinder.frames()[4].pc);
+  EXPECT_EQ(0x7ffb6c0d10U, unwinder.frames()[4].sp);
+  EXPECT_EQ(0x5d478af3b0U, unwinder.frames()[5].pc);
+  EXPECT_EQ(0x7ffb6c0d40U, unwinder.frames()[5].sp);
+  EXPECT_EQ(0x72a01cf594U, unwinder.frames()[6].pc);
+  EXPECT_EQ(0x7ffb6c0f30U, unwinder.frames()[6].sp);
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/files/offline/empty_arm64/libbinder.so b/libunwindstack/tests/files/offline/empty_arm64/libbinder.so
new file mode 100644
index 0000000..f30384c
--- /dev/null
+++ b/libunwindstack/tests/files/offline/empty_arm64/libbinder.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/empty_arm64/libc.so b/libunwindstack/tests/files/offline/empty_arm64/libc.so
new file mode 100644
index 0000000..b05dcaf
--- /dev/null
+++ b/libunwindstack/tests/files/offline/empty_arm64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/empty_arm64/maps.txt b/libunwindstack/tests/files/offline/empty_arm64/maps.txt
new file mode 100644
index 0000000..edb83c6
--- /dev/null
+++ b/libunwindstack/tests/files/offline/empty_arm64/maps.txt
@@ -0,0 +1,9 @@
+5d4786b000-5d47893000 r--p 0 00:00 0   netd
+5d47893000-5d47894000 ---p 0 00:00 0
+5d47894000-5d47901000 --xp 29000 00:00 0   netd
+729f709000-729f750000 r--p 0 00:00 0   libbinder.so
+729f750000-729f751000 ---p 0 00:00 0
+729f751000-729f794000 --xp 48000 00:00 0   libbinder.so
+72a018a000-72a01c2000 r--p 0 00:00 0   libc.so
+72a01c2000-72a01c3000 ---p 0 00:00 0
+72a01c3000-72a023b000 --xp 39000 00:00 0   libc.so
diff --git a/libunwindstack/tests/files/offline/empty_arm64/netd b/libunwindstack/tests/files/offline/empty_arm64/netd
new file mode 100644
index 0000000..8a72e94
--- /dev/null
+++ b/libunwindstack/tests/files/offline/empty_arm64/netd
Binary files differ
diff --git a/libunwindstack/tests/files/offline/empty_arm64/regs.txt b/libunwindstack/tests/files/offline/empty_arm64/regs.txt
new file mode 100644
index 0000000..3d4279f
--- /dev/null
+++ b/libunwindstack/tests/files/offline/empty_arm64/regs.txt
@@ -0,0 +1,34 @@
+x0: 1d
+x1: c0306201
+x2: 7ffb6c0c50
+x3: 0
+x4: 0
+x5: 0
+x6: 0
+x7: 0
+x8: 1d
+x9: 7ffb6c0c00
+x10: 7ffb6c0c50
+x11: 7ffb6c0bd0
+x12: ffffff80ffffffd0
+x13: 0
+x14: 72a0240ce2
+x15: 20
+x16: 729f7a54e8
+x17: 72a01dd3c0
+x18: 72a0ac2000
+x19: 72a0666000
+x20: 719769b610
+x21: 719769b730
+x22: c0306201
+x23: fffffff7
+x24: 72a0666000
+x25: 0
+x26: 0
+x27: 0
+x28: 0
+x29: 7ffb6c0c30
+sp: 7ffb6c0b50
+lr: 72a01dd450
+pc: 72a02203a4
+pst: a0000000
diff --git a/libunwindstack/tests/files/offline/empty_arm64/stack.data b/libunwindstack/tests/files/offline/empty_arm64/stack.data
new file mode 100644
index 0000000..6d6108c
--- /dev/null
+++ b/libunwindstack/tests/files/offline/empty_arm64/stack.data
Binary files differ
diff --git a/libutils/Android.bp b/libutils/Android.bp
index efa4c41..3311793 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -159,6 +159,11 @@
             ],
         },
     },
+
+    apex_available: [
+        "//apex_available:anyapex",
+        "//apex_available:platform",
+    ],
 }
 
 cc_library {
diff --git a/libutils/StrongPointer_test.cpp b/libutils/StrongPointer_test.cpp
index 153cf96..7b2e37f 100644
--- a/libutils/StrongPointer_test.cpp
+++ b/libutils/StrongPointer_test.cpp
@@ -56,3 +56,18 @@
     }
     ASSERT_TRUE(isDeleted) << "foo was leaked!";
 }
+
+TEST(StrongPointer, NullptrComparison) {
+    sp<SPFoo> foo;
+    ASSERT_EQ(foo, nullptr);
+    ASSERT_EQ(nullptr, foo);
+}
+
+TEST(StrongPointer, PointerComparison) {
+    bool isDeleted;
+    sp<SPFoo> foo = new SPFoo(&isDeleted);
+    ASSERT_EQ(foo.get(), foo);
+    ASSERT_EQ(foo, foo.get());
+    ASSERT_NE(nullptr, foo);
+    ASSERT_NE(foo, nullptr);
+}
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index 31ca138..540dcf4 100644
--- a/libutils/Threads.cpp
+++ b/libutils/Threads.cpp
@@ -18,8 +18,8 @@
 #define LOG_TAG "libutils.threads"
 
 #include <assert.h>
-#include <utils/Thread.h>
 #include <utils/AndroidThreads.h>
+#include <utils/Thread.h>
 
 #if !defined(_WIN32)
 # include <sys/resource.h>
@@ -36,7 +36,10 @@
 
 #include <utils/Log.h>
 
+#if defined(__ANDROID__)
+#include <processgroup/processgroup.h>
 #include <processgroup/sched_policy.h>
+#endif
 
 #if defined(__ANDROID__)
 # define __android_unused
@@ -64,6 +67,7 @@
 
 typedef void* (*android_pthread_entry)(void*);
 
+#if defined(__ANDROID__)
 struct thread_data_t {
     thread_func_t   entryFunction;
     void*           userData;
@@ -79,10 +83,11 @@
         char * name = t->threadName;
         delete t;
         setpriority(PRIO_PROCESS, 0, prio);
+
+        // A new thread will be in its parent's sched group by default,
+        // so we just need to handle the background case.
         if (prio >= ANDROID_PRIORITY_BACKGROUND) {
-            set_sched_policy(0, SP_BACKGROUND);
-        } else {
-            set_sched_policy(0, SP_FOREGROUND);
+            SetTaskProfiles(0, {"SCHED_SP_BACKGROUND"}, true);
         }
 
         if (name) {
@@ -92,6 +97,7 @@
         return f(u);
     }
 };
+#endif
 
 void androidSetThreadName(const char* name) {
 #if defined(__linux__)
@@ -300,11 +306,19 @@
 {
     int rc = 0;
     int lasterr = 0;
+    int curr_pri = getpriority(PRIO_PROCESS, tid);
+
+    if (curr_pri == pri) {
+        return rc;
+    }
 
     if (pri >= ANDROID_PRIORITY_BACKGROUND) {
-        rc = set_sched_policy(tid, SP_BACKGROUND);
-    } else if (getpriority(PRIO_PROCESS, tid) >= ANDROID_PRIORITY_BACKGROUND) {
-        rc = set_sched_policy(tid, SP_FOREGROUND);
+        rc = SetTaskProfiles(tid, {"SCHED_SP_BACKGROUND"}, true) ? 0 : -1;
+    } else if (curr_pri >= ANDROID_PRIORITY_BACKGROUND) {
+        SchedPolicy policy = SP_FOREGROUND;
+        // Change to the sched policy group of the process.
+        get_sched_policy(getpid(), &policy);
+        rc = SetTaskProfiles(tid, {get_sched_policy_profile_name(policy)}, true) ? 0 : -1;
     }
 
     if (rc) {
diff --git a/libutils/include/utils/Flattenable.h b/libutils/include/utils/Flattenable.h
index 953b859..17c5e10 100644
--- a/libutils/include/utils/Flattenable.h
+++ b/libutils/include/utils/Flattenable.h
@@ -52,7 +52,12 @@
 
     template<size_t N>
     static size_t align(void*& buffer) {
-        return align<N>( const_cast<void const*&>(buffer) );
+        static_assert(!(N & (N - 1)), "Can only align to a power of 2.");
+        void* b = buffer;
+        buffer = reinterpret_cast<void*>((uintptr_t(buffer) + (N-1)) & ~(N-1));
+        size_t delta = size_t(uintptr_t(buffer) - uintptr_t(b));
+        memset(b, 0, delta);
+        return delta;
     }
 
     static void advance(void*& buffer, size_t& size, size_t offset) {
diff --git a/libutils/include/utils/String16.h b/libutils/include/utils/String16.h
index adc3e7d..c0e3f1e 100644
--- a/libutils/include/utils/String16.h
+++ b/libutils/include/utils/String16.h
@@ -17,7 +17,8 @@
 #ifndef ANDROID_STRING16_H
 #define ANDROID_STRING16_H
 
-#include <string> // for std::string
+#include <iostream>
+#include <string>
 
 #include <utils/Errors.h>
 #include <utils/String8.h>
@@ -25,18 +26,10 @@
 
 // ---------------------------------------------------------------------------
 
-extern "C" {
-
-}
-
-// ---------------------------------------------------------------------------
-
 namespace android {
 
 // ---------------------------------------------------------------------------
 
-class String8;
-
 template <size_t N>
 class StaticString16;
 
@@ -203,6 +196,11 @@
 // require any change to the underlying SharedBuffer contents or reference count.
 ANDROID_TRIVIAL_MOVE_TRAIT(String16)
 
+static inline std::ostream& operator<<(std::ostream& os, const String16& str) {
+    os << String8(str).c_str();
+    return os;
+}
+
 // ---------------------------------------------------------------------------
 
 /*
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
index 100e507..6f4fb47 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -27,43 +27,6 @@
 
 // ---------------------------------------------------------------------------
 
-// TODO: Maybe remove sp<> ? wp<> comparison? These are dangerous: If the wp<>
-// was created before the sp<>, and they point to different objects, they may
-// compare equal even if they are entirely unrelated. E.g. CameraService
-// currently performa such comparisons.
-
-#define COMPARE_STRONG(_op_)                                           \
-template<typename U>                                            \
-inline bool operator _op_ (const sp<U>& o) const {              \
-    return m_ptr _op_ o.m_ptr;                                  \
-}                                                               \
-template<typename U>                                            \
-inline bool operator _op_ (const U* o) const {                  \
-    return m_ptr _op_ o;                                        \
-}                                                               \
-/* Needed to handle type inference for nullptr: */              \
-inline bool operator _op_ (const T* o) const {                  \
-    return m_ptr _op_ o;                                        \
-}
-
-template<template<typename C> class comparator, typename T, typename U>
-static inline bool _sp_compare_(T* a, U* b) {
-    return comparator<typename std::common_type<T*, U*>::type>()(a, b);
-}
-
-// Use std::less and friends to avoid undefined behavior when ordering pointers
-// to different objects.
-#define COMPARE_STRONG_FUNCTIONAL(_op_, _compare_)               \
-template<typename U>                                             \
-inline bool operator _op_ (const sp<U>& o) const {               \
-    return _sp_compare_<_compare_>(m_ptr, o.m_ptr);              \
-}                                                                \
-template<typename U>                                             \
-inline bool operator _op_ (const U* o) const {                   \
-    return _sp_compare_<_compare_>(m_ptr, o);                    \
-}
-// ---------------------------------------------------------------------------
-
 template<typename T>
 class sp {
 public:
@@ -102,15 +65,6 @@
     inline T*       get() const            { return m_ptr; }
     inline explicit operator bool () const { return m_ptr != nullptr; }
 
-    // Operators
-
-    COMPARE_STRONG(==)
-    COMPARE_STRONG(!=)
-    COMPARE_STRONG_FUNCTIONAL(>, std::greater)
-    COMPARE_STRONG_FUNCTIONAL(<, std::less)
-    COMPARE_STRONG_FUNCTIONAL(<=, std::less_equal)
-    COMPARE_STRONG_FUNCTIONAL(>=, std::greater_equal)
-
     // Punt these to the wp<> implementation.
     template<typename U>
     inline bool operator == (const wp<U>& o) const {
@@ -130,13 +84,69 @@
     T* m_ptr;
 };
 
-// For code size reasons, we do not want these inlined or templated.
-void sp_report_race();
-void sp_report_stack_pointer();
+#define COMPARE_STRONG(_op_)                                           \
+    template <typename T, typename U>                                  \
+    static inline bool operator _op_(const sp<T>& t, const sp<U>& u) { \
+        return t.get() _op_ u.get();                                   \
+    }                                                                  \
+    template <typename T, typename U>                                  \
+    static inline bool operator _op_(const T* t, const sp<U>& u) {     \
+        return t _op_ u.get();                                         \
+    }                                                                  \
+    template <typename T, typename U>                                  \
+    static inline bool operator _op_(const sp<T>& t, const U* u) {     \
+        return t.get() _op_ u;                                         \
+    }                                                                  \
+    template <typename T>                                              \
+    static inline bool operator _op_(const sp<T>& t, std::nullptr_t) { \
+        return t.get() _op_ nullptr;                                   \
+    }                                                                  \
+    template <typename T>                                              \
+    static inline bool operator _op_(std::nullptr_t, const sp<T>& t) { \
+        return nullptr _op_ t.get();                                   \
+    }
+
+template <template <typename C> class comparator, typename T, typename U>
+static inline bool _sp_compare_(T* a, U* b) {
+    return comparator<typename std::common_type<T*, U*>::type>()(a, b);
+}
+
+#define COMPARE_STRONG_FUNCTIONAL(_op_, _compare_)                     \
+    template <typename T, typename U>                                  \
+    static inline bool operator _op_(const sp<T>& t, const sp<U>& u) { \
+        return _sp_compare_<_compare_>(t.get(), u.get());              \
+    }                                                                  \
+    template <typename T, typename U>                                  \
+    static inline bool operator _op_(const T* t, const sp<U>& u) {     \
+        return _sp_compare_<_compare_>(t, u.get());                    \
+    }                                                                  \
+    template <typename T, typename U>                                  \
+    static inline bool operator _op_(const sp<T>& t, const U* u) {     \
+        return _sp_compare_<_compare_>(t.get(), u);                    \
+    }                                                                  \
+    template <typename T>                                              \
+    static inline bool operator _op_(const sp<T>& t, std::nullptr_t) { \
+        return _sp_compare_<_compare_>(t.get(), nullptr);              \
+    }                                                                  \
+    template <typename T>                                              \
+    static inline bool operator _op_(std::nullptr_t, const sp<T>& t) { \
+        return _sp_compare_<_compare_>(nullptr, t.get());              \
+    }
+
+COMPARE_STRONG(==)
+COMPARE_STRONG(!=)
+COMPARE_STRONG_FUNCTIONAL(>, std::greater)
+COMPARE_STRONG_FUNCTIONAL(<, std::less)
+COMPARE_STRONG_FUNCTIONAL(<=, std::less_equal)
+COMPARE_STRONG_FUNCTIONAL(>=, std::greater_equal)
 
 #undef COMPARE_STRONG
 #undef COMPARE_STRONG_FUNCTIONAL
 
+// For code size reasons, we do not want these inlined or templated.
+void sp_report_race();
+void sp_report_stack_pointer();
+
 // ---------------------------------------------------------------------------
 // No user serviceable parts below here.
 
diff --git a/libvndksupport/Android.bp b/libvndksupport/Android.bp
index f4544a1..b92c76c 100644
--- a/libvndksupport/Android.bp
+++ b/libvndksupport/Android.bp
@@ -1,5 +1,3 @@
-subdirs = ["tests"]
-
 cc_library {
     name: "libvndksupport",
     native_bridge_supported: true,
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index b26ad4d..1c3acb8 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -304,10 +304,13 @@
     bool cmdlineValid;             // cmdline has been cached
     bool updated;                  // cleared before monitoring pass.
     bool killed;                   // sent a kill to this thread, next panic...
+    bool frozen;                   // process is in frozen cgroup.
 
     void setComm(const char* _comm) { strncpy(comm + 1, _comm, sizeof(comm) - 2); }
 
-    proc(pid_t tid, pid_t pid, pid_t ppid, const char* _comm, int time, char state)
+    void setFrozen(bool _frozen) { frozen = _frozen; }
+
+    proc(pid_t tid, pid_t pid, pid_t ppid, const char* _comm, int time, char state, bool frozen)
         : tid(tid),
           schedUpdate(0),
           nrSwitches(0),
@@ -327,7 +330,8 @@
           exeMissingValid(false),
           cmdlineValid(false),
           updated(true),
-          killed(!llkTestWithKill) {
+          killed(!llkTestWithKill),
+          frozen(frozen) {
         memset(comm, '\0', sizeof(comm));
         setComm(_comm);
     }
@@ -373,6 +377,8 @@
         return uid;
     }
 
+    bool isFrozen() { return frozen; }
+
     void reset(void) {  // reset cache, if we detected pid rollover
         uid = -1;
         state = '?';
@@ -592,8 +598,9 @@
     tids.erase(tid);
 }
 
-proc* llkTidAlloc(pid_t tid, pid_t pid, pid_t ppid, const char* comm, int time, char state) {
-    auto it = tids.emplace(std::make_pair(tid, proc(tid, pid, ppid, comm, time, state)));
+proc* llkTidAlloc(pid_t tid, pid_t pid, pid_t ppid, const char* comm, int time, char state,
+                  bool frozen) {
+    auto it = tids.emplace(std::make_pair(tid, proc(tid, pid, ppid, comm, time, state, frozen)));
     return &it.first->second;
 }
 
@@ -1039,12 +1046,18 @@
                 continue;
             }
 
+            // Get the process cgroup
+            auto cgroup = ReadFile(piddir + "/cgroup");
+            auto frozen = cgroup.find(":freezer:/frozen") != std::string::npos;
+
             auto procp = llkTidLookup(tid);
             if (procp == nullptr) {
-                procp = llkTidAlloc(tid, pid, ppid, pdir, utime + stime, state);
+                procp = llkTidAlloc(tid, pid, ppid, pdir, utime + stime, state, frozen);
             } else {
                 // comm can change ...
                 procp->setComm(pdir);
+                // frozen can change, too...
+                procp->setFrozen(frozen);
                 procp->updated = true;
                 // pid/ppid/tid wrap?
                 if (((procp->update != prevUpdate) && (procp->update != llkUpdate)) ||
@@ -1084,6 +1097,9 @@
             if ((tid == myTid) || llkSkipPid(tid)) {
                 continue;
             }
+            if (procp->isFrozen()) {
+                break;
+            }
             if (llkSkipPpid(ppid)) {
                 break;
             }
@@ -1101,7 +1117,7 @@
 
             auto pprocp = llkTidLookup(ppid);
             if (pprocp == nullptr) {
-                pprocp = llkTidAlloc(ppid, ppid, 0, "", 0, '?');
+                pprocp = llkTidAlloc(ppid, ppid, 0, "", 0, '?', false);
             }
             if (pprocp) {
                 if (llkSkipPproc(pprocp, procp)) break;
diff --git a/llkd/llkd-debuggable.rc b/llkd/llkd-debuggable.rc
index 724cb5e..4b11b1c 100644
--- a/llkd/llkd-debuggable.rc
+++ b/llkd/llkd-debuggable.rc
@@ -13,7 +13,7 @@
     disabled
     user llkd
     group llkd readproc
-    capabilities KILL IPC_LOCK SYS_PTRACE DAC_OVERRIDE
+    capabilities KILL IPC_LOCK SYS_PTRACE DAC_OVERRIDE SYS_ADMIN
     file /dev/kmsg w
     file /proc/sysrq-trigger w
     writepid /dev/cpuset/system-background/tasks
diff --git a/llkd/tests/llkd_test.cpp b/llkd/tests/llkd_test.cpp
index 96079cc..475512c 100644
--- a/llkd/tests/llkd_test.cpp
+++ b/llkd/tests/llkd_test.cpp
@@ -89,7 +89,8 @@
         rest();
         std::string setprop("setprop ");
         // Manually check that SyS_openat is _added_ to the list when restarted
-        execute((setprop + LLK_CHECK_STACK_PROPERTY + " ,SyS_openat").c_str());
+        // 4.19+ kernels report __arm64_sys_openat b/147486902
+        execute((setprop + LLK_CHECK_STACK_PROPERTY + " ,SyS_openat,__arm64_sys_openat").c_str());
         rest();
         execute((setprop + LLK_ENABLE_WRITEABLE_PROPERTY + " false").c_str());
         rest();
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 7b18438..76a970f 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -110,7 +110,7 @@
 #endif
 
 static int openLogFile(const char* pathname, size_t sizeKB) {
-    int fd = open(pathname, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
+    int fd = open(pathname, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP);
     if (fd < 0) {
         return fd;
     }
@@ -1129,7 +1129,7 @@
         if (!ret) {
             error(EXIT_FAILURE, 0, R"init(Unexpected EOF!
 
-This means that either logd crashed, or more likely, this instance of logcat was unable to read log
+This means that either the device shut down, logd crashed, or this instance of logcat was unable to read log
 messages as quickly as they were being produced.
 
 If you have enabled significant logging, look into using the -G option to increase log buffer sizes.)init");
diff --git a/logcat/logcatd b/logcat/logcatd
index 622e567..5a1415d 100755
--- a/logcat/logcatd
+++ b/logcat/logcatd
@@ -4,6 +4,10 @@
 # first reads the 'last' logcat to persistent storage with `-L` then run logcat again without
 # `-L` to read the current logcat buffers to persistent storage.
 
+# init sets the umask to 077 for forked processes. logpersist needs to create files that are group
+# readable. So relax the umask to only disallow group wx and world rwx.
+umask 037
+
 has_last="false"
 for arg in "$@"; do
   if [ "$arg" == "-L" -o "$arg" == "--last" ]; then
diff --git a/logcat/logcatd.rc b/logcat/logcatd.rc
index e986184..64d5500 100644
--- a/logcat/logcatd.rc
+++ b/logcat/logcatd.rc
@@ -30,8 +30,8 @@
     setprop logd.logpersistd.enable true
 
 on property:logd.logpersistd.enable=true && property:logd.logpersistd=logcatd
-    # all exec/services are called with umask(077), so no gain beyond 0700
-    mkdir /data/misc/logd 0700 logd log
+    # log group should be able to read persisted logs
+    mkdir /data/misc/logd 0750 logd log
     start logcatd
 
 # stop logcatd service and clear data
diff --git a/logd/README.property b/logd/README.property
index d2a2cbb..1b7e165 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -6,7 +6,7 @@
 ro.logd.auditd.main        bool   true   selinux audit messages sent to main.
 ro.logd.auditd.events      bool   true   selinux audit messages sent to events.
 persist.logd.security      bool   false  Enable security buffer.
-ro.device_owner            bool   false  Override persist.logd.security to false
+ro.organization_owned      bool   false  Override persist.logd.security to false
 ro.logd.kernel             bool+ svelte+ Enable klogd daemon
 ro.logd.statistics         bool+ svelte+ Enable logcat -S statistics.
 ro.debuggable              number        if not "1", logd.statistics &
diff --git a/logd/logtagd.rc b/logd/logtagd.rc
index 46aa8c1..248a78c 100644
--- a/logd/logtagd.rc
+++ b/logd/logtagd.rc
@@ -2,7 +2,7 @@
 # logtagd event log tag service (debug only)
 #
 on post-fs-data
-    mkdir /data/misc/logd 0700 logd log
+    mkdir /data/misc/logd 0750 logd log
     write /data/misc/logd/event-log-tags ""
     chown logd log /data/misc/logd/event-log-tags
     chmod 0600 /data/misc/logd/event-log-tags
diff --git a/logd/tests/AndroidTest.xml b/logd/tests/AndroidTest.xml
index 9a18edb..a25dc44 100644
--- a/logd/tests/AndroidTest.xml
+++ b/logd/tests/AndroidTest.xml
@@ -18,6 +18,7 @@
     <option name="config-descriptor:metadata" key="component" value="systems" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
         <option name="push" value="CtsLogdTestCases->/data/local/tmp/CtsLogdTestCases" />
diff --git a/property_service/Android.bp b/property_service/Android.bp
deleted file mode 100644
index b44c296..0000000
--- a/property_service/Android.bp
+++ /dev/null
@@ -1 +0,0 @@
-subdirs = ["*"]
diff --git a/property_service/libpropertyinfoparser/Android.bp b/property_service/libpropertyinfoparser/Android.bp
index ac802b5..108d15a 100644
--- a/property_service/libpropertyinfoparser/Android.bp
+++ b/property_service/libpropertyinfoparser/Android.bp
@@ -2,6 +2,7 @@
     name: "libpropertyinfoparser",
     host_supported: true,
     vendor_available: true,
+    ramdisk_available: true,
     recovery_available: true,
     native_bridge_supported: true,
     srcs: ["property_info_parser.cpp"],
diff --git a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
index f4a846f..a643062 100644
--- a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
+++ b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
@@ -317,7 +317,7 @@
       {"ro.boot.bootreason", "u:object_r:bootloader_boot_reason_prop:s0", "string", false},
       {"persist.sys.boot.reason", "u:object_r:last_boot_reason_prop:s0", "string", false},
       {"sys.boot.reason", "u:object_r:system_boot_reason_prop:s0", "string", false},
-      {"ro.device_owner", "u:object_r:device_logging_prop:s0", "string", false},
+      {"ro.organization_owned", "u:object_r:device_logging_prop:s0", "string", false},
 
       {"selinux.restorecon_recursive", "u:object_r:restorecon_prop:s0", "string", false},
 
@@ -669,7 +669,7 @@
       {"ro.crypto.type", "u:object_r:vold_prop:s0"},
       {"ro.dalvik.vm.native.bridge", "u:object_r:dalvik_prop:s0"},
       {"ro.debuggable", "u:object_r:default_prop:s0"},
-      {"ro.device_owner", "u:object_r:device_logging_prop:s0"},
+      {"ro.organization_owned", "u:object_r:device_logging_prop:s0"},
       {"ro.expect.recovery_id", "u:object_r:default_prop:s0"},
       {"ro.frp.pst", "u:object_r:default_prop:s0"},
       {"ro.hardware", "u:object_r:default_prop:s0"},
diff --git a/property_service/property_info_checker/Android.bp b/property_service/property_info_checker/Android.bp
index 7d66199..65e660a 100644
--- a/property_service/property_info_checker/Android.bp
+++ b/property_service/property_info_checker/Android.bp
@@ -7,6 +7,7 @@
         "libpropertyinfoserializer",
         "libpropertyinfoparser",
         "libbase",
+        "liblog",
         "libsepol",
     ],
     srcs: ["property_info_checker.cpp"],
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 5821379..a9d0ed0 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -67,6 +67,11 @@
   EXPORT_GLOBAL_GCOV_OPTIONS := export GCOV_PREFIX /data/misc/trace
 endif
 
+EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS :=
+ifeq ($(CLANG_COVERAGE),true)
+  EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS := export LLVM_PROFILE_FILE /data/misc/trace/clang-%p-%m.profraw
+endif
+
 # Put it here instead of in init.rc module definition,
 # because init.rc is conditionally included.
 #
@@ -147,6 +152,7 @@
 	$(hide) sed -i -e 's?%SYSTEMSERVERCLASSPATH%?$(PRODUCT_SYSTEM_SERVER_CLASSPATH)?g' $@
 	$(hide) sed -i -e 's?%EXPORT_GLOBAL_ASAN_OPTIONS%?$(EXPORT_GLOBAL_ASAN_OPTIONS)?g' $@
 	$(hide) sed -i -e 's?%EXPORT_GLOBAL_GCOV_OPTIONS%?$(EXPORT_GLOBAL_GCOV_OPTIONS)?g' $@
+	$(hide) sed -i -e 's?%EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS%?$(EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS)?g' $@
 	$(hide) sed -i -e 's?%EXPORT_GLOBAL_HWASAN_OPTIONS%?$(EXPORT_GLOBAL_HWASAN_OPTIONS)?g' $@
 
 # Append PLATFORM_VNDK_VERSION to base name.
diff --git a/rootdir/avb/Android.mk b/rootdir/avb/Android.mk
index 80573fb..f96ffdd 100644
--- a/rootdir/avb/Android.mk
+++ b/rootdir/avb/Android.mk
@@ -46,6 +46,21 @@
 include $(BUILD_PREBUILT)
 
 #######################################
+# r-developer-gsi.avbpubkey
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := r-developer-gsi.avbpubkey
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
+else
+LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)/avb
+endif
+
+include $(BUILD_PREBUILT)
+
+#######################################
 # s-gsi.avbpubkey
 include $(CLEAR_VARS)
 
@@ -59,3 +74,18 @@
 endif
 
 include $(BUILD_PREBUILT)
+
+#######################################
+# s-developer-gsi.avbpubkey
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := s-developer-gsi.avbpubkey
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
+else
+LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)/avb
+endif
+
+include $(BUILD_PREBUILT)
diff --git a/rootdir/avb/r-developer-gsi.avbpubkey b/rootdir/avb/r-developer-gsi.avbpubkey
new file mode 100644
index 0000000..aac39cc
--- /dev/null
+++ b/rootdir/avb/r-developer-gsi.avbpubkey
Binary files differ
diff --git a/rootdir/avb/s-developer-gsi.avbpubkey b/rootdir/avb/s-developer-gsi.avbpubkey
new file mode 100644
index 0000000..f0a6c11
--- /dev/null
+++ b/rootdir/avb/s-developer-gsi.avbpubkey
Binary files differ
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index 50005d9..fdaaf1a 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -15,4 +15,5 @@
     export SYSTEMSERVERCLASSPATH %SYSTEMSERVERCLASSPATH%
     %EXPORT_GLOBAL_ASAN_OPTIONS%
     %EXPORT_GLOBAL_GCOV_OPTIONS%
+    %EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS%
     %EXPORT_GLOBAL_HWASAN_OPTIONS%
diff --git a/rootdir/init.rc b/rootdir/init.rc
index fdfcde8..91dd7a5 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -32,6 +32,10 @@
     # cgroup for system_server and surfaceflinger
     mkdir /dev/memcg/system 0550 system system
 
+    # symlink the Android specific /dev/tun to Linux expected /dev/net/tun
+    mkdir /dev/net 0755 root root
+    symlink ../tun /dev/net/tun
+
     # set RLIMIT_NICE to allow priorities from 19 to -20
     setrlimit nice 40 40
 
@@ -42,8 +46,12 @@
     mkdir /linkerconfig/bootstrap 0755
     mkdir /linkerconfig/default 0755
 
+    # Disable dm-verity hash prefetching, since it doesn't help performance
+    # Read more in b/136247322
+    write /sys/module/dm_verity/parameters/prefetch_cluster 0
+
     # Generate ld.config.txt for early executed processes
-    exec -- /system/bin/linkerconfig --target /linkerconfig/bootstrap/ld.config.txt
+    exec -- /system/bin/linkerconfig --target /linkerconfig/bootstrap
     chmod 644 /linkerconfig/bootstrap/ld.config.txt
     copy /linkerconfig/bootstrap/ld.config.txt /linkerconfig/default/ld.config.txt
     chmod 644 /linkerconfig/default/ld.config.txt
@@ -106,15 +114,6 @@
     symlink /proc/self/fd/1 /dev/stdout
     symlink /proc/self/fd/2 /dev/stderr
 
-    symlink /system/bin /bin
-    symlink /system/etc /etc
-
-    # Backward compatibility.
-    symlink /sys/kernel/debug /d
-
-    # Link /vendor to /system/vendor for devices without a vendor partition.
-    symlink /system/vendor /vendor
-
     # Create energy-aware scheduler tuning nodes
     mkdir /dev/stune/foreground
     mkdir /dev/stune/background
@@ -136,6 +135,14 @@
     chmod 0664 /dev/stune/top-app/tasks
     chmod 0664 /dev/stune/rt/tasks
 
+    # Create an stune group for NNAPI HAL processes
+    mkdir /dev/stune/nnapi-hal
+    chown system system /dev/stune/nnapi-hal
+    chown system system /dev/stune/nnapi-hal/tasks
+    chmod 0664 /dev/stune/nnapi-hal/tasks
+    write /dev/stune/nnapi-hal/schedtune.boost 1
+    write /dev/stune/nnapi-hal/schedtune.prefer_idle 1
+
     # Create blkio group and apply initial settings.
     # This feature needs kernel to support it, and the
     # device's init.rc must actually set the correct values.
@@ -157,11 +164,24 @@
     chmod 0770 /config/sdcardfs
     chown system package_info /config/sdcardfs
 
+    # Mount binderfs
+    mkdir /dev/binderfs
+    mount binder binder /dev/binderfs stats=global
+    chmod 0755 /dev/binderfs
+
+    symlink /dev/binderfs/binder /dev/binder
+    symlink /dev/binderfs/hwbinder /dev/hwbinder
+    symlink /dev/binderfs/vndbinder /dev/vndbinder
+
+    chmod 0666 /dev/binderfs/hwbinder
+    chmod 0666 /dev/binderfs/binder
+    chmod 0666 /dev/binderfs/vndbinder
+
     mkdir /mnt/secure 0700 root root
     mkdir /mnt/secure/asec 0700 root root
     mkdir /mnt/asec 0755 root system
     mkdir /mnt/obb 0755 root system
-    mkdir /mnt/media_rw 0750 root media_rw
+    mkdir /mnt/media_rw 0750 root external_storage
     mkdir /mnt/user 0755 root root
     mkdir /mnt/user/0 0755 root root
     mkdir /mnt/user/0/self 0755 root root
@@ -169,11 +189,11 @@
     mkdir /mnt/user/0/emulated/0 0755 root root
 
     # Prepare directories for pass through processes
-    mkdir /mnt/pass_through 0755 root root
-    mkdir /mnt/pass_through/0 0755 root root
-    mkdir /mnt/pass_through/0/self 0755 root root
-    mkdir /mnt/pass_through/0/emulated 0755 root root
-    mkdir /mnt/pass_through/0/emulated/0 0755 root root
+    mkdir /mnt/pass_through 0700 root root
+    mkdir /mnt/pass_through/0 0710 root media_rw
+    mkdir /mnt/pass_through/0/self 0710 root media_rw
+    mkdir /mnt/pass_through/0/emulated 0710 root media_rw
+    mkdir /mnt/pass_through/0/emulated/0 0710 root media_rw
 
     mkdir /mnt/expand 0771 system system
     mkdir /mnt/appfuse 0711 root root
@@ -190,7 +210,6 @@
     mkdir /mnt/runtime/full/self 0755 root root
 
     # Symlink to keep legacy apps working in multi-user world
-    symlink /storage/self/primary /sdcard
     symlink /storage/self/primary /mnt/sdcard
     symlink /mnt/user/0/primary /mnt/runtime/default/self/primary
 
@@ -367,6 +386,10 @@
     # Start logd before any other services run to ensure we capture all of their logs.
     start logd
     # Start lmkd before any other services run so that it can register them
+    chown root system /sys/module/lowmemorykiller/parameters/adj
+    chmod 0664 /sys/module/lowmemorykiller/parameters/adj
+    chown root system /sys/module/lowmemorykiller/parameters/minfree
+    chmod 0664 /sys/module/lowmemorykiller/parameters/minfree
     start lmkd
 
     # Start essential services.
@@ -477,6 +500,7 @@
     mkdir /metadata/vold
     chmod 0700 /metadata/vold
     mkdir /metadata/password_slots 0771 root system
+    mkdir /metadata/bootstat 0750 system log
     mkdir /metadata/ota 0700 root system
     mkdir /metadata/ota/snapshots 0700 root system
 
@@ -485,6 +509,7 @@
 on late-fs
     # Ensure that tracefs has the correct permissions.
     # This does not work correctly if it is called in post-fs.
+    chmod 0755 /sys/kernel/tracing
     chmod 0755 /sys/kernel/debug/tracing
 
     # HALs required before storage encryption can get unlocked (FBE/FDE)
@@ -547,6 +572,7 @@
     chown bluetooth bluetooth /data/misc/bluedroid/bt_config.conf
     mkdir /data/misc/bluetooth 0770 bluetooth bluetooth
     mkdir /data/misc/bluetooth/logs 0770 bluetooth bluetooth
+    mkdir /data/misc/credstore 0700 credstore credstore
     mkdir /data/misc/keystore 0700 keystore keystore
     mkdir /data/misc/gatekeeper 0700 system system
     mkdir /data/misc/keychain 0771 system system
@@ -584,12 +610,15 @@
     # profile file layout
     mkdir /data/misc/profiles 0771 system system
     mkdir /data/misc/profiles/cur 0771 system system
-    mkdir /data/misc/profiles/ref 0771 system system
+    mkdir /data/misc/profiles/ref 0770 system system
     mkdir /data/misc/profman 0770 system shell
     mkdir /data/misc/gcov 0770 root root
     mkdir /data/misc/installd 0700 root root
-    mkdir /data/misc/apexdata 0700 root root
+    mkdir /data/misc/apexdata 0711 root root
     mkdir /data/misc/apexrollback 0700 root root
+    mkdir /data/misc/snapshotctl_log 0755 root root
+    # create location to store pre-reboot information
+    mkdir /data/misc/prereboot 0700 system system
 
     mkdir /data/preloads 0775 system system encryption=None
 
@@ -670,10 +699,6 @@
     mkdir /data/user/0 0700 system system encryption=None
     mount none /data/data /data/user/0 bind rec
 
-    # Special-case /data/media/obb per b/64566063
-    mkdir /data/media 0770 media_rw media_rw encryption=None
-    mkdir /data/media/obb 0770 media_rw media_rw encryption=Attempt
-
     # A tmpfs directory, which will contain all apps CE DE data directory that
     # bind mount from the original source.
     chown root root /data_mirror
@@ -691,6 +716,10 @@
     mount none /data/user /data_mirror/data_ce/null bind rec
     mount none /data/user_de /data_mirror/data_de/null bind rec
 
+    # Create mirror directory for jit profiles
+    mkdir /data_mirror/cur_profiles 0700 root root
+    mount none /data/misc/profiles/cur /data_mirror/cur_profiles bind rec
+
     mkdir /data/cache 0770 system cache encryption=Require
     mkdir /data/cache/recovery 0770 system cache
     mkdir /data/cache/backup_stage 0700 system system
@@ -701,16 +730,27 @@
     mkdir /data/rollback-observer 0700 system system encryption=DeleteIfNecessary
 
     # Create root dir for Incremental Service
-    mkdir /data/incremental 0770 system system encryption=None
+    mkdir /data/incremental 0771 system system encryption=Require
 
     # Wait for apexd to finish activating APEXes before starting more processes.
     wait_for_prop apexd.status ready
     perform_apex_config
 
+    # Special-case /data/media/obb per b/64566063
+    mkdir /data/media 0770 media_rw media_rw encryption=None
+    exec - media_rw media_rw -- /system/bin/chattr +F /data/media
+    mkdir /data/media/obb 0770 media_rw media_rw encryption=Attempt
+
     exec_start derive_sdk
 
     init_user0
 
+    # Allow apexd to snapshot and restore device encrypted apex data in the case
+    # of a rollback. This should be done immediately after DE_user data keys
+    # are loaded. APEXes should not access this data until this has been
+    # completed.
+    exec_start apexd-snapshotde
+
     # Set SELinux security contexts on upgrade or policy update.
     restorecon --recursive --skip-ce /data
 
@@ -729,6 +769,9 @@
     # IOCTLs on ashmem fds any more.
     setprop sys.use_memfd false
 
+    # Explicitly disable FUSE
+    setprop persist.sys.fuse false
+
     # Set fscklog permission
     chown root system /dev/fscklogs/log
     chmod 0770 /dev/fscklogs/log
@@ -786,10 +829,6 @@
     # parameters to match how it is managing things.
     write /proc/sys/vm/overcommit_memory 1
     write /proc/sys/vm/min_free_order_shift 4
-    chown root system /sys/module/lowmemorykiller/parameters/adj
-    chmod 0664 /sys/module/lowmemorykiller/parameters/adj
-    chown root system /sys/module/lowmemorykiller/parameters/minfree
-    chmod 0664 /sys/module/lowmemorykiller/parameters/minfree
 
     # System server manages zram writeback
     chown root system /sys/block/zram0/idle
@@ -936,14 +975,33 @@
 on property:sys.sysctl.tcp_def_init_rwnd=*
     write /proc/sys/net/ipv4/tcp_default_init_rwnd ${sys.sysctl.tcp_def_init_rwnd}
 
-on property:security.perf_harden=0
+# perf_event_open syscall security:
+# Newer kernels have the ability to control the use of the syscall via SELinux
+# hooks. init tests for this, and sets sys_init.perf_lsm_hooks to 1 if the
+# kernel has the hooks. In this case, the system-wide perf_event_paranoid
+# sysctl is set to -1 (unrestricted use), and the SELinux policy is used for
+# controlling access. On older kernels, the paranoid value is the only means of
+# controlling access. It is normally 3 (allow only root), but the shell user
+# can lower it to 1 (allowing thread-scoped pofiling) via security.perf_harden.
+on property:sys.init.perf_lsm_hooks=1
+    write /proc/sys/kernel/perf_event_paranoid -1
+on property:security.perf_harden=0 && property:sys.init.perf_lsm_hooks=""
     write /proc/sys/kernel/perf_event_paranoid 1
+on property:security.perf_harden=1 && property:sys.init.perf_lsm_hooks=""
+    write /proc/sys/kernel/perf_event_paranoid 3
+
+# Additionally, simpleperf profiler uses debug.* and security.perf_harden
+# sysprops to be able to indirectly set these sysctls.
+on property:security.perf_harden=0
     write /proc/sys/kernel/perf_event_max_sample_rate ${debug.perf_event_max_sample_rate:-100000}
     write /proc/sys/kernel/perf_cpu_time_max_percent ${debug.perf_cpu_time_max_percent:-25}
     write /proc/sys/kernel/perf_event_mlock_kb ${debug.perf_event_mlock_kb:-516}
-
+# Default values.
 on property:security.perf_harden=1
-    write /proc/sys/kernel/perf_event_paranoid 3
+    write /proc/sys/kernel/perf_event_max_sample_rate 100000
+    write /proc/sys/kernel/perf_cpu_time_max_percent 25
+    write /proc/sys/kernel/perf_event_mlock_kb 516
+
 
 # on shutdown
 # In device's init.rc, this trigger can be used to do device-specific actions
@@ -979,10 +1037,13 @@
 on userspace-reboot-requested
   # TODO(b/135984674): reset all necessary properties here.
   setprop sys.boot_completed ""
+  setprop dev.bootcomplete ""
   setprop sys.init.updatable_crashing ""
   setprop sys.init.updatable_crashing_process_name ""
   setprop apexd.status ""
   setprop sys.user.0.ce_available ""
+  setprop sys.shutdown.requested ""
+  setprop service.bootanim.exit ""
 
 on userspace-reboot-fs-remount
   # Make sure that vold is running.
@@ -992,6 +1053,7 @@
   exec - system system -- /system/bin/vdc checkpoint resetCheckpoint
   exec - system system -- /system/bin/vdc checkpoint markBootAttempt
   remount_userdata
+  start bootanim
 
 on userspace-reboot-resume
   trigger userspace-reboot-fs-remount
@@ -1001,4 +1063,4 @@
   trigger boot
 
 on property:sys.boot_completed=1 && property:sys.init.userspace_reboot.in_progress=1
-  finish_userspace_reboot
+  setprop sys.init.userspace_reboot.in_progress ""
diff --git a/rootdir/init.usb.rc b/rootdir/init.usb.rc
index 02d34ba..27b05ec 100644
--- a/rootdir/init.usb.rc
+++ b/rootdir/init.usb.rc
@@ -138,5 +138,4 @@
 
 on userspace-reboot-requested
   setprop sys.usb.config ""
-  setprop sys.usb.configfs ""
   setprop sys.usb.state ""
diff --git a/set-verity-state/Android.bp b/set-verity-state/Android.bp
index cd8c8c5..da112c9 100644
--- a/set-verity-state/Android.bp
+++ b/set-verity-state/Android.bp
@@ -9,8 +9,9 @@
         "libcrypto_utils",
         "libcutils",
         "libfec",
-        "libfs_mgr",
+        "libfs_mgr_binder",
         "liblog",
+        "libutils",
     ],
     static_libs: [
         "libavb_user",
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
index d391cc1..3bee875 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -218,25 +218,28 @@
 
 bzip2: bzcat bzip2 bunzip2
 
+gavinhoward/bc: bc
+
 one-true-awk: awk
 
 toolbox: getevent getprop setprop start stop
 
-toybox: acpi base64 basename bc blkid blockdev cal cat chattr chcon chgrp
-chmod chown chroot chrt cksum clear cmp comm cp cpio cut date dd df
-diff dirname dmesg dos2unix du echo egrep env expand expr fallocate
-false fgrep file find flock fmt free freeramdisk fsfreeze getconf
-getenforce getfattr grep groups gunzip gzip head help hostname hwclock
-i2cdetect i2cdump i2cget i2cset iconv id ifconfig inotifyd insmod
-install ionice iorenice iotop kill killall ln load\_policy log logname
-losetup ls lsattr lsmod lsof lspci lsusb makedevs md5sum microcom
+toybox: acpi base64 basename blkid blockdev cal cat chattr chcon chgrp chmod
+chown chroot chrt cksum clear cmp comm cp cpio cut date dd devmem
+df diff dirname dmesg dos2unix du echo egrep env expand expr fallocate
+false fgrep file find flock fmt free freeramdisk fsfreeze fsync getconf
+getenforce getfattr getopt grep groups gunzip gzip head help hostname
+hwclock i2cdetect i2cdump i2cget i2cset iconv id ifconfig inotifyd
+insmod install ionice iorenice iotop kill killall ln load\_policy log
+logname losetup ls lsattr lsmod lsof lspci lsusb makedevs md5sum microcom
 mkdir mkfifo mknod mkswap mktemp modinfo modprobe more mount mountpoint
 mv nbd-client nc netcat netstat nice nl nohup nproc nsenter od partprobe
 paste patch pgrep pidof ping ping6 pivot\_root pkill pmap printenv
-printf prlimit ps pwd pwdx readlink realpath renice restorecon rev
-rfkill rm rmdir rmmod runcon sed sendevent seq setenforce setfattr
-setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep sort split
-stat strings stty swapoff swapon sync sysctl tac tail tar taskset tee
-time timeout top touch tr traceroute traceroute6 true truncate tty tunctl
-ulimit umount uname uniq unix2dos unlink unshare uptime usleep uudecode
-uuencode uuidgen vconfig vmstat watch wc which whoami xargs xxd yes zcat
+printf prlimit ps pwd pwdx readelf readlink realpath renice restorecon
+rev rfkill rm rmdir rmmod runcon sed sendevent seq setenforce setfattr
+setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep sort
+split stat strings stty swapoff swapon sync sysctl tac tail tar taskset
+tee time timeout top touch tr traceroute traceroute6 true truncate
+tty tunctl ulimit umount uname uniq unix2dos unlink unshare uptime
+usleep uudecode uuencode uuidgen vconfig vi vmstat watch wc which
+whoami xargs xxd yes zcat
diff --git a/storaged/main.cpp b/storaged/main.cpp
index 3817fb5..a7bda14 100644
--- a/storaged/main.cpp
+++ b/storaged/main.cpp
@@ -46,12 +46,7 @@
 
 // Function of storaged's main thread
 void* storaged_main(void* /* unused */) {
-    storaged_sp = new storaged_t();
-
-    storaged_sp->init();
-    storaged_sp->report_storage_info();
-
-    LOG_TO(SYSTEM, INFO) << "storaged: Start";
+    LOG(INFO) << "storaged: Start";
 
     for (;;) {
         storaged_sp->event_checked();
@@ -76,6 +71,8 @@
     bool flag_dump_perf = false;
     int opt;
 
+    android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
+
     for (;;) {
         int opt_idx = 0;
         static struct option long_options[] = {
@@ -121,16 +118,19 @@
 
     if (flag_main_service) { // start main thread
         // Start the main thread of storaged
+        storaged_sp = new storaged_t();
+        storaged_sp->init();
+        storaged_sp->report_storage_info();
         pthread_t storaged_main_thread;
         errno = pthread_create(&storaged_main_thread, NULL, storaged_main, NULL);
         if (errno != 0) {
-            PLOG_TO(SYSTEM, ERROR) << "Failed to create main thread";
+            PLOG(ERROR) << "Failed to create main thread";
             return -1;
         }
 
         if (StoragedService::start() != android::OK ||
             StoragedPrivateService::start() != android::OK) {
-            PLOG_TO(SYSTEM, ERROR) << "Failed to start storaged service";
+            PLOG(ERROR) << "Failed to start storaged service";
             return -1;
         }
 
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index 1d934a2..573b8c5 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -97,25 +97,23 @@
 
     health = get_health_service();
     if (health == NULL) {
-        LOG_TO(SYSTEM, WARNING) << "health: failed to find IHealth service";
+        LOG(WARNING) << "health: failed to find IHealth service";
         return;
     }
 
     BatteryStatus status = BatteryStatus::UNKNOWN;
     auto ret = health->getChargeStatus([&](Result r, BatteryStatus v) {
         if (r != Result::SUCCESS) {
-            LOG_TO(SYSTEM, WARNING)
-                << "health: cannot get battery status " << toString(r);
+            LOG(WARNING) << "health: cannot get battery status " << toString(r);
             return;
         }
         if (v == BatteryStatus::UNKNOWN) {
-            LOG_TO(SYSTEM, WARNING) << "health: invalid battery status";
+            LOG(WARNING) << "health: invalid battery status";
         }
         status = v;
     });
     if (!ret.isOk()) {
-        LOG_TO(SYSTEM, WARNING) << "health: get charge status transaction error "
-            << ret.description();
+        LOG(WARNING) << "health: get charge status transaction error " << ret.description();
     }
 
     mUidm.init(is_charger_on(status));
@@ -126,11 +124,11 @@
 
 void storaged_t::serviceDied(uint64_t cookie, const wp<::android::hidl::base::V1_0::IBase>& who) {
     if (health != NULL && interfacesEqual(health, who.promote())) {
-        LOG_TO(SYSTEM, ERROR) << "health service died, exiting";
+        LOG(ERROR) << "health service died, exiting";
         android::hardware::IPCThreadState::self()->stopProcess();
         exit(1);
     } else {
-        LOG_TO(SYSTEM, ERROR) << "unknown service died";
+        LOG(ERROR) << "unknown service died";
     }
 }
 
@@ -192,7 +190,7 @@
         reinterpret_cast<const Bytef*>(uid_io_usage.SerializeAsString().c_str()),
         uid_io_usage.ByteSize());
     if (proto.crc() != computed_crc) {
-        LOG_TO(SYSTEM, WARNING) << "CRC mismatch in " << proto_file;
+        LOG(WARNING) << "CRC mismatch in " << proto_file;
         return;
     }
 
@@ -228,8 +226,7 @@
     char* data = nullptr;
     if (posix_memalign(reinterpret_cast<void**>(&data),
                        pagesize, proto->ByteSize())) {
-        PLOG_TO(SYSTEM, ERROR) << "Faied to alloc aligned buffer (size: "
-                               << proto->ByteSize() << ")";
+        PLOG(ERROR) << "Faied to alloc aligned buffer (size: " << proto->ByteSize() << ")";
         return data;
     }
 
@@ -246,7 +243,7 @@
                     (user_id == USER_SYSTEM ? O_DIRECT : 0),
                  S_IRUSR | S_IWUSR)));
     if (fd == -1) {
-        PLOG_TO(SYSTEM, ERROR) << "Faied to open tmp file: " << tmp_file;
+        PLOG(ERROR) << "Faied to open tmp file: " << tmp_file;
         return;
     }
 
@@ -261,7 +258,7 @@
             start = steady_clock::now();
             ret = write(fd, data, MIN(benchmark_unit_size, size));
             if (ret <= 0) {
-                PLOG_TO(SYSTEM, ERROR) << "Faied to write tmp file: " << tmp_file;
+                PLOG(ERROR) << "Faied to write tmp file: " << tmp_file;
                 return;
             }
             end = steady_clock::now();
@@ -284,7 +281,7 @@
         }
     } else {
         if (!WriteFully(fd, data, size)) {
-            PLOG_TO(SYSTEM, ERROR) << "Faied to write tmp file: " << tmp_file;
+            PLOG(ERROR) << "Faied to write tmp file: " << tmp_file;
             return;
         }
     }
@@ -343,22 +340,21 @@
     if (mConfig.event_time_check_usec &&
         clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start_ts) < 0) {
         check_time = false;
-        PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
+        PLOG(ERROR) << "clock_gettime() failed";
     }
 
     event();
 
     if (mConfig.event_time_check_usec && check_time) {
         if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end_ts) < 0) {
-            PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
+            PLOG(ERROR) << "clock_gettime() failed";
             return;
         }
         int64_t cost = (end_ts.tv_sec - start_ts.tv_sec) * SEC_TO_USEC +
                        (end_ts.tv_nsec - start_ts.tv_nsec) / USEC_TO_NSEC;
         if (cost > mConfig.event_time_check_usec) {
-            LOG_TO(SYSTEM, ERROR)
-                << "event loop spent " << cost << " usec, threshold "
-                << mConfig.event_time_check_usec << " usec";
+            LOG(ERROR) << "event loop spent " << cost << " usec, threshold "
+                       << mConfig.event_time_check_usec << " usec";
         }
     }
 }
diff --git a/storaged/storaged_diskstats.cpp b/storaged/storaged_diskstats.cpp
index 8b5001d..52bd4e0 100644
--- a/storaged/storaged_diskstats.cpp
+++ b/storaged/storaged_diskstats.cpp
@@ -41,8 +41,8 @@
     // skip if the input structure are all zeros
     if (perf == NULL || perf->is_zero()) return;
 
-    LOG_TO(SYSTEM, INFO) << "disk_perf " << type
-              << " rd: " << perf->read_perf << " kbps, " << perf->read_ios << " iops"
+    LOG(INFO) << "disk_perf " << type << " rd: " << perf->read_perf << " kbps, " << perf->read_ios
+              << " iops"
               << " wr: " << perf->write_perf << " kbps, " << perf->write_ios << " iops"
               << " q: " << perf->queue;
 }
@@ -71,7 +71,7 @@
     // when system is running.
     int ret = clock_gettime(CLOCK_MONOTONIC, ts);
     if (ret < 0) {
-        PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
+        PLOG(ERROR) << "clock_gettime() failed";
         return false;
     }
     return true;
@@ -93,7 +93,7 @@
 
     std::string buffer;
     if (!android::base::ReadFileToString(disk_stats_path, &buffer)) {
-        PLOG_TO(SYSTEM, ERROR) << disk_stats_path << ": ReadFileToString failed.";
+        PLOG(ERROR) << disk_stats_path << ": ReadFileToString failed.";
         return false;
     }
 
@@ -130,12 +130,12 @@
     bool success = false;
     auto ret = service->getDiskStats([&success, stats](auto result, const auto& halStats) {
         if (result == Result::NOT_SUPPORTED) {
-            LOG_TO(SYSTEM, DEBUG) << "getDiskStats is not supported on health HAL.";
+            LOG(DEBUG) << "getDiskStats is not supported on health HAL.";
             return;
         }
         if (result != Result::SUCCESS || halStats.size() == 0) {
-            LOG_TO(SYSTEM, ERROR) << "getDiskStats failed with result " << toString(result)
-                                  << " and size " << halStats.size();
+            LOG(ERROR) << "getDiskStats failed with result " << toString(result) << " and size "
+                       << halStats.size();
             return;
         }
 
@@ -144,7 +144,7 @@
     });
 
     if (!ret.isOk()) {
-        LOG_TO(SYSTEM, ERROR) << "getDiskStats failed with " << ret.description();
+        LOG(ERROR) << "getDiskStats failed with " << ret.description();
         return false;
     }
 
@@ -199,9 +199,9 @@
 void add_disk_stats(struct disk_stats* src, struct disk_stats* dst)
 {
     if (dst->end_time != 0 && dst->end_time != src->start_time) {
-        LOG_TO(SYSTEM, WARNING) << "Two dis-continuous periods of diskstats"
-            << " are added. dst end with " << dst->end_time
-            << ", src start with " << src->start_time;
+        LOG(WARNING) << "Two dis-continuous periods of diskstats"
+                     << " are added. dst end with " << dst->end_time << ", src start with "
+                     << src->start_time;
     }
 
     *dst += *src;
diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp
index 6668cf3..bb21829 100644
--- a/storaged/storaged_info.cpp
+++ b/storaged/storaged_info.cpp
@@ -76,7 +76,7 @@
     if (!perf_history.has_day_start_sec() ||
         perf_history.daily_perf_size() > (int)daily_perf.size() ||
         perf_history.weekly_perf_size() > (int)weekly_perf.size()) {
-        LOG_TO(SYSTEM, ERROR) << "Invalid IOPerfHistory proto";
+        LOG(ERROR) << "Invalid IOPerfHistory proto";
         return;
     }
 
@@ -114,7 +114,7 @@
 {
     struct statvfs buf;
     if (statvfs(userdata_path.c_str(), &buf) != 0) {
-        PLOG_TO(SYSTEM, WARNING) << "Failed to get userdata info";
+        PLOG(WARNING) << "Failed to get userdata info";
         return;
     }
 
@@ -328,12 +328,12 @@
 void health_storage_info_t::report() {
     auto ret = mHealth->getStorageInfo([this](auto result, const auto& halInfos) {
         if (result == Result::NOT_SUPPORTED) {
-            LOG_TO(SYSTEM, DEBUG) << "getStorageInfo is not supported on health HAL.";
+            LOG(DEBUG) << "getStorageInfo is not supported on health HAL.";
             return;
         }
         if (result != Result::SUCCESS || halInfos.size() == 0) {
-            LOG_TO(SYSTEM, ERROR) << "getStorageInfo failed with result " << toString(result)
-                                  << " and size " << halInfos.size();
+            LOG(ERROR) << "getStorageInfo failed with result " << toString(result) << " and size "
+                       << halInfos.size();
             return;
         }
         set_values_from_hal_storage_info(halInfos[0]);
@@ -341,7 +341,7 @@
     });
 
     if (!ret.isOk()) {
-        LOG_TO(SYSTEM, ERROR) << "getStorageInfo failed with " << ret.description();
+        LOG(ERROR) << "getStorageInfo failed with " << ret.description();
     }
 }
 
diff --git a/storaged/storaged_uid_monitor.cpp b/storaged/storaged_uid_monitor.cpp
index 55380ba..f47bf72 100644
--- a/storaged/storaged_uid_monitor.cpp
+++ b/storaged/storaged_uid_monitor.cpp
@@ -71,8 +71,7 @@
         !ParseUint(fields[8],  &io[BACKGROUND].write_bytes) ||
         !ParseUint(fields[9],  &io[FOREGROUND].fsync) ||
         !ParseUint(fields[10], &io[BACKGROUND].fsync)) {
-        LOG_TO(SYSTEM, WARNING) << "Invalid uid I/O stats: \""
-                                << s << "\"";
+        LOG(WARNING) << "Invalid uid I/O stats: \"" << s << "\"";
         return false;
     }
     return true;
@@ -95,8 +94,7 @@
         !ParseUint(fields[size - 3], &io[BACKGROUND].write_bytes) ||
         !ParseUint(fields[size - 2], &io[FOREGROUND].fsync) ||
         !ParseUint(fields[size - 1], &io[BACKGROUND].fsync)) {
-        LOG_TO(SYSTEM, WARNING) << "Invalid task I/O stats: \""
-                                << s << "\"";
+        LOG(WARNING) << "Invalid task I/O stats: \"" << s << "\"";
         return false;
     }
     comm = Join(std::vector<std::string>(
@@ -123,13 +121,13 @@
 {
     sp<IServiceManager> sm = defaultServiceManager();
     if (sm == NULL) {
-        LOG_TO(SYSTEM, ERROR) << "defaultServiceManager failed";
+        LOG(ERROR) << "defaultServiceManager failed";
         return;
     }
 
     sp<IBinder> binder = sm->getService(String16("package_native"));
     if (binder == NULL) {
-        LOG_TO(SYSTEM, ERROR) << "getService package_native failed";
+        LOG(ERROR) << "getService package_native failed";
         return;
     }
 
@@ -137,8 +135,7 @@
     std::vector<std::string> names;
     binder::Status status = package_mgr->getNamesForUids(uids, &names);
     if (!status.isOk()) {
-        LOG_TO(SYSTEM, ERROR) << "package_native::getNamesForUids failed: "
-                              << status.exceptionMessage();
+        LOG(ERROR) << "package_native::getNamesForUids failed: " << status.exceptionMessage();
         return;
     }
 
@@ -158,7 +155,7 @@
     std::unordered_map<uint32_t, uid_info> uid_io_stats;
     std::string buffer;
     if (!ReadFileToString(UID_IO_STATS_PATH, &buffer)) {
-        PLOG_TO(SYSTEM, ERROR) << UID_IO_STATS_PATH << ": ReadFileToString failed";
+        PLOG(ERROR) << UID_IO_STATS_PATH << ": ReadFileToString failed";
         return uid_io_stats;
     }
 
diff --git a/trusty/Android.bp b/trusty/Android.bp
deleted file mode 100644
index 2fb2e19..0000000
--- a/trusty/Android.bp
+++ /dev/null
@@ -1,6 +0,0 @@
-subdirs = [
-    "gatekeeper",
-    "keymaster",
-    "libtrusty",
-    "storage/*",
-]
diff --git a/trusty/confirmationui/.clang-format b/trusty/confirmationui/.clang-format
new file mode 100644
index 0000000..b0dc94c
--- /dev/null
+++ b/trusty/confirmationui/.clang-format
@@ -0,0 +1,10 @@
+BasedOnStyle: LLVM
+IndentWidth: 4
+UseTab: Never
+BreakBeforeBraces: Attach
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: true
+IndentCaseLabels: false
+ColumnLimit: 100
+PointerBindsToType: true
+SpacesBeforeTrailingComments: 2
diff --git a/trusty/confirmationui/Android.bp b/trusty/confirmationui/Android.bp
new file mode 100644
index 0000000..60e0e71
--- /dev/null
+++ b/trusty/confirmationui/Android.bp
@@ -0,0 +1,95 @@
+// 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.
+//
+
+// WARNING: Everything listed here will be built on ALL platforms,
+// including x86, the emulator, and the SDK.  Modules must be uniquely
+// named (liblights.panda), and must build everywhere, or limit themselves
+// to only building on ARM if they include assembly. Individual makefiles
+// are responsible for having their own logic, for fine-grained control.
+
+cc_binary {
+    name: "android.hardware.confirmationui@1.0-service.trusty",
+    relative_install_path: "hw",
+    vendor: true,
+    shared_libs: [
+        "android.hardware.confirmationui@1.0",
+        "android.hardware.confirmationui.not-so-secure-input",
+        "android.hardware.confirmationui@1.0-lib.trusty",
+        "libbase",
+        "libhidlbase",
+        "libutils",
+    ],
+
+    init_rc: ["android.hardware.confirmationui@1.0-service.trusty.rc"],
+
+    vintf_fragments: ["android.hardware.confirmationui@1.0-service.trusty.xml"],
+
+    srcs: [
+        "service.cpp",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-DTEEUI_USE_STD_VECTOR",
+    ],
+}
+
+cc_library {
+    name: "android.hardware.confirmationui@1.0-lib.trusty",
+    vendor: true,
+    shared_libs: [
+        "android.hardware.confirmationui@1.0",
+        "android.hardware.keymaster@4.0",
+        "libbase",
+        "libhidlbase",
+        "libteeui_hal_support",
+        "libtrusty",
+        "libutils",
+    ],
+
+    export_include_dirs: ["include"],
+
+    srcs: [
+        "TrustyApp.cpp",
+        "TrustyConfirmationUI.cpp",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-DTEEUI_USE_STD_VECTOR",
+    ],
+}
+
+cc_library {
+    name: "android.hardware.confirmationui.not-so-secure-input",
+    vendor: true,
+    shared_libs: [
+        "libbase",
+        "libcrypto",
+        "libteeui_hal_support",
+    ],
+
+    srcs: [
+        "NotSoSecureInput.cpp",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-DTEEUI_USE_STD_VECTOR",
+    ],
+}
\ No newline at end of file
diff --git a/trusty/confirmationui/NotSoSecureInput.cpp b/trusty/confirmationui/NotSoSecureInput.cpp
new file mode 100644
index 0000000..3d9a2d6
--- /dev/null
+++ b/trusty/confirmationui/NotSoSecureInput.cpp
@@ -0,0 +1,207 @@
+/*
+ * Copyright 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 <android-base/logging.h>
+#include <endian.h>
+#include <memory>
+#include <openssl/hmac.h>
+#include <openssl/rand.h>
+#include <openssl/sha.h>
+#include <secure_input/evdev.h>
+#include <secure_input/secure_input_device.h>
+#include <teeui/utils.h>
+
+#include <initializer_list>
+
+using namespace secure_input;
+
+using teeui::AuthTokenKey;
+using teeui::ByteBufferProxy;
+using teeui::Hmac;
+using teeui::optional;
+using teeui::ResponseCode;
+using teeui::TestKeyBits;
+
+constexpr const auto kTestKey = AuthTokenKey::fill(static_cast<uint8_t>(TestKeyBits::BYTE));
+
+class SecureInputHMacer {
+  public:
+    static optional<Hmac> hmac256(const AuthTokenKey& key,
+                                  std::initializer_list<ByteBufferProxy> buffers) {
+        HMAC_CTX hmacCtx;
+        HMAC_CTX_init(&hmacCtx);
+        if (!HMAC_Init_ex(&hmacCtx, key.data(), key.size(), EVP_sha256(), nullptr)) {
+            return {};
+        }
+        for (auto& buffer : buffers) {
+            if (!HMAC_Update(&hmacCtx, buffer.data(), buffer.size())) {
+                return {};
+            }
+        }
+        Hmac result;
+        if (!HMAC_Final(&hmacCtx, result.data(), nullptr)) {
+            return {};
+        }
+        return result;
+    }
+};
+
+using HMac = teeui::HMac<SecureInputHMacer>;
+
+Nonce generateNonce() {
+    /*
+     * Completely random nonce.
+     * Running the secure input protocol from the HAL service is not secure
+     * because we don't trust the non-secure world (i.e., HLOS/Android/Linux). So
+     * using a constant "nonce" here does not weaken security. If this code runs
+     * on a truly trustworthy source of input events this function needs to return
+     * hight entropy nonces.
+     * As of this writing the call to RAND_bytes is commented, because the
+     * emulator this HAL service runs on does not have a good source of entropy.
+     * It would block the call to RAND_bytes indefinitely.
+     */
+    Nonce result{0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+                 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+                 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04};
+    // RAND_bytes(result.data(), result.size());
+    return result;
+}
+
+/**
+ * This is an implementation of the SecureInput protocol in unserspace. This is
+ * just an example and should not be used as is. The protocol implemented her
+ * should be used by a trusted input device that can assert user events with
+ * high assurance even if the HLOS kernel is compromised. A confirmationui HAL
+ * that links directly against this implementation is not secure and shal not be
+ * used on a production device.
+ */
+class NotSoSecureInput : public SecureInput {
+  public:
+    NotSoSecureInput(HsBeginCb hsBeginCb, HsFinalizeCb hsFinalizeCb, DeliverEventCb deliverEventCb,
+                     InputResultCb inputResultCb)
+        : hsBeginCb_{hsBeginCb}, hsFinalizeCb_{hsFinalizeCb}, deliverEventCb_{deliverEventCb},
+          inputResultCb_{inputResultCb}, discardEvents_{true} {}
+
+    operator bool() const override { return true; }
+
+    void handleEvent(const EventDev& evdev) override {
+        bool gotEvent;
+        input_event evt;
+        std::tie(gotEvent, evt) = evdev.readEvent();
+        while (gotEvent) {
+            if (!(discardEvents_) && evt.type == EV_KEY &&
+                (evt.code == KEY_POWER || evt.code == KEY_VOLUMEDOWN || evt.code == KEY_VOLUMEUP) &&
+                evt.value == 1) {
+                DTupKeyEvent event = DTupKeyEvent::RESERVED;
+
+                // Translate the event code into DTupKeyEvent which the TA understands.
+                switch (evt.code) {
+                case KEY_POWER:
+                    event = DTupKeyEvent::PWR;
+                    break;
+                case KEY_VOLUMEDOWN:
+                    event = DTupKeyEvent::VOL_DOWN;
+                    break;
+                case KEY_VOLUMEUP:
+                    event = DTupKeyEvent::VOL_UP;
+                    break;
+                }
+
+                // The event goes into the HMAC in network byte order.
+                uint32_t keyEventBE = htobe32(static_cast<uint32_t>(event));
+                auto signature = HMac::hmac256(kTestKey, kConfirmationUIEventLabel,
+                                               teeui::bytesCast(keyEventBE), nCi_);
+
+                teeui::ResponseCode rc;
+                InputResponse ir;
+                auto response = std::tie(rc, ir);
+                if (event != DTupKeyEvent::RESERVED) {
+                    response = deliverEventCb_(event, *signature);
+                    if (rc != ResponseCode::OK) {
+                        LOG(ERROR) << "DeliverInputEvent returned with " << uint32_t(rc);
+                        inputResultCb_(rc);
+                    } else {
+                        switch (ir) {
+                        case InputResponse::OK:
+                            inputResultCb_(rc);
+                            break;
+                        case InputResponse::PENDING_MORE:
+                            rc = performDTUPHandshake();
+                            if (rc != ResponseCode::OK) {
+                                inputResultCb_(rc);
+                            }
+                            break;
+                        case InputResponse::TIMED_OUT:
+                            inputResultCb_(rc);
+                            break;
+                        }
+                    }
+                }
+            }
+            std::tie(gotEvent, evt) = evdev.readEvent();
+        }
+    }
+
+    void start() override {
+        auto rc = performDTUPHandshake();
+        if (rc != ResponseCode::OK) {
+            inputResultCb_(rc);
+        }
+        discardEvents_ = false;
+    };
+
+  private:
+    teeui::ResponseCode performDTUPHandshake() {
+        ResponseCode rc;
+        LOG(INFO) << "Start handshake";
+        Nonce nCo;
+        std::tie(rc, nCo) = hsBeginCb_();
+        if (rc != ResponseCode::OK) {
+            LOG(ERROR) << "Failed to begin secure input handshake (" << uint32_t(rc) << ")";
+            return rc;
+        }
+
+        nCi_ = generateNonce();
+        rc =
+            hsFinalizeCb_(*HMac::hmac256(kTestKey, kConfirmationUIHandshakeLabel, nCo, nCi_), nCi_);
+
+        if (rc != ResponseCode::OK) {
+            LOG(ERROR) << "Failed to finalize secure input handshake (" << uint32_t(rc) << ")";
+            return rc;
+        }
+        return ResponseCode::OK;
+    }
+
+    HsBeginCb hsBeginCb_;
+    HsFinalizeCb hsFinalizeCb_;
+    DeliverEventCb deliverEventCb_;
+    InputResultCb inputResultCb_;
+
+    std::atomic_bool discardEvents_;
+    Nonce nCi_;
+};
+
+namespace secure_input {
+
+std::shared_ptr<SecureInput> createSecureInput(SecureInput::HsBeginCb hsBeginCb,
+                                               SecureInput::HsFinalizeCb hsFinalizeCb,
+                                               SecureInput::DeliverEventCb deliverEventCb,
+                                               SecureInput::InputResultCb inputResultCb) {
+    return std::make_shared<NotSoSecureInput>(hsBeginCb, hsFinalizeCb, deliverEventCb,
+                                              inputResultCb);
+}
+
+}  // namespace secure_input
diff --git a/trusty/confirmationui/README b/trusty/confirmationui/README
new file mode 100644
index 0000000..45d4e76
--- /dev/null
+++ b/trusty/confirmationui/README
@@ -0,0 +1,20 @@
+## Secure UI Architecture
+
+To implement confirmationui a secure UI architecture is required. This entails a way
+to display the confirmation dialog driven by a reduced trusted computing base, typically
+a trusted execution environment (TEE), without having to rely on Linux and the Android
+system for integrity and authenticity of input events. This implementation provides
+neither. But it provides most of the functionlity required to run a full Android Protected
+Confirmation feature when integrated into a secure UI architecture.
+
+## Secure input (NotSoSecureInput)
+
+This implementation does not provide any security guaranties.
+The input method (NotSoSecureInput) runs a cryptographic protocols that is
+sufficiently secure IFF the end point is implemented on a trustworthy
+secure input device. But since the endpoint is currently in the HAL
+service itself this implementation is not secure.
+
+NOTE that a secure input device end point needs a good source of entropy
+for generating nonces. The current implementation (NotSoSecureInput.cpp#generateNonce)
+uses a constant nonce.
\ No newline at end of file
diff --git a/trusty/confirmationui/TrustyApp.cpp b/trusty/confirmationui/TrustyApp.cpp
new file mode 100644
index 0000000..e4c68f9
--- /dev/null
+++ b/trusty/confirmationui/TrustyApp.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright 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 "TrustyApp.h"
+
+#include <android-base/logging.h>
+#include <sys/uio.h>
+#include <trusty/tipc.h>
+
+namespace android {
+namespace trusty {
+
+// 0x1000 is the message buffer size but we need to leave some space for a protocol header.
+// This assures that packets can always be read/written in one read/write operation.
+static constexpr const uint32_t kPacketSize = 0x1000 - 32;
+
+enum class PacketType : uint32_t {
+    SND,
+    RCV,
+    ACK,
+};
+
+struct PacketHeader {
+    PacketType type;
+    uint32_t remaining;
+};
+
+const char* toString(PacketType t) {
+    switch (t) {
+    case PacketType::SND:
+        return "SND";
+    case PacketType::RCV:
+        return "RCV";
+    case PacketType::ACK:
+        return "ACK";
+    default:
+        return "UNKNOWN";
+    }
+}
+
+static constexpr const uint32_t kHeaderSize = sizeof(PacketHeader);
+static constexpr const uint32_t kPayloadSize = kPacketSize - kHeaderSize;
+
+ssize_t TrustyRpc(int handle, const uint8_t* obegin, const uint8_t* oend, uint8_t* ibegin,
+                  uint8_t* iend) {
+    while (obegin != oend) {
+        PacketHeader header = {
+            .type = PacketType::SND,
+            .remaining = uint32_t(oend - obegin),
+        };
+        uint32_t body_size = std::min(kPayloadSize, header.remaining);
+        iovec iov[] = {
+            {
+                .iov_base = &header,
+                .iov_len = kHeaderSize,
+            },
+            {
+                .iov_base = const_cast<uint8_t*>(obegin),
+                .iov_len = body_size,
+            },
+        };
+        int rc = writev(handle, iov, 2);
+        if (!rc) {
+            PLOG(ERROR) << "Error sending SND message. " << rc;
+            return rc;
+        }
+
+        obegin += body_size;
+
+        rc = read(handle, &header, kHeaderSize);
+        if (!rc) {
+            PLOG(ERROR) << "Error reading ACK. " << rc;
+            return rc;
+        }
+
+        if (header.type != PacketType::ACK || header.remaining != oend - obegin) {
+            LOG(ERROR) << "malformed ACK";
+            return -1;
+        }
+    }
+
+    ssize_t remaining = 0;
+    auto begin = ibegin;
+    do {
+        PacketHeader header = {
+            .type = PacketType::RCV,
+            .remaining = 0,
+        };
+
+        iovec iov[] = {
+            {
+                .iov_base = &header,
+                .iov_len = kHeaderSize,
+            },
+            {
+                .iov_base = begin,
+                .iov_len = uint32_t(iend - begin),
+            },
+        };
+
+        ssize_t rc = writev(handle, iov, 1);
+        if (!rc) {
+            PLOG(ERROR) << "Error sending RCV message. " << rc;
+            return rc;
+        }
+
+        rc = readv(handle, iov, 2);
+        if (rc < 0) {
+            PLOG(ERROR) << "Error reading response. " << rc;
+            return rc;
+        }
+
+        uint32_t body_size = std::min(kPayloadSize, header.remaining);
+        if (body_size != rc - kHeaderSize) {
+            LOG(ERROR) << "Unexpected amount of data: " << rc;
+            return -1;
+        }
+
+        remaining = header.remaining - body_size;
+        begin += body_size;
+    } while (remaining);
+
+    return begin - ibegin;
+}
+
+TrustyApp::TrustyApp(const std::string& path, const std::string& appname)
+    : handle_(kInvalidHandle) {
+    handle_ = tipc_connect(path.c_str(), appname.c_str());
+    if (handle_ == kInvalidHandle) {
+        LOG(ERROR) << AT << "failed to connect to Trusty TA \"" << appname << "\" using dev:"
+                   << "\"" << path << "\"";
+    }
+    LOG(INFO) << AT << "succeeded to connect to Trusty TA \"" << appname << "\"";
+}
+TrustyApp::~TrustyApp() {
+    if (handle_ != kInvalidHandle) {
+        tipc_close(handle_);
+    }
+    LOG(INFO) << "Done shutting down TrustyApp";
+}
+
+}  // namespace trusty
+}  // namespace android
diff --git a/trusty/confirmationui/TrustyApp.h b/trusty/confirmationui/TrustyApp.h
new file mode 100644
index 0000000..05a25f6
--- /dev/null
+++ b/trusty/confirmationui/TrustyApp.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright 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 <android-base/logging.h>
+#include <errno.h>
+#include <poll.h>
+#include <stdio.h>
+#include <sys/eventfd.h>
+#include <sys/stat.h>
+#include <teeui/msg_formatting.h>
+#include <trusty/tipc.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <functional>
+#include <future>
+#include <iostream>
+#include <sstream>
+#include <thread>
+#include <vector>
+
+#define AT __FILE__ ":" << __LINE__ << ": "
+
+namespace android {
+namespace trusty {
+
+using ::teeui::Message;
+using ::teeui::msg2tuple_t;
+using ::teeui::ReadStream;
+using ::teeui::WriteStream;
+
+#ifndef TEEUI_USE_STD_VECTOR
+/*
+ * TEEUI_USE_STD_VECTOR makes certain wire types like teeui::MsgString and
+ * teeui::MsgVector be aliases for std::vector. This is required for thread safe
+ * message serialization. Always compile this with -DTEEUI_USE_STD_VECTOR set in
+ * CFLAGS of the HAL service.
+ */
+#error "Must be compiled with -DTEEUI_USE_STD_VECTOR."
+#endif
+
+enum class TrustyAppError : int32_t {
+    OK,
+    ERROR = -1,
+    MSG_TOO_LONG = -2,
+};
+
+/*
+ * There is a hard limitation of 0x1800 bytes for the to-be-signed message size. The protocol
+ * overhead is limited, so that 0x2000 is a buffer size that will be sufficient in any benign
+ * mode of operation.
+ */
+static constexpr const size_t kSendBufferSize = 0x2000;
+
+ssize_t TrustyRpc(int handle, const uint8_t* obegin, const uint8_t* oend, uint8_t* ibegin,
+                  uint8_t* iend);
+
+class TrustyApp {
+  private:
+    int handle_;
+    static constexpr const int kInvalidHandle = -1;
+    /*
+     * This mutex serializes communication with the trusted app, not handle_.
+     * Calling issueCmd during construction or deletion is undefined behavior.
+     */
+    std::mutex mutex_;
+
+  public:
+    TrustyApp(const std::string& path, const std::string& appname);
+    ~TrustyApp();
+
+    template <typename Request, typename Response, typename... T>
+    std::tuple<TrustyAppError, msg2tuple_t<Response>> issueCmd(const T&... args) {
+        std::lock_guard<std::mutex> lock(mutex_);
+
+        if (handle_ == kInvalidHandle) {
+            LOG(ERROR) << "TrustyApp not connected";
+            return {TrustyAppError::ERROR, {}};
+        }
+
+        uint8_t buffer[kSendBufferSize];
+        WriteStream out(buffer);
+
+        out = write(Request(), out, args...);
+        if (!out) {
+            LOG(ERROR) << AT << "send command failed: message formatting";
+            return {TrustyAppError::MSG_TOO_LONG, {}};
+        }
+
+        auto rc = TrustyRpc(handle_, &buffer[0], const_cast<const uint8_t*>(out.pos()), &buffer[0],
+                            &buffer[kSendBufferSize]);
+        if (rc < 0) return {TrustyAppError::ERROR, {}};
+
+        ReadStream in(&buffer[0], rc);
+        auto result = read(Response(), in);
+        if (!std::get<0>(result)) {
+            LOG(ERROR) << "send command failed: message parsing";
+            return {TrustyAppError::ERROR, {}};
+        }
+
+        return {std::get<0>(result) ? TrustyAppError::OK : TrustyAppError::ERROR,
+                tuple_tail(std::move(result))};
+    }
+
+    template <typename Request, typename... T> TrustyAppError issueCmd(const T&... args) {
+        std::lock_guard<std::mutex> lock(mutex_);
+
+        if (handle_ == kInvalidHandle) {
+            LOG(ERROR) << "TrustyApp not connected";
+            return TrustyAppError::ERROR;
+        }
+
+        uint8_t buffer[kSendBufferSize];
+        WriteStream out(buffer);
+
+        out = write(Request(), out, args...);
+        if (!out) {
+            LOG(ERROR) << AT << "send command failed: message formatting";
+            return TrustyAppError::MSG_TOO_LONG;
+        }
+
+        auto rc = TrustyRpc(handle_, &buffer[0], const_cast<const uint8_t*>(out.pos()), &buffer[0],
+                            &buffer[kSendBufferSize]);
+        if (rc < 0) {
+            LOG(ERROR) << "send command failed: " << strerror(errno) << " (" << errno << ")";
+            return TrustyAppError::ERROR;
+        }
+
+        if (rc > 0) {
+            LOG(ERROR) << "Unexpected non zero length response";
+            return TrustyAppError::ERROR;
+        }
+        return TrustyAppError::OK;
+    }
+
+    operator bool() const { return handle_ != kInvalidHandle; }
+};
+
+}  // namespace trusty
+}  // namespace android
diff --git a/trusty/confirmationui/TrustyConfirmationUI.cpp b/trusty/confirmationui/TrustyConfirmationUI.cpp
new file mode 100644
index 0000000..6b25893
--- /dev/null
+++ b/trusty/confirmationui/TrustyConfirmationUI.cpp
@@ -0,0 +1,513 @@
+/*
+ *
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TrustyConfirmationUI.h"
+
+#include <android-base/logging.h>
+#include <android/hardware/confirmationui/1.0/types.h>
+#include <android/hardware/keymaster/4.0/types.h>
+#include <fcntl.h>
+#include <linux/input.h>
+#include <poll.h>
+#include <pthread.h>
+#include <secure_input/evdev.h>
+#include <secure_input/secure_input_device.h>
+#include <secure_input/secure_input_proto.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <teeui/msg_formatting.h>
+#include <teeui/utils.h>
+#include <time.h>
+
+#include <atomic>
+#include <functional>
+#include <memory>
+#include <thread>
+#include <tuple>
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace confirmationui {
+namespace V1_0 {
+namespace implementation {
+
+using namespace secure_input;
+
+using ::android::trusty::TrustyAppError;
+
+using ::teeui::AbortMsg;
+using ::teeui::DeliverTestCommandMessage;
+using ::teeui::DeliverTestCommandResponse;
+using ::teeui::FetchConfirmationResult;
+using ::teeui::MsgString;
+using ::teeui::MsgVector;
+using ::teeui::PromptUserConfirmationMsg;
+using ::teeui::PromptUserConfirmationResponse;
+using ::teeui::ResultMsg;
+
+using ::secure_input::createSecureInput;
+
+using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
+
+using ::std::tie;
+
+using TeeuiRc = ::teeui::ResponseCode;
+
+constexpr const char kTrustyDeviceName[] = "/dev/trusty-ipc-dev0";
+constexpr const char kConfirmationuiAppName[] = "com.android.trusty.confirmationui";
+
+namespace {
+
+class Finalize {
+  private:
+    std::function<void()> f_;
+
+  public:
+    Finalize(std::function<void()> f) : f_(f) {}
+    ~Finalize() {
+        if (f_) f_();
+    }
+    void release() { f_ = {}; }
+};
+
+ResponseCode convertRc(TeeuiRc trc) {
+    static_assert(
+        uint32_t(TeeuiRc::OK) == uint32_t(ResponseCode::OK) &&
+            uint32_t(TeeuiRc::Canceled) == uint32_t(ResponseCode::Canceled) &&
+            uint32_t(TeeuiRc::Aborted) == uint32_t(ResponseCode::Aborted) &&
+            uint32_t(TeeuiRc::OperationPending) == uint32_t(ResponseCode::OperationPending) &&
+            uint32_t(TeeuiRc::Ignored) == uint32_t(ResponseCode::Ignored) &&
+            uint32_t(TeeuiRc::SystemError) == uint32_t(ResponseCode::SystemError) &&
+            uint32_t(TeeuiRc::Unimplemented) == uint32_t(ResponseCode::Unimplemented) &&
+            uint32_t(TeeuiRc::Unexpected) == uint32_t(ResponseCode::Unexpected) &&
+            uint32_t(TeeuiRc::UIError) == uint32_t(ResponseCode::UIError) &&
+            uint32_t(TeeuiRc::UIErrorMissingGlyph) == uint32_t(ResponseCode::UIErrorMissingGlyph) &&
+            uint32_t(TeeuiRc::UIErrorMessageTooLong) ==
+                uint32_t(ResponseCode::UIErrorMessageTooLong) &&
+            uint32_t(TeeuiRc::UIErrorMalformedUTF8Encoding) ==
+                uint32_t(ResponseCode::UIErrorMalformedUTF8Encoding),
+        "teeui::ResponseCode and "
+        "::android::hardware::confirmationui::V1_0::Responsecude are out of "
+        "sync");
+    return ResponseCode(trc);
+}
+
+teeui::UIOption convertUIOption(UIOption uio) {
+    static_assert(uint32_t(UIOption::AccessibilityInverted) ==
+                          uint32_t(teeui::UIOption::AccessibilityInverted) &&
+                      uint32_t(UIOption::AccessibilityMagnified) ==
+                          uint32_t(teeui::UIOption::AccessibilityMagnified),
+                  "teeui::UIOPtion and ::android::hardware::confirmationui::V1_0::UIOption "
+                  "anre out of sync");
+    return teeui::UIOption(uio);
+}
+
+inline MsgString hidl2MsgString(const hidl_string& s) {
+    return {s.c_str(), s.c_str() + s.size()};
+}
+template <typename T> inline MsgVector<T> hidl2MsgVector(const hidl_vec<T>& v) {
+    return {v};
+}
+
+inline MsgVector<teeui::UIOption> hidl2MsgVector(const hidl_vec<UIOption>& v) {
+    MsgVector<teeui::UIOption> result(v.size());
+    for (unsigned int i = 0; i < v.size(); ++i) {
+        result[i] = convertUIOption(v[i]);
+    }
+    return result;
+}
+
+}  // namespace
+
+TrustyConfirmationUI::TrustyConfirmationUI()
+    : listener_state_(ListenerState::None), prompt_result_(ResponseCode::Ignored) {}
+
+TrustyConfirmationUI::~TrustyConfirmationUI() {
+    ListenerState state = listener_state_;
+    if (state == ListenerState::SetupDone || state == ListenerState::Interactive) {
+        abort();
+    }
+    if (state != ListenerState::None) {
+        callback_thread_.join();
+    }
+}
+
+std::tuple<TeeuiRc, MsgVector<uint8_t>, MsgVector<uint8_t>>
+TrustyConfirmationUI::promptUserConfirmation_(const MsgString& promptText,
+                                              const MsgVector<uint8_t>& extraData,
+                                              const MsgString& locale,
+                                              const MsgVector<teeui::UIOption>& uiOptions) {
+    std::unique_lock<std::mutex> stateLock(listener_state_lock_);
+    /*
+     * This is the main listener thread function. The listener thread life cycle
+     * is equivalent to the life cycle of a single confirmation request. The life
+     * cycle is devided in four phases.
+     *  * The starting phase:
+     *    * The Trusted App gets loaded and/or the connection to it gets established.
+     *    * A connection to the secure input device is established.
+     *    * The prompt is initiated. This sends all information required by the
+     *      confirmation dialog to the TA. The dialog is not yet displayed.
+     *    * An event loop is created.
+     *      * The event loop listens for user input events, fetches them from the
+     *        secure input device, and delivers them to the TA.
+     *    * All evdev devices are grabbed to give confirmationui exclusive access
+     *      to user input.
+     *
+     * Note: During the starting phase the hwbinder service thread is blocked and
+     * waiting for possible Errors. If the setup phase concludes sucessfully, the
+     * hwbinder service thread gets unblocked and returns successfully. Errors
+     * that occur after the first phase are delivered by callback interface.
+     *
+     *  * The 2nd phase - non interactive phase
+     *    * The event loop thread is started.
+     *    * After a grace period:
+     *      * A handshake between the secure input device SecureInput and the TA
+     *        is performed.
+     *      * The input event handler are armed to process user input events.
+     *
+     *  * The 3rd phase - interactive phase
+     *    * We wait to any external event
+     *      * Abort
+     *      * Secure user input asserted
+     *      * Secure input delivered (for non interactive VTS testing)
+     *    * The result is fetched from the TA.
+     *
+     *  * The 4th phase - cleanup
+     *    The cleanup phase is given by the scope of automatic variables created
+     *    in this function. The cleanup commences in reverse order of their creation.
+     *    Here is a list of more complex items in the order in which they go out of
+     *    scope
+     *    * finalizeSecureTouch - signals and joins the secure touch thread.
+     *    * eventloop - signals and joins the event loop thread. The event
+     *      handlers also own all EventDev instances which ungrab the event devices.
+     *      When the eventloop goes out of scope the EventDevs get destroyed
+     *      relinquishing the exclusive hold on the event devices.
+     *    * finalizeConfirmationPrompt - calls abort on the TA, making sure a
+     *      pending operation gets canceled. If the prompt concluded successfully this
+     *      is a spurious call but semantically a no op.
+     *    * secureInput - shuts down the connection to the secure input device
+     *      SecureInput.
+     *    * app - disconnects the TA. Since app is a shared pointer this may not
+     *      unload the app here. It is possible that more instances of the shared
+     *      pointer are held in TrustyConfirmationUI::deliverSecureInputEvent and
+     *      TrustyConfirmationUI::abort. But these instances are extremely short lived
+     *      and it is safe if they are destroyed by either.
+     *    * stateLock - unlocks the listener_state_lock_ if it happens to be held
+     *      at the time of return.
+     */
+
+    std::tuple<TeeuiRc, MsgVector<uint8_t>, MsgVector<uint8_t>> result;
+    TeeuiRc& rc = std::get<TeeuiRc>(result);
+    rc = TeeuiRc::SystemError;
+
+    listener_state_ = ListenerState::Starting;
+
+    auto app = std::make_shared<TrustyApp>(kTrustyDeviceName, kConfirmationuiAppName);
+    if (!app) return result;  // TeeuiRc::SystemError
+
+    app_ = app;
+
+    auto hsBegin = [&]() -> std::tuple<TeeuiRc, Nonce> {
+        auto [error, result] =
+            app->issueCmd<secure_input::InputHandshake, secure_input::InputHandshakeResponse>();
+        auto& [rc, nCo] = result;
+
+        if (error != TrustyAppError::OK || rc != TeeuiRc::OK) {
+            LOG(ERROR) << "Failed to begin secure input handshake (" << int32_t(error) << "/"
+                       << uint32_t(rc) << ")";
+            rc = error != TrustyAppError::OK ? TeeuiRc::SystemError : rc;
+        }
+        return result;
+    };
+
+    auto hsFinalize = [&](const Signature& sig, const Nonce& nCi) -> TeeuiRc {
+        auto [error, finalizeResponse] =
+            app->issueCmd<FinalizeInputSessionHandshake, FinalizeInputSessionHandshakeResponse>(
+                nCi, sig);
+        auto& [rc] = finalizeResponse;
+        if (error != TrustyAppError::OK || rc != TeeuiRc::OK) {
+            LOG(ERROR) << "Failed to finalize secure input handshake (" << int32_t(error) << "/"
+                       << uint32_t(rc) << ")";
+            rc = error != TrustyAppError::OK ? TeeuiRc::SystemError : rc;
+        }
+        return rc;
+    };
+
+    auto deliverInput = [&](DTupKeyEvent event,
+                            const Signature& sig) -> std::tuple<TeeuiRc, InputResponse> {
+        auto [error, result] =
+            app->issueCmd<DeliverInputEvent, DeliverInputEventResponse>(event, sig);
+        auto& [rc, ir] = result;
+        if (error != TrustyAppError::OK) {
+            LOG(ERROR) << "Failed to deliver input command";
+            rc = TeeuiRc::SystemError;
+        }
+        return result;
+    };
+
+    std::atomic<TeeuiRc> eventRC = TeeuiRc::OperationPending;
+    auto inputResult = [&](TeeuiRc rc) {
+        TeeuiRc expected = TeeuiRc::OperationPending;
+        if (eventRC.compare_exchange_strong(expected, rc)) {
+            listener_state_condv_.notify_all();
+        }
+    };
+
+    // create Secure Input device.
+    auto secureInput = createSecureInput(hsBegin, hsFinalize, deliverInput, inputResult);
+    if (!secureInput || !(*secureInput)) {
+        LOG(ERROR) << "Failed to open secure input device";
+        return result;  // TeeuiRc::SystemError;
+    }
+
+    Finalize finalizeConfirmationPrompt([app] {
+        LOG(INFO) << "Calling abort for cleanup";
+        app->issueCmd<AbortMsg>();
+    });
+
+    // initiate prompt
+    LOG(INFO) << "Initiating prompt";
+    TrustyAppError error;
+    auto initResponse = std::tie(rc);
+    std::tie(error, initResponse) =
+        app->issueCmd<PromptUserConfirmationMsg, PromptUserConfirmationResponse>(
+            promptText, extraData, locale, uiOptions);
+    if (error == TrustyAppError::MSG_TOO_LONG) {
+        LOG(ERROR) << "PromptUserConfirmationMsg failed: message too long";
+        rc = TeeuiRc::UIErrorMessageTooLong;
+        return result;
+    } else if (error != TrustyAppError::OK) {
+        LOG(ERROR) << "PromptUserConfirmationMsg failed: " << int32_t(error);
+        return result;  // TeeuiRc::SystemError;
+    }
+    if (rc != TeeuiRc::OK) {
+        LOG(ERROR) << "PromptUserConfirmationMsg failed: " << uint32_t(rc);
+        return result;
+    }
+
+    LOG(INFO) << "Grabbing event devices";
+    EventLoop eventloop;
+    bool grabbed =
+        grabAllEvDevsAndRegisterCallbacks(&eventloop, [&](short flags, const EventDev& evDev) {
+            if (!(flags & POLLIN)) return;
+            secureInput->handleEvent(evDev);
+        });
+
+    if (!grabbed) {
+        rc = TeeuiRc::SystemError;
+        return result;
+    }
+
+    abort_called_ = false;
+    secureInputDelivered_ = false;
+
+    //  ############################## Start 2nd Phase #############################################
+    listener_state_ = ListenerState::SetupDone;
+    stateLock.unlock();
+    listener_state_condv_.notify_all();
+
+    if (!eventloop.start()) {
+        rc = TeeuiRc::SystemError;
+        return result;
+    }
+
+    stateLock.lock();
+
+    LOG(INFO) << "going to sleep for the grace period";
+    auto then = std::chrono::system_clock::now() +
+                std::chrono::milliseconds(kUserPreInputGracePeriodMillis) +
+                std::chrono::microseconds(50);
+    listener_state_condv_.wait_until(stateLock, then, [&]() { return abort_called_; });
+    LOG(INFO) << "waking up";
+
+    if (abort_called_) {
+        LOG(ERROR) << "Abort called";
+        result = {TeeuiRc::Aborted, {}, {}};
+        return result;
+    }
+
+    LOG(INFO) << "Arming event poller";
+    // tell the event poller to act on received input events from now on.
+    secureInput->start();
+
+    //  ############################## Start 3rd Phase - interactive phase #########################
+    LOG(INFO) << "Transition to Interactive";
+    listener_state_ = ListenerState::Interactive;
+    stateLock.unlock();
+    listener_state_condv_.notify_all();
+
+    stateLock.lock();
+    listener_state_condv_.wait(stateLock, [&]() {
+        return eventRC != TeeuiRc::OperationPending || abort_called_ || secureInputDelivered_;
+    });
+    LOG(INFO) << "Listener waking up";
+    if (abort_called_) {
+        LOG(ERROR) << "Abort called";
+        result = {TeeuiRc::Aborted, {}, {}};
+        return result;
+    }
+
+    if (!secureInputDelivered_) {
+        if (eventRC != TeeuiRc::OK) {
+            LOG(ERROR) << "Bad input response";
+            result = {eventRC, {}, {}};
+            return result;
+        }
+    }
+
+    stateLock.unlock();
+
+    LOG(INFO) << "Fetching Result";
+    std::tie(error, result) = app->issueCmd<FetchConfirmationResult, ResultMsg>();
+    LOG(INFO) << "Result yields " << int32_t(error) << "/" << uint32_t(rc);
+    if (error != TrustyAppError::OK) {
+        result = {TeeuiRc::SystemError, {}, {}};
+    }
+    return result;
+
+    //  ############################## Start 4th Phase - cleanup ##################################
+}
+
+// Methods from ::android::hardware::confirmationui::V1_0::IConfirmationUI
+// follow.
+Return<ResponseCode> TrustyConfirmationUI::promptUserConfirmation(
+    const sp<IConfirmationResultCallback>& resultCB, const hidl_string& promptText,
+    const hidl_vec<uint8_t>& extraData, const hidl_string& locale,
+    const hidl_vec<UIOption>& uiOptions) {
+    std::unique_lock<std::mutex> stateLock(listener_state_lock_, std::defer_lock);
+    if (!stateLock.try_lock()) {
+        return ResponseCode::OperationPending;
+    }
+    switch (listener_state_) {
+    case ListenerState::None:
+        break;
+    case ListenerState::Starting:
+    case ListenerState::SetupDone:
+    case ListenerState::Interactive:
+        return ResponseCode::OperationPending;
+    case ListenerState::Terminating:
+        callback_thread_.join();
+        listener_state_ = ListenerState::None;
+        break;
+    default:
+        return ResponseCode::Unexpected;
+    }
+
+    assert(listener_state_ == ListenerState::None);
+
+    callback_thread_ = std::thread(
+        [this](sp<IConfirmationResultCallback> resultCB, hidl_string promptText,
+               hidl_vec<uint8_t> extraData, hidl_string locale, hidl_vec<UIOption> uiOptions) {
+            auto [trc, msg, token] =
+                promptUserConfirmation_(hidl2MsgString(promptText), hidl2MsgVector(extraData),
+                                        hidl2MsgString(locale), hidl2MsgVector(uiOptions));
+            bool do_callback = (listener_state_ == ListenerState::Interactive ||
+                                listener_state_ == ListenerState::SetupDone) &&
+                               resultCB;
+            prompt_result_ = convertRc(trc);
+            listener_state_ = ListenerState::Terminating;
+            if (do_callback) {
+                auto error = resultCB->result(prompt_result_, msg, token);
+                if (!error.isOk()) {
+                    LOG(ERROR) << "Result callback failed " << error.description();
+                }
+            } else {
+                listener_state_condv_.notify_all();
+            }
+        },
+        resultCB, promptText, extraData, locale, uiOptions);
+
+    listener_state_condv_.wait(stateLock, [this] {
+        return listener_state_ == ListenerState::SetupDone ||
+               listener_state_ == ListenerState::Interactive ||
+               listener_state_ == ListenerState::Terminating;
+    });
+    if (listener_state_ == ListenerState::Terminating) {
+        callback_thread_.join();
+        listener_state_ = ListenerState::None;
+        return prompt_result_;
+    }
+    return ResponseCode::OK;
+}
+
+Return<ResponseCode>
+TrustyConfirmationUI::deliverSecureInputEvent(const HardwareAuthToken& secureInputToken) {
+    ResponseCode rc = ResponseCode::Ignored;
+    {
+        /*
+         * deliverSecureInputEvent is only used by the VTS test to mock human input. A correct
+         * implementation responds with a mock confirmation token signed with a test key. The
+         * problem is that the non interactive grace period was not formalized in the HAL spec,
+         * so that the VTS test does not account for the grace period. (It probably should.)
+         * This means we can only pass the VTS test if we block until the grace period is over
+         * (SetupDone -> Interactive) before we deliver the input event.
+         *
+         * The true secure input is delivered by a different mechanism and gets ignored -
+         * not queued - until the grace period is over.
+         *
+         */
+        std::unique_lock<std::mutex> stateLock(listener_state_lock_);
+        listener_state_condv_.wait(stateLock,
+                                   [this] { return listener_state_ != ListenerState::SetupDone; });
+
+        if (listener_state_ != ListenerState::Interactive) return ResponseCode::Ignored;
+        auto sapp = app_.lock();
+        if (!sapp) return ResponseCode::Ignored;
+        auto [error, response] =
+            sapp->issueCmd<DeliverTestCommandMessage, DeliverTestCommandResponse>(
+                static_cast<teeui::TestModeCommands>(secureInputToken.challenge));
+        if (error != TrustyAppError::OK) return ResponseCode::SystemError;
+        auto& [trc] = response;
+        if (trc != TeeuiRc::Ignored) secureInputDelivered_ = true;
+        rc = convertRc(trc);
+    }
+    if (secureInputDelivered_) listener_state_condv_.notify_all();
+    // VTS test expect an OK response if the event was successfully delivered.
+    // But since the TA returns the callback response now, we have to translate
+    // Canceled into OK. Canceled is only returned if the delivered event canceled
+    // the operation, which means that the event was successfully delivered. Thus
+    // we return OK.
+    if (rc == ResponseCode::Canceled) return ResponseCode::OK;
+    return rc;
+}
+
+Return<void> TrustyConfirmationUI::abort() {
+    {
+        std::unique_lock<std::mutex> stateLock(listener_state_lock_);
+        if (listener_state_ == ListenerState::SetupDone ||
+            listener_state_ == ListenerState::Interactive) {
+            auto sapp = app_.lock();
+            if (sapp) sapp->issueCmd<AbortMsg>();
+            abort_called_ = true;
+        }
+    }
+    listener_state_condv_.notify_all();
+    return Void();
+}
+
+android::sp<IConfirmationUI> createTrustyConfirmationUI() {
+    return new TrustyConfirmationUI();
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace confirmationui
+}  // namespace hardware
+}  // namespace android
diff --git a/trusty/confirmationui/TrustyConfirmationUI.h b/trusty/confirmationui/TrustyConfirmationUI.h
new file mode 100644
index 0000000..3a7c7ef
--- /dev/null
+++ b/trusty/confirmationui/TrustyConfirmationUI.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright 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 ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_TRUSTY_CONFIRMATIONUI_H
+#define ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_TRUSTY_CONFIRMATIONUI_H
+
+#include <android/hardware/confirmationui/1.0/IConfirmationUI.h>
+#include <android/hardware/keymaster/4.0/types.h>
+#include <hidl/Status.h>
+
+#include <atomic>
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <teeui/generic_messages.h>
+#include <thread>
+
+#include "TrustyApp.h"
+
+namespace android {
+namespace hardware {
+namespace confirmationui {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+
+using ::android::trusty::TrustyApp;
+
+class TrustyConfirmationUI : public IConfirmationUI {
+  public:
+    TrustyConfirmationUI();
+    virtual ~TrustyConfirmationUI();
+    // Methods from ::android::hardware::confirmationui::V1_0::IConfirmationUI
+    // follow.
+    Return<ResponseCode> promptUserConfirmation(const sp<IConfirmationResultCallback>& resultCB,
+                                                const hidl_string& promptText,
+                                                const hidl_vec<uint8_t>& extraData,
+                                                const hidl_string& locale,
+                                                const hidl_vec<UIOption>& uiOptions) override;
+    Return<ResponseCode> deliverSecureInputEvent(
+        const ::android::hardware::keymaster::V4_0::HardwareAuthToken& secureInputToken) override;
+    Return<void> abort() override;
+
+  private:
+    std::weak_ptr<TrustyApp> app_;
+    std::thread callback_thread_;
+
+    enum class ListenerState : uint32_t {
+        None,
+        Starting,
+        SetupDone,
+        Interactive,
+        Terminating,
+    };
+
+    /*
+     * listener_state is protected by listener_state_lock. It makes transitions between phases
+     * of the confirmation operation atomic.
+     * (See TrustyConfirmationUI.cpp#promptUserConfirmation_ for details about operation phases)
+     */
+    ListenerState listener_state_;
+    /*
+     * abort_called_ is also protected by listener_state_lock_ and indicates that the HAL user
+     * called abort.
+     */
+    bool abort_called_;
+    std::mutex listener_state_lock_;
+    std::condition_variable listener_state_condv_;
+    ResponseCode prompt_result_;
+    bool secureInputDelivered_;
+
+    std::tuple<teeui::ResponseCode, teeui::MsgVector<uint8_t>, teeui::MsgVector<uint8_t>>
+    promptUserConfirmation_(const teeui::MsgString& promptText,
+                            const teeui::MsgVector<uint8_t>& extraData,
+                            const teeui::MsgString& locale,
+                            const teeui::MsgVector<teeui::UIOption>& uiOptions);
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace confirmationui
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_TRUSTY_CONFIRMATIONUI_H
diff --git a/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.rc b/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.rc
new file mode 100644
index 0000000..dc7a03b
--- /dev/null
+++ b/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.rc
@@ -0,0 +1,4 @@
+service confirmationui-1-0 /vendor/bin/hw/android.hardware.confirmationui@1.0-service.trusty
+    class hal
+    user nobody
+    group drmrpc input
diff --git a/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.xml b/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.xml
new file mode 100644
index 0000000..9008b87
--- /dev/null
+++ b/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+    <hal format="hidl">
+        <name>android.hardware.confirmationui</name>
+        <transport>hwbinder</transport>
+        <version>1.0</version>
+        <interface>
+        <name>IConfirmationUI</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/trusty/confirmationui/include/TrustyConfirmationuiHal.h b/trusty/confirmationui/include/TrustyConfirmationuiHal.h
new file mode 100644
index 0000000..2ab9389
--- /dev/null
+++ b/trusty/confirmationui/include/TrustyConfirmationuiHal.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 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 <android/hardware/confirmationui/1.0/IConfirmationUI.h>
+
+namespace android {
+namespace hardware {
+namespace confirmationui {
+namespace V1_0 {
+namespace implementation {
+
+android::sp<IConfirmationUI> createTrustyConfirmationUI();
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace confirmationui
+}  // namespace hardware
+}  // namespace android
diff --git a/trusty/confirmationui/service.cpp b/trusty/confirmationui/service.cpp
new file mode 100644
index 0000000..dd7e84b
--- /dev/null
+++ b/trusty/confirmationui/service.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright 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 <android-base/logging.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include <TrustyConfirmationuiHal.h>
+
+using android::sp;
+using android::hardware::confirmationui::V1_0::implementation::createTrustyConfirmationUI;
+
+int main() {
+    ::android::hardware::configureRpcThreadpool(1, true /*willJoinThreadpool*/);
+    auto service = createTrustyConfirmationUI();
+    auto status = service->registerAsService();
+    if (status != android::OK) {
+        LOG(FATAL) << "Could not register service for ConfirmationUI 1.0 (" << status << ")";
+        return -1;
+    }
+    ::android::hardware::joinRpcThreadpool();
+    return -1;
+}
diff --git a/trusty/libtrusty/Android.bp b/trusty/libtrusty/Android.bp
index f6e9bee..8dba78d 100644
--- a/trusty/libtrusty/Android.bp
+++ b/trusty/libtrusty/Android.bp
@@ -12,10 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-subdirs = [
-    "tipc-test",
-]
-
 cc_library {
     name: "libtrusty",
     vendor: true,
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index 5f56408..e230941 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -48,6 +48,8 @@
         return VIRT_RPMB;
     } else if (!strcmp(dev_type_name, "sock")) {
         return SOCK_RPMB;
+    } else if (!strcmp(dev_type_name, "ufs")) {
+        return UFS_RPMB;
     } else {
         return UNKNOWN_RPMB;
     }
diff --git a/trusty/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c
index 03b1099..7dfd0d0 100644
--- a/trusty/storage/proxy/rpmb.c
+++ b/trusty/storage/proxy/rpmb.c
@@ -361,6 +361,15 @@
             return rc;
         }
         rpmb_fd = rc;
+
+        /* For UFS, it is prudent to check we have a sg device by calling an ioctl */
+        if (dev_type == UFS_RPMB) {
+            if ((ioctl(rpmb_fd, SG_GET_VERSION_NUM, &sg_version_num) < 0) ||
+                (sg_version_num < RPMB_MIN_SG_VERSION_NUM)) {
+                ALOGE("%s is not a sg device, or old sg driver\n", rpmb_devname);
+                return -1;
+            }
+        }
     } else {
         struct sockaddr_un unaddr;
         struct sockaddr *addr = (struct sockaddr *)&unaddr;
@@ -382,15 +391,6 @@
         }
     }
 
-    /* For UFS, it is prudent to check we hava a sg device by calling an ioctl */
-    if (dev_type == UFS_RPMB) {
-        if ((ioctl(rc, SG_GET_VERSION_NUM, &sg_version_num) < 0) ||
-            (sg_version_num < RPMB_MIN_SG_VERSION_NUM)) {
-            ALOGE("%s is not a sg device, or old sg driver\n", rpmb_devname);
-            return -1;
-        }
-    }
-    rpmb_fd = rc;
     return 0;
 }