Merge "String*: remove 'StaticLinkage' constructor"
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 675525c..fea8c78 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -114,66 +114,6 @@
     },
 }
 
-// libadbconnection
-// =========================================================
-// libadbconnection_client/server implement the socket handling for jdwp
-// forwarding and the track-jdwp service.
-cc_library {
-    name: "libadbconnection_server",
-    srcs: ["adbconnection/adbconnection_server.cpp"],
-
-    export_include_dirs: ["adbconnection/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/adbconnection_client.cpp"],
-
-    export_include_dirs: ["adbconnection/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: "adbconnection/libadbconnection_client.map.txt",
-        },
-    },
-    stubs: {
-        symbol_file: "adbconnection/libadbconnection_client.map.txt",
-        versions: ["1"],
-    },
-
-    host_supported: true,
-    compile_multilib: "both",
-}
-
 // libadb
 // =========================================================
 // These files are compiled for both the host and the device.
@@ -225,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"],
@@ -255,6 +197,10 @@
     },
 
     static_libs: [
+        "libadb_crypto",
+        "libadb_protos",
+        "libadb_pairing_connection",
+        "libadb_tls_connection",
         "libbase",
         "libcrypto_utils",
         "libcrypto",
@@ -264,6 +210,7 @@
         "libutils",
         "liblog",
         "libcutils",
+        "libprotobuf-cpp-lite",
     ],
 }
 
@@ -272,7 +219,12 @@
     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",
@@ -280,6 +232,8 @@
         "liblog",
         "libmdnssd",
         "libdiagnose_usb",
+        "libprotobuf-cpp-lite",
+        "libssl",
         "libusb",
     ],
 
@@ -311,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",
     ],
 }
@@ -338,6 +296,8 @@
         "client/line_printer.cpp",
         "client/fastdeploy.cpp",
         "client/fastdeploycallbacks.cpp",
+        "client/incremental.cpp",
+        "client/incremental_server.cpp",
         "shell_service_protocol.cpp",
     ],
 
@@ -347,7 +307,12 @@
     ],
 
     static_libs: [
+        "libadb_crypto",
         "libadb_host",
+	"libadb_pairing_auth",
+	"libadb_pairing_connection",
+        "libadb_protos",
+        "libadb_tls_connection",
         "libandroidfw",
         "libbase",
         "libcutils",
@@ -356,8 +321,10 @@
         "libfastdeploy_host",
         "libdiagnose_usb",
         "liblog",
+        "liblz4",
         "libmdnssd",
         "libprotobuf-cpp-lite",
+        "libssl",
         "libusb",
         "libutils",
         "liblog",
@@ -408,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: [
@@ -422,12 +390,15 @@
     ],
 
     shared_libs: [
+        "libadb_crypto",
+        "libadb_pairing_connection",
+        "libadb_protos",
+        "libadb_tls_connection",
         "libadbd_auth",
         "libasyncio",
         "libbase",
         "libcrypto",
         "libcrypto_utils",
-        "libcutils",
         "liblog",
     ],
 
@@ -451,7 +422,7 @@
     },
 }
 
-cc_library {
+cc_library_static {
     name: "libadbd_services",
     defaults: ["adbd_defaults", "host_adbd_supported"],
     recovery_available: true,
@@ -476,12 +447,16 @@
     ],
 
     shared_libs: [
+        "libadb_crypto",
+        "libadb_pairing_connection",
+        "libadb_protos",
+        "libadb_tls_connection",
         "libadbd_auth",
+        "libadbd_fs",
         "libasyncio",
         "libbase",
         "libcrypto",
         "libcrypto_utils",
-        "libcutils",
         "liblog",
     ],
 
@@ -524,14 +499,24 @@
     ],
 
     shared_libs: [
+        "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: [
@@ -566,20 +551,29 @@
         "libbase",
         "libcap",
         "libcrypto_utils",
-        "libcutils",
+        "libcutils_sockets",
         "libdiagnose_usb",
         "liblog",
         "libmdnssd",
         "libminijail",
         "libselinux",
+        "libssl",
     ],
 
     shared_libs: [
+        "libadb_crypto",
+        "libadb_pairing_connection",
+        "libadb_protos",
+        "libadb_tls_connection",
         "libadbd_auth",
+        "libadbd_fs",
         "libcrypto",
     ],
 
-    required: ["libadbd_auth"],
+    required: [
+        "libadbd_auth",
+        "libadbd_fs",
+    ],
 }
 
 phony {
@@ -651,10 +645,13 @@
     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",
@@ -765,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",
@@ -776,6 +778,7 @@
         "liblog",
         "libmdnssd",
         "libprotobuf-cpp-lite",
+        "libssl",
         "libusb",
         "libutils",
         "libziparchive",
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 7f7dd0d..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
@@ -229,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
index a6b1e78..0189455 100644
--- a/adb/apex/Android.bp
+++ b/adb/apex/Android.bp
@@ -5,10 +5,15 @@
     compile_multilib: "both",
     multilib: {
         both: {
-            native_shared_libs: ["libadbconnection_client"],
+            native_shared_libs: [
+                "libadb_pairing_auth",
+                "libadb_pairing_connection",
+                "libadb_pairing_server",
+                "libadbconnection_client",
+            ],
         },
     },
-    prebuilts: ["com.android.adbd.init.rc", "com.android.adbd.ld.config.txt"],
+    prebuilts: ["com.android.adbd.init.rc"],
 
     key: "com.android.adbd.key",
     certificate: ":com.android.adbd.certificate",
@@ -30,13 +35,6 @@
 }
 
 prebuilt_etc {
-    name: "com.android.adbd.ld.config.txt",
-    src: "ld.config.txt",
-    filename: "ld.config.txt",
-    installable: false,
-}
-
-prebuilt_etc {
     name: "com.android.adbd.init.rc",
     src: "adbd.rc",
     filename: "init.rc",
diff --git a/adb/apex/ld.config.txt b/adb/apex/ld.config.txt
deleted file mode 100644
index ca297fe..0000000
--- a/adb/apex/ld.config.txt
+++ /dev/null
@@ -1,53 +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 = apex,platform,art
-
-namespace.default.isolated = true
-namespace.default.permitted.paths = /system/${LIB}
-namespace.default.asan.permitted.paths = /system/${LIB}
-namespace.default.links = apex,art,platform
-namespace.default.link.apex.shared_libs = libcrypto.so
-namespace.default.link.art.shared_libs = libadbconnection_server.so
-
-# libcrypto.so in the APEX might be a symlink to /system, for APEXes bundled with the system image.
-# The dynamic linker works off of realpath, so we need to permit loading libcrypto.so from /system.
-namespace.default.link.platform.shared_libs = libc.so:libdl.so:libm.so:libclang_rt.hwasan-aarch64-android.so:liblog.so:libadbd_auth.so:libcrypto.so
-
-namespace.apex.isolated = true
-namespace.apex.search.paths = /apex/com.android.adbd/${LIB}
-namespace.apex.asan.search.paths = /apex/com.android.adbd/${LIB}
-namespace.apex.links = platform
-namespace.apex.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/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 2bcd0a6..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));
@@ -287,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;
@@ -296,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");
         }
     }
 
@@ -341,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]);
         }
     }
@@ -357,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;
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 c302965..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"
@@ -1637,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")) {
@@ -1959,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/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/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..22b9b18 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);
+    services->erase(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/adbconnection/.clang-format b/adb/libs/.clang-format
similarity index 100%
copy from adb/adbconnection/.clang-format
copy to adb/libs/.clang-format
diff --git a/adb/adbconnection/.clang-format b/adb/libs/adbconnection/.clang-format
similarity index 100%
rename from adb/adbconnection/.clang-format
rename to adb/libs/adbconnection/.clang-format
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/adbconnection/adbconnection_client.cpp b/adb/libs/adbconnection/adbconnection_client.cpp
similarity index 100%
rename from adb/adbconnection/adbconnection_client.cpp
rename to adb/libs/adbconnection/adbconnection_client.cpp
diff --git a/adb/adbconnection/adbconnection_server.cpp b/adb/libs/adbconnection/adbconnection_server.cpp
similarity index 100%
rename from adb/adbconnection/adbconnection_server.cpp
rename to adb/libs/adbconnection/adbconnection_server.cpp
diff --git a/adb/adbconnection/include/adbconnection/client.h b/adb/libs/adbconnection/include/adbconnection/client.h
similarity index 100%
rename from adb/adbconnection/include/adbconnection/client.h
rename to adb/libs/adbconnection/include/adbconnection/client.h
diff --git a/adb/adbconnection/include/adbconnection/server.h b/adb/libs/adbconnection/include/adbconnection/server.h
similarity index 100%
rename from adb/adbconnection/include/adbconnection/server.h
rename to adb/libs/adbconnection/include/adbconnection/server.h
diff --git a/adb/adbconnection/libadbconnection_client.map.txt b/adb/libs/adbconnection/libadbconnection_client.map.txt
similarity index 100%
rename from adb/adbconnection/libadbconnection_client.map.txt
rename to adb/libs/adbconnection/libadbconnection_client.map.txt
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/errno.cpp b/adb/sysdeps/errno.cpp
index 9a37ea2..e6af68b 100644
--- a/adb/sysdeps/errno.cpp
+++ b/adb/sysdeps/errno.cpp
@@ -24,7 +24,7 @@
 
 #include "adb.h"
 
-// Use the linux asm-generic values for errno (which are used on all android archs but mips).
+// Use the linux asm-generic values for errno (which are used on all android architectures).
 #define ERRNO_VALUES()             \
     ERRNO_VALUE(EACCES, 13);       \
     ERRNO_VALUE(EEXIST, 17);       \
@@ -48,7 +48,7 @@
     ERRNO_VALUE(ETXTBSY, 26)
 
 // Make sure these values are actually correct.
-#if defined(__linux__) && !defined(__mips__)
+#if defined(__linux__)
 #define ERRNO_VALUE(error_name, wire_value) static_assert((error_name) == (wire_value), "")
 ERRNO_VALUES();
 #undef ERRNO_VALUE
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/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 a32959b..25c74f2 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -128,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/include/android-base/macros.h b/base/include/android-base/macros.h
index 5abf514..546b2ec 100644
--- a/base/include/android-base/macros.h
+++ b/base/include/android-base/macros.h
@@ -143,8 +143,4 @@
 #define ABI_STRING "x86"
 #elif defined(__x86_64__)
 #define ABI_STRING "x86_64"
-#elif defined(__mips__) && !defined(__LP64__)
-#define ABI_STRING "mips"
-#elif defined(__mips__) && defined(__LP64__)
-#define ABI_STRING "mips64"
 #endif
diff --git a/base/include/android-base/result.h b/base/include/android-base/result.h
index 4a59552..5e65876 100644
--- a/base/include/android-base/result.h
+++ b/base/include/android-base/result.h
@@ -191,11 +191,6 @@
   return ErrorCode(code, args...);
 }
 
-// TODO(tomcherry): Remove this once we've removed all `using android::base::Errorf` and `using
-// android::base::ErrnoErrorf` lines.
-enum Errorf {};
-enum ErrnoErrorf {};
-
 template <typename T, typename... Args>
 inline Error ErrorfImpl(const T&& fmt, const Args&... args) {
   return Error(false, ErrorCode(0, args...), fmt::format(fmt, args...));
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 9ffe5dd..a9c1676 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -870,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";
@@ -1059,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.
@@ -1233,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 {
@@ -1241,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
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 c8df3e3..f28c778 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -171,6 +171,7 @@
 
     srcs: [
         "libdebuggerd/backtrace.cpp",
+        "libdebuggerd/gwp_asan.cpp",
         "libdebuggerd/open_files_list.cpp",
         "libdebuggerd/tombstone.cpp",
         "libdebuggerd/utility.cpp",
@@ -181,7 +182,10 @@
 
     // Needed for private/bionic_fdsan.h
     include_dirs: ["bionic/libc"],
-    header_libs: ["bionic_libc_platform_headers"],
+    header_libs: [
+        "bionic_libc_platform_headers",
+        "gwp_asan_headers",
+    ],
 
     static_libs: [
         "libdexfile_support_static",  // libunwindstack dependency
@@ -192,6 +196,8 @@
         "liblog",
     ],
 
+    whole_static_libs: ["gwp_asan_crash_handler"],
+
     target: {
         recovery: {
             exclude_static_libs: [
@@ -246,10 +252,12 @@
 
     static_libs: [
         "libdebuggerd",
+        "libgmock",
     ],
 
     header_libs: [
         "bionic_libc_platform_headers",
+        "gwp_asan_headers",
     ],
 
     local_include_dirs: [
@@ -355,3 +363,49 @@
 
     init_rc: ["tombstoned/tombstoned.rc"],
 }
+
+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_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 e8f366f..3e99880 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -255,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)));
@@ -272,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;
@@ -284,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;
@@ -416,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);
@@ -477,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 {
@@ -592,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 e86f499..61c5395 100644
--- a/debuggerd/crasher/Android.bp
+++ b/debuggerd/crasher/Android.bp
@@ -24,12 +24,6 @@
         arm64: {
             srcs: ["arm64/crashglue.S"],
         },
-        mips: {
-            srcs: ["mips/crashglue.S"],
-        },
-        mips64: {
-            srcs: ["mips64/crashglue.S"],
-        },
         x86: {
             srcs: ["x86/crashglue.S"],
         },
diff --git a/debuggerd/crasher/mips/crashglue.S b/debuggerd/crasher/mips/crashglue.S
deleted file mode 100644
index 70a6641..0000000
--- a/debuggerd/crasher/mips/crashglue.S
+++ /dev/null
@@ -1,48 +0,0 @@
-	.set	noat
-
-	.globl crash1
-	.globl crashnostack
-
-crash1:
-	li	$0,0xdead0000+0
-	li	$1,0xdead0000+1
-	li	$2,0xdead0000+2
-	li	$3,0xdead0000+3
-	li	$4,0xdead0000+4
-	li	$5,0xdead0000+5
-	li	$6,0xdead0000+6
-	li	$7,0xdead0000+7
-	li	$8,0xdead0000+8
-	li	$9,0xdead0000+9
-	li	$10,0xdead0000+10
-	li	$11,0xdead0000+11
-	li	$12,0xdead0000+12
-	li	$13,0xdead0000+13
-	li	$14,0xdead0000+14
-	li	$15,0xdead0000+15
-	li	$16,0xdead0000+16
-	li	$17,0xdead0000+17
-	li	$18,0xdead0000+18
-	li	$19,0xdead0000+19
-	li	$20,0xdead0000+20
-	li	$21,0xdead0000+21
-	li	$22,0xdead0000+22
-	li	$23,0xdead0000+23
-	li	$24,0xdead0000+24
-	li	$25,0xdead0000+25
-	li	$26,0xdead0000+26
-	li	$27,0xdead0000+27
-	li	$28,0xdead0000+28
-	# don't trash the stack otherwise the signal handler won't run
-	#li	$29,0xdead0000+29
-	li	$30,0xdead0000+30
-	li	$31,0xdead0000+31
-
-	lw	$zero,($0)
-	b .
-
-
-crashnostack:
-	li	$sp, 0
-	lw	$zero,($0)
-	b .
diff --git a/debuggerd/crasher/mips64/crashglue.S b/debuggerd/crasher/mips64/crashglue.S
deleted file mode 100644
index 70a6641..0000000
--- a/debuggerd/crasher/mips64/crashglue.S
+++ /dev/null
@@ -1,48 +0,0 @@
-	.set	noat
-
-	.globl crash1
-	.globl crashnostack
-
-crash1:
-	li	$0,0xdead0000+0
-	li	$1,0xdead0000+1
-	li	$2,0xdead0000+2
-	li	$3,0xdead0000+3
-	li	$4,0xdead0000+4
-	li	$5,0xdead0000+5
-	li	$6,0xdead0000+6
-	li	$7,0xdead0000+7
-	li	$8,0xdead0000+8
-	li	$9,0xdead0000+9
-	li	$10,0xdead0000+10
-	li	$11,0xdead0000+11
-	li	$12,0xdead0000+12
-	li	$13,0xdead0000+13
-	li	$14,0xdead0000+14
-	li	$15,0xdead0000+15
-	li	$16,0xdead0000+16
-	li	$17,0xdead0000+17
-	li	$18,0xdead0000+18
-	li	$19,0xdead0000+19
-	li	$20,0xdead0000+20
-	li	$21,0xdead0000+21
-	li	$22,0xdead0000+22
-	li	$23,0xdead0000+23
-	li	$24,0xdead0000+24
-	li	$25,0xdead0000+25
-	li	$26,0xdead0000+26
-	li	$27,0xdead0000+27
-	li	$28,0xdead0000+28
-	# don't trash the stack otherwise the signal handler won't run
-	#li	$29,0xdead0000+29
-	li	$30,0xdead0000+30
-	li	$31,0xdead0000+31
-
-	lw	$zero,($0)
-	b .
-
-
-crashnostack:
-	li	$sp, 0
-	lw	$zero,($0)
-	b .
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index f8192b5..8b4b630 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -49,6 +49,7 @@
 #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>
@@ -298,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.
@@ -342,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) {
@@ -485,6 +490,8 @@
   }
 
   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 == BIONIC_SIGNAL_DEBUGGER) {
     if (info->si_code == SI_QUEUE && info->si_pid == __getpid()) {
@@ -499,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
@@ -532,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.
diff --git a/debuggerd/include/debuggerd/handler.h b/debuggerd/include/debuggerd/handler.h
index cd6fc05..4f24360 100644
--- a/debuggerd/include/debuggerd/handler.h
+++ b/debuggerd/include/debuggerd/handler.h
@@ -24,11 +24,20 @@
 
 __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);
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 b33adf31..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() {
@@ -359,3 +365,131 @@
   dump_timestamp(&log_, 0);
   ASSERT_STREQ("Timestamp: 1970-01-01 00:00:00+0000\n", amfd_data_.c_str());
 }
+
+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_);
+  }
+
+  void SetCrashAddress(uintptr_t crash_address) {
+    crash_address_ = crash_address;
+  }
+};
+
+TEST_F(TombstoneTest, gwp_asan_cause_uaf_exact) {
+  gwp_asan::AllocationMetadata meta;
+  meta.Addr = 0x1000;
+  meta.Size = 32;
+
+  GwpAsanCrashDataTest crash_data(gwp_asan::Error::USE_AFTER_FREE, &meta);
+  crash_data.SetCrashAddress(0x1000);
+
+  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\\]: Use After Free on a 32-byte "
+                           "allocation at 0x[a-fA-F0-9]+\n"));
+}
+
+TEST_F(TombstoneTest, gwp_asan_cause_double_free) {
+  gwp_asan::AllocationMetadata meta;
+  meta.Addr = 0x1000;
+  meta.Size = 32;
+
+  GwpAsanCrashDataTest crash_data(gwp_asan::Error::DOUBLE_FREE, &meta);
+  crash_data.SetCrashAddress(0x1000);
+
+  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\\]: Double Free on a 32-byte "
+                           "allocation at 0x[a-fA-F0-9]+\n"));
+}
+
+TEST_F(TombstoneTest, gwp_asan_cause_overflow) {
+  gwp_asan::AllocationMetadata meta;
+  meta.Addr = 0x1000;
+  meta.Size = 32;
+
+  GwpAsanCrashDataTest crash_data(gwp_asan::Error::BUFFER_OVERFLOW, &meta);
+  crash_data.SetCrashAddress(0x1025);
+
+  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\\]: Buffer Overflow, 5 bytes right of a 32-byte "
+          "allocation at 0x[a-fA-F0-9]+\n"));
+}
+
+TEST_F(TombstoneTest, gwp_asan_cause_underflow) {
+  gwp_asan::AllocationMetadata meta;
+  meta.Addr = 0x1000;
+  meta.Size = 32;
+
+  GwpAsanCrashDataTest crash_data(gwp_asan::Error::BUFFER_UNDERFLOW, &meta);
+  crash_data.SetCrashAddress(0xffe);
+
+  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\\]: 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 4e7f35c..fd52e81 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -53,9 +53,13 @@
 #include <unwindstack/Unwinder.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;
@@ -372,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");
@@ -381,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) {
@@ -402,6 +413,14 @@
   }
 
   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());
@@ -583,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);
 
@@ -607,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);
@@ -618,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/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/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index b27126b..bd35815 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"
 
@@ -126,6 +127,7 @@
     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) {
@@ -234,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) {
@@ -256,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 */
@@ -335,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;
 }
 
@@ -376,7 +379,7 @@
         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;
@@ -418,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;
@@ -462,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;
@@ -499,7 +502,7 @@
     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;
@@ -535,13 +538,56 @@
     LINFO << "Enabling ext4 casefold on " << blk_device;
 
     const char* argv[] = {TUNE2FS_BIN, "-O", "casefold", "-E", "encoding=utf8", 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 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.
+    const char* tune2fs_args[] = {TUNE2FS_BIN, "-O",        "metadata_csum,64bit,extent",
+                                  "-T",        "now", 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
@@ -632,7 +678,7 @@
 
     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)) {
@@ -640,6 +686,7 @@
             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);
         }
     }
 
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index a8c2cc1..7be024f 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -58,7 +58,7 @@
 }
 
 static int format_ext4(const std::string& fs_blkdev, const std::string& fs_mnt_point,
-                       bool crypt_footer, bool needs_projid) {
+                       bool crypt_footer, bool needs_projid, bool needs_metadata_csum) {
     uint64_t dev_sz;
     int rc = 0;
 
@@ -83,11 +83,26 @@
     }
     // 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, true,
-                             nullptr);
+    rc = logwrap_fork_execvp(mke2fs_args.size(), mke2fs_args.data(), nullptr, false, LOG_KLOG,
+                             false, nullptr);
     if (rc) {
         LERROR << "mke2fs returned " << rc;
         return rc;
@@ -97,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;
     }
@@ -135,7 +150,7 @@
     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, true, nullptr);
+    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) {
@@ -153,7 +168,8 @@
         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, needs_projid);
+        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 561d994..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() == '%') {
@@ -277,9 +278,9 @@
         } else if (StartsWith(flag, "keydirectory=")) {
             // The metadata flag is followed by an = and the directory for the keys.
             entry->metadata_key_dir = arg;
-        } else if (StartsWith(flag, "metadata_cipher=")) {
-            // Specify the cipher to use for metadata encryption
-            entry->metadata_cipher = 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;
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 2bc53d3..1fa1aa1 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -1026,6 +1026,8 @@
         }
 
         if (change) *change = true;
+    } else if (scratch_device->empty()) {
+        *scratch_device = GetBootScratchDevice();
     }
     return true;
 }
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_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 4dc09c1..05e18fa 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -38,7 +38,7 @@
     std::string fs_options;
     std::string key_loc;
     std::string metadata_key_dir;
-    std::string metadata_cipher;
+    std::string metadata_encryption;
     off64_t length = 0;
     std::string label;
     int partnum = -1;
@@ -83,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/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
index d7b689e..6461788 100644
--- a/fs_mgr/libdm/dm_target.cpp
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -280,6 +280,7 @@
         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()));
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index affdd29..67af59a 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -526,13 +526,18 @@
     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,
-                              is_legacy, !is_legacy);
+    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());
     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");
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index e3dd92b..d2e50d3 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -280,20 +280,20 @@
 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 is_legacy, bool set_dun)
+                       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),
-          is_legacy_(is_legacy),
-          set_dun_(set_dun) {}
+          start_sector_(start_sector) {}
 
     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_;
@@ -301,8 +301,9 @@
     std::string key_;
     std::string blockdev_;
     uint64_t start_sector_;
-    bool is_legacy_;
-    bool set_dun_;
+    bool is_legacy_ = false;
+    bool set_dun_ = false;
+    bool is_hw_wrapped_ = false;
 };
 
 }  // namespace dm
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 ed623bc..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,6 +267,18 @@
     return avb_handle;
 }
 
+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.
@@ -275,7 +288,7 @@
     }
 
     // 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;
@@ -364,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) {
@@ -381,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_);
 
@@ -405,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/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index adfee1d..d274ba4 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -72,6 +72,7 @@
         "android/snapshot/snapshot.proto",
         "device_info.cpp",
         "snapshot.cpp",
+        "snapshot_stats.cpp",
         "snapshot_metadata_updater.cpp",
         "partition_cow_creator.cpp",
         "return.cpp",
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index a3a518d..2ac0c44 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -131,3 +131,13 @@
     // 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/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index ed92dd7..b440c71 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -91,6 +91,8 @@
     using MergeStatus = android::hardware::boot::V1_1::MergeStatus;
     using FiemapStatus = android::fiemap::FiemapStatus;
 
+    friend class SnapshotMergeStats;
+
   public:
     // Dependency injection for testing.
     class IDeviceInfo {
@@ -177,7 +179,7 @@
     //   - 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.
@@ -395,6 +397,10 @@
     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);
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index a937b43..187f24c 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -46,6 +46,7 @@
 #include "device_info.h"
 #include "partition_cow_creator.h"
 #include "snapshot_metadata_updater.h"
+#include "snapshot_stats.h"
 #include "utility.h"
 
 namespace android {
@@ -232,7 +233,12 @@
     LOG(WARNING) << callstack_str.c_str();
     std::stringstream path;
     path << "/data/misc/snapshotctl_log/libsnapshot." << Now() << ".log";
-    android::base::WriteStringToFile(callstack_str.c_str(), path.str());
+    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)) {
@@ -1734,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_;
 }
@@ -2342,6 +2352,9 @@
 
     ss << "Current slot: " << device_->GetSlotSuffix() << std::endl;
     ss << "Boot indicator: booting from " << GetCurrentSlot() << " slot" << std::endl;
+    ss << "Rollback indicator: "
+       << (access(GetRollbackIndicatorPath().c_str(), F_OK) == 0 ? "exists" : strerror(errno))
+       << std::endl;
 
     bool ok = true;
     std::vector<std::string> snapshots;
@@ -2378,7 +2391,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.
@@ -2388,6 +2401,8 @@
         }
     }
 
+    SnapshotMergeStats merge_stats(*this);
+
     unsigned int last_progress = 0;
     auto callback = [&]() -> void {
         double progress;
@@ -2400,7 +2415,9 @@
 
     LOG(INFO) << "Waiting for any previous merge request to complete. "
               << "This can take up to several minutes.";
+    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;
@@ -2410,6 +2427,11 @@
             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;
@@ -2418,9 +2440,13 @@
         LOG(INFO) << "Waiting for merge to complete. This can take up to several minutes.";
         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;
 }
 
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 d87274d..5d2840f 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -1767,6 +1767,7 @@
   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>();
@@ -1774,6 +1775,7 @@
     }
     void TearDown() override {
         if (!is_virtual_ab_) return;
+        return;  // BUG(149738928)
 
         EXPECT_TRUE(!image_manager_->BackingImageExists(kImageName) ||
                     image_manager_->DeleteBackingImage(kImageName));
@@ -1808,10 +1810,6 @@
     std::vector<uint64_t> ret;
     for (uint64_t size = 1_MiB; size <= 512_MiB; size *= 2) {
         ret.push_back(size);
-#ifdef SKIP_TEST_IN_PRESUBMIT
-        // BUG(148889015);
-        break;
-#endif
     }
     return ret;
 }
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index d724be3..e35ad4b 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -61,7 +61,16 @@
         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, 0660)));
+                     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_)); }
@@ -108,7 +117,8 @@
 
     // 'snapshotctl merge' is stripped away from arguments to
     // Logger.
-    android::base::InitLogging(argv, MergeCmdLogger(argc - 2, argv + 2));
+    android::base::InitLogging(argv);
+    android::base::SetLogger(MergeCmdLogger(argc - 2, argv + 2));
 
     auto state = SnapshotManager::New()->InitiateMergeAndWait();
 
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index e364436..cf324fe 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -902,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
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 800ad7e..69b3150 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -895,11 +895,11 @@
     EXPECT_EQ("/dir/key", entry->metadata_key_dir);
 }
 
-TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_MetadataCipher) {
+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_cipher=adiantum
+source none0       swap   defaults      keydirectory=/dir/key,metadata_encryption=adiantum
 )fs";
 
     ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
@@ -909,7 +909,28 @@
     ASSERT_EQ(1U, fstab.size());
 
     auto entry = fstab.begin();
-    EXPECT_EQ("adiantum", entry->metadata_cipher);
+    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 7caf468..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;
@@ -134,13 +137,13 @@
             {"Normal", BatteryCapacityLevel::NORMAL},
             {"High", BatteryCapacityLevel::HIGH},
             {"Full", BatteryCapacityLevel::FULL},
-            {NULL, BatteryCapacityLevel::UNKNOWN},
+            {NULL, BatteryCapacityLevel::UNSUPPORTED},
     };
 
     auto ret = mapSysfsString(capacityLevel, batteryCapacityLevelMap);
     if (!ret) {
-        KLOG_WARNING(LOG_TAG, "Unknown battery capacity level '%s'\n", capacityLevel);
-        *ret = BatteryCapacityLevel::UNKNOWN;
+        KLOG_WARNING(LOG_TAG, "Unsupported battery capacity level '%s'\n", capacityLevel);
+        *ret = BatteryCapacityLevel::UNSUPPORTED;
     }
 
     return *ret;
@@ -265,7 +268,9 @@
         mHealthInfo->batteryChargeTimeToFullNowSeconds =
                 getIntField(mHealthdConfig->batteryChargeTimeToFullNowPath);
 
-    mHealthInfo->batteryFullCapacityUah = props.batteryFullCharge;
+    if (!mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty())
+        mHealthInfo->batteryFullChargeDesignCapacityUah =
+                getIntField(mHealthdConfig->batteryFullChargeDesignCapacityUahPath);
 
     props.batteryTemperature = mBatteryFixedTemperature ?
         mBatteryFixedTemperature :
@@ -622,6 +627,13 @@
                         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",
@@ -694,6 +706,8 @@
             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/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/include/healthd/healthd.h b/healthd/include/healthd/healthd.h
index 8ffb114..706c332 100644
--- a/healthd/include/healthd/healthd.h
+++ b/healthd/include/healthd/healthd.h
@@ -71,6 +71,7 @@
     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 f28934e..52628f3 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -28,6 +28,7 @@
     "rlimit_parser.cpp",
     "service.cpp",
     "service_list.cpp",
+    "service_lock.cpp",
     "service_parser.cpp",
     "service_utils.cpp",
     "subcontext.cpp",
@@ -81,6 +82,7 @@
         "-Wextra",
         "-Wno-unused-parameter",
         "-Werror",
+        "-Wthread-safety",
         "-DALLOW_FIRST_STAGE_CONSOLE=0",
         "-DALLOW_LOCAL_PROP_OVERRIDE=0",
         "-DALLOW_PERMISSIVE_SELINUX=0",
@@ -88,6 +90,7 @@
         "-DWORLD_WRITABLE_KMSG=0",
         "-DDUMP_ON_UMOUNT_FAILURE=0",
         "-DSHUTDOWN_ZERO_TIMEOUT=0",
+        "-DINIT_FULL_SOURCES",
     ],
     product_variables: {
         debuggable: {
@@ -267,6 +270,37 @@
     static_libs: ["libinit"],
 }
 
+cc_defaults {
+    name: "libinit_test_utils_libraries_defaults",
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "libselinux",
+        "libhidl-gen-utils",
+        "liblog",
+        "libprocessgroup",
+        "libprotobuf-cpp-lite",
+    ],
+}
+
+cc_library_static {
+    name: "libinit_test_utils",
+    defaults: ["libinit_test_utils_libraries_defaults"],
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Wno-unused-parameter",
+        "-Werror",
+    ],
+    srcs: init_common_sources + [
+        "test_utils/service_utils.cpp",
+    ],
+    whole_static_libs: [
+        "libcap",
+    ],
+    export_include_dirs: ["test_utils/include"], // for tests
+}
+
 // Host Verifier
 // ------------------------------------------------------------------------------
 
diff --git a/init/action_manager.cpp b/init/action_manager.cpp
index ebca762..b45f5cd 100644
--- a/init/action_manager.cpp
+++ b/init/action_manager.cpp
@@ -41,10 +41,12 @@
 }
 
 void ActionManager::QueueEventTrigger(const std::string& trigger) {
+    auto lock = std::lock_guard{event_queue_lock_};
     event_queue_.emplace(trigger);
 }
 
 void ActionManager::QueuePropertyChange(const std::string& name, const std::string& value) {
+    auto lock = std::lock_guard{event_queue_lock_};
     event_queue_.emplace(std::make_pair(name, value));
 }
 
@@ -53,6 +55,7 @@
 }
 
 void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
+    auto lock = std::lock_guard{event_queue_lock_};
     auto action = std::make_unique<Action>(true, nullptr, "<Builtin Action>", 0, name,
                                            std::map<std::string, std::string>{});
     action->AddCommand(std::move(func), {name}, 0);
@@ -62,15 +65,18 @@
 }
 
 void ActionManager::ExecuteOneCommand() {
-    // Loop through the event queue until we have an action to execute
-    while (current_executing_actions_.empty() && !event_queue_.empty()) {
-        for (const auto& action : actions_) {
-            if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },
-                           event_queue_.front())) {
-                current_executing_actions_.emplace(action.get());
+    {
+        auto lock = std::lock_guard{event_queue_lock_};
+        // Loop through the event queue until we have an action to execute
+        while (current_executing_actions_.empty() && !event_queue_.empty()) {
+            for (const auto& action : actions_) {
+                if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },
+                               event_queue_.front())) {
+                    current_executing_actions_.emplace(action.get());
+                }
             }
+            event_queue_.pop();
         }
-        event_queue_.pop();
     }
 
     if (current_executing_actions_.empty()) {
@@ -103,6 +109,7 @@
 }
 
 bool ActionManager::HasMoreCommands() const {
+    auto lock = std::lock_guard{event_queue_lock_};
     return !current_executing_actions_.empty() || !event_queue_.empty();
 }
 
@@ -113,6 +120,7 @@
 }
 
 void ActionManager::ClearQueue() {
+    auto lock = std::lock_guard{event_queue_lock_};
     // We are shutting down so don't claim the oneshot builtin actions back
     current_executing_actions_ = {};
     event_queue_ = {};
diff --git a/init/action_manager.h b/init/action_manager.h
index a2b95ac..b6f93d9 100644
--- a/init/action_manager.h
+++ b/init/action_manager.h
@@ -16,9 +16,12 @@
 
 #pragma once
 
+#include <mutex>
 #include <string>
 #include <vector>
 
+#include <android-base/thread_annotations.h>
+
 #include "action.h"
 #include "builtins.h"
 
@@ -48,7 +51,9 @@
     void operator=(ActionManager const&) = delete;
 
     std::vector<std::unique_ptr<Action>> actions_;
-    std::queue<std::variant<EventTrigger, PropertyChange, BuiltinAction>> event_queue_;
+    std::queue<std::variant<EventTrigger, PropertyChange, BuiltinAction>> event_queue_
+            GUARDED_BY(event_queue_lock_);
+    mutable std::mutex event_queue_lock_;
     std::queue<const Action*> current_executing_actions_;
     std::size_t current_command_;
 };
diff --git a/init/action_parser.cpp b/init/action_parser.cpp
index f316871..52f6a1f 100644
--- a/init/action_parser.cpp
+++ b/init/action_parser.cpp
@@ -21,7 +21,7 @@
 #include <android-base/properties.h>
 #include <android-base/strings.h>
 
-#if defined(__ANDROID__)
+#ifdef INIT_FULL_SOURCES
 #include "property_service.h"
 #include "selinux.h"
 #else
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 200bfff..dd5af72 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -151,6 +151,7 @@
 
 template <typename F>
 static void ForEachServiceInClass(const std::string& classname, F function) {
+    auto lock = std::lock_guard{service_lock};
     for (const auto& service : ServiceList::GetInstance()) {
         if (service->classnames().count(classname)) std::invoke(function, service);
     }
@@ -162,6 +163,7 @@
         return {};
     // Starting a class does not start services which are explicitly disabled.
     // They must  be started individually.
+    auto lock = std::lock_guard{service_lock};
     for (const auto& service : ServiceList::GetInstance()) {
         if (service->classnames().count(args[1])) {
             if (auto result = service->StartIfNotDisabled(); !result.ok()) {
@@ -184,6 +186,7 @@
         // stopped either.
         return {};
     }
+    auto lock = std::lock_guard{service_lock};
     for (const auto& service : ServiceList::GetInstance()) {
         if (service->classnames().count(args[1])) {
             if (auto result = service->StartIfPostData(); !result.ok()) {
@@ -234,6 +237,7 @@
 }
 
 static Result<void> do_enable(const BuiltinArguments& args) {
+    auto lock = std::lock_guard{service_lock};
     Service* svc = ServiceList::GetInstance().FindService(args[1]);
     if (!svc) return Error() << "Could not find service";
 
@@ -245,6 +249,7 @@
 }
 
 static Result<void> do_exec(const BuiltinArguments& args) {
+    auto lock = std::lock_guard{service_lock};
     auto service = Service::MakeTemporaryOneshotService(args.args);
     if (!service.ok()) {
         return Error() << "Could not create exec service: " << service.error();
@@ -258,6 +263,7 @@
 }
 
 static Result<void> do_exec_background(const BuiltinArguments& args) {
+    auto lock = std::lock_guard{service_lock};
     auto service = Service::MakeTemporaryOneshotService(args.args);
     if (!service.ok()) {
         return Error() << "Could not create exec background service: " << service.error();
@@ -271,6 +277,7 @@
 }
 
 static Result<void> do_exec_start(const BuiltinArguments& args) {
+    auto lock = std::lock_guard{service_lock};
     Service* service = ServiceList::GetInstance().FindService(args[1]);
     if (!service) {
         return Error() << "Service not found";
@@ -340,6 +347,7 @@
 }
 
 static Result<void> do_interface_restart(const BuiltinArguments& args) {
+    auto lock = std::lock_guard{service_lock};
     Service* svc = ServiceList::GetInstance().FindInterface(args[1]);
     if (!svc) return Error() << "interface " << args[1] << " not found";
     svc->Restart();
@@ -347,6 +355,7 @@
 }
 
 static Result<void> do_interface_start(const BuiltinArguments& args) {
+    auto lock = std::lock_guard{service_lock};
     Service* svc = ServiceList::GetInstance().FindInterface(args[1]);
     if (!svc) return Error() << "interface " << args[1] << " not found";
     if (auto result = svc->Start(); !result.ok()) {
@@ -356,6 +365,7 @@
 }
 
 static Result<void> do_interface_stop(const BuiltinArguments& args) {
+    auto lock = std::lock_guard{service_lock};
     Service* svc = ServiceList::GetInstance().FindInterface(args[1]);
     if (!svc) return Error() << "interface " << args[1] << " not found";
     svc->Stop();
@@ -740,6 +750,7 @@
 }
 
 static Result<void> do_start(const BuiltinArguments& args) {
+    auto lock = std::lock_guard{service_lock};
     Service* svc = ServiceList::GetInstance().FindService(args[1]);
     if (!svc) return Error() << "service " << args[1] << " not found";
     if (auto result = svc->Start(); !result.ok()) {
@@ -749,6 +760,7 @@
 }
 
 static Result<void> do_stop(const BuiltinArguments& args) {
+    auto lock = std::lock_guard{service_lock};
     Service* svc = ServiceList::GetInstance().FindService(args[1]);
     if (!svc) return Error() << "service " << args[1] << " not found";
     svc->Stop();
@@ -756,6 +768,7 @@
 }
 
 static Result<void> do_restart(const BuiltinArguments& args) {
+    auto lock = std::lock_guard{service_lock};
     Service* svc = ServiceList::GetInstance().FindService(args[1]);
     if (!svc) return Error() << "service " << args[1] << " not found";
     svc->Restart();
@@ -1111,6 +1124,7 @@
             function(StringPrintf("Exec service failed, status %d", siginfo.si_status));
         }
     });
+    auto lock = std::lock_guard{service_lock};
     if (auto result = (*service)->ExecStart(); !result.ok()) {
         function("ExecStart failed: " + result.error().message());
     }
@@ -1250,6 +1264,7 @@
         }
         success &= parser.ParseConfigFile(c);
     }
+    auto lock = std::lock_guard{service_lock};
     ServiceList::GetInstance().MarkServicesUpdate();
     if (success) {
         return {};
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 21663e6..622e457 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -21,6 +21,7 @@
 #include <unistd.h>
 
 #include <chrono>
+#include <filesystem>
 #include <map>
 #include <memory>
 #include <set>
@@ -99,7 +100,11 @@
     void GetDmLinearMetadataDevice(std::set<std::string>* devices);
     bool InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata);
     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);
 
@@ -595,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;
@@ -663,6 +673,27 @@
     return true;
 }
 
+// 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;
 
diff --git a/init/init.cpp b/init/init.cpp
index 5bf1b36..bfef9e5 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -33,7 +33,9 @@
 #include <functional>
 #include <map>
 #include <memory>
+#include <mutex>
 #include <optional>
+#include <thread>
 #include <vector>
 
 #include <android-base/chrono_utils.h>
@@ -95,15 +97,148 @@
 static int signal_fd = -1;
 static int property_fd = -1;
 
-static std::unique_ptr<Timer> waiting_for_prop(nullptr);
-static std::string wait_prop_name;
-static std::string wait_prop_value;
-static std::string shutdown_command;
-static bool do_shutdown = false;
-
 static std::unique_ptr<Subcontext> subcontext;
 
+// Init epolls various FDs to wait for various inputs.  It previously waited on property changes
+// with a blocking socket that contained the information related to the change, however, it was easy
+// to fill that socket and deadlock the system.  Now we use locks to handle the property changes
+// directly in the property thread, however we still must wake the epoll to inform init that there
+// is a change to process, so we use this FD.  It is non-blocking, since we do not care how many
+// times WakeEpoll() is called, only that the epoll will wake.
+static int wake_epoll_fd = -1;
+static void InstallInitNotifier(Epoll* epoll) {
+    int sockets[2];
+    if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, sockets) != 0) {
+        PLOG(FATAL) << "Failed to socketpair() between property_service and init";
+    }
+    int epoll_fd = sockets[0];
+    wake_epoll_fd = sockets[1];
+
+    auto drain_socket = [epoll_fd] {
+        char buf[512];
+        while (read(epoll_fd, buf, sizeof(buf)) > 0) {
+        }
+    };
+
+    if (auto result = epoll->RegisterHandler(epoll_fd, drain_socket); !result) {
+        LOG(FATAL) << result.error();
+    }
+}
+
+static void WakeEpoll() {
+    constexpr char value[] = "1";
+    write(wake_epoll_fd, value, sizeof(value));
+}
+
+static class PropWaiterState {
+  public:
+    bool StartWaiting(const char* name, const char* value) {
+        auto lock = std::lock_guard{lock_};
+        if (waiting_for_prop_) {
+            return false;
+        }
+        if (GetProperty(name, "") != value) {
+            // Current property value is not equal to expected value
+            wait_prop_name_ = name;
+            wait_prop_value_ = value;
+            waiting_for_prop_.reset(new Timer());
+        } else {
+            LOG(INFO) << "start_waiting_for_property(\"" << name << "\", \"" << value
+                      << "\"): already set";
+        }
+        return true;
+    }
+
+    void ResetWaitForProp() {
+        auto lock = std::lock_guard{lock_};
+        ResetWaitForPropLocked();
+    }
+
+    void CheckAndResetWait(const std::string& name, const std::string& value) {
+        auto lock = std::lock_guard{lock_};
+        // We always record how long init waited for ueventd to tell us cold boot finished.
+        // If we aren't waiting on this property, it means that ueventd finished before we even
+        // started to wait.
+        if (name == kColdBootDoneProp) {
+            auto time_waited = waiting_for_prop_ ? waiting_for_prop_->duration().count() : 0;
+            std::thread([time_waited] {
+                SetProperty("ro.boottime.init.cold_boot_wait", std::to_string(time_waited));
+            }).detach();
+        }
+
+        if (waiting_for_prop_) {
+            if (wait_prop_name_ == name && wait_prop_value_ == value) {
+                LOG(INFO) << "Wait for property '" << wait_prop_name_ << "=" << wait_prop_value_
+                          << "' took " << *waiting_for_prop_;
+                ResetWaitForPropLocked();
+                WakeEpoll();
+            }
+        }
+    }
+
+    // This is not thread safe because it releases the lock when it returns, so the waiting state
+    // may change.  However, we only use this function to prevent running commands in the main
+    // thread loop when we are waiting, so we do not care about false positives; only false
+    // negatives.  StartWaiting() and this function are always called from the same thread, so false
+    // negatives are not possible and therefore we're okay.
+    bool MightBeWaiting() {
+        auto lock = std::lock_guard{lock_};
+        return static_cast<bool>(waiting_for_prop_);
+    }
+
+  private:
+    void ResetWaitForPropLocked() {
+        wait_prop_name_.clear();
+        wait_prop_value_.clear();
+        waiting_for_prop_.reset();
+    }
+
+    std::mutex lock_;
+    std::unique_ptr<Timer> waiting_for_prop_{nullptr};
+    std::string wait_prop_name_;
+    std::string wait_prop_value_;
+
+} prop_waiter_state;
+
+bool start_waiting_for_property(const char* name, const char* value) {
+    return prop_waiter_state.StartWaiting(name, value);
+}
+
+void ResetWaitForProp() {
+    prop_waiter_state.ResetWaitForProp();
+}
+
+static class ShutdownState {
+  public:
+    void TriggerShutdown(const std::string& command) {
+        // We can't call HandlePowerctlMessage() directly in this function,
+        // because it modifies the contents of the action queue, which can cause the action queue
+        // to get into a bad state if this function is called from a command being executed by the
+        // action queue.  Instead we set this flag and ensure that shutdown happens before the next
+        // command is run in the main init loop.
+        auto lock = std::lock_guard{shutdown_command_lock_};
+        shutdown_command_ = command;
+        do_shutdown_ = true;
+        WakeEpoll();
+    }
+
+    std::optional<std::string> CheckShutdown() {
+        auto lock = std::lock_guard{shutdown_command_lock_};
+        if (do_shutdown_ && !IsShuttingDown()) {
+            do_shutdown_ = false;
+            return shutdown_command_;
+        }
+        return {};
+    }
+
+  private:
+    std::mutex shutdown_command_lock_;
+    std::string shutdown_command_;
+    bool do_shutdown_ = false;
+} shutdown_state;
+
 void DumpState() {
+    auto lock = std::lock_guard{service_lock};
     ServiceList::GetInstance().DumpState();
     ActionManager::GetInstance().DumpState();
 }
@@ -156,40 +291,7 @@
     }
 }
 
-bool start_waiting_for_property(const char *name, const char *value)
-{
-    if (waiting_for_prop) {
-        return false;
-    }
-    if (GetProperty(name, "") != value) {
-        // Current property value is not equal to expected value
-        wait_prop_name = name;
-        wait_prop_value = value;
-        waiting_for_prop.reset(new Timer());
-    } else {
-        LOG(INFO) << "start_waiting_for_property(\""
-                  << name << "\", \"" << value << "\"): already set";
-    }
-    return true;
-}
-
-void ResetWaitForProp() {
-    wait_prop_name.clear();
-    wait_prop_value.clear();
-    waiting_for_prop.reset();
-}
-
-static void TriggerShutdown(const std::string& command) {
-    // We can't call HandlePowerctlMessage() directly in this function,
-    // because it modifies the contents of the action queue, which can cause the action queue
-    // to get into a bad state if this function is called from a command being executed by the
-    // action queue.  Instead we set this flag and ensure that shutdown happens before the next
-    // command is run in the main init loop.
-    shutdown_command = command;
-    do_shutdown = true;
-}
-
-void property_changed(const std::string& name, const std::string& value) {
+void PropertyChanged(const std::string& name, const std::string& value) {
     // If the property is sys.powerctl, we bypass the event queue and immediately handle it.
     // This is to ensure that init will always and immediately shutdown/reboot, regardless of
     // if there are other pending events to process or if init is waiting on an exec service or
@@ -197,30 +299,20 @@
     // In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific
     // commands to be executed.
     if (name == "sys.powerctl") {
-        TriggerShutdown(value);
+        trigger_shutdown(value);
     }
 
-    if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value);
-
-    // We always record how long init waited for ueventd to tell us cold boot finished.
-    // If we aren't waiting on this property, it means that ueventd finished before we even started
-    // to wait.
-    if (name == kColdBootDoneProp) {
-        auto time_waited = waiting_for_prop ? waiting_for_prop->duration().count() : 0;
-        SetProperty("ro.boottime.init.cold_boot_wait", std::to_string(time_waited));
+    if (property_triggers_enabled) {
+        ActionManager::GetInstance().QueuePropertyChange(name, value);
+        WakeEpoll();
     }
 
-    if (waiting_for_prop) {
-        if (wait_prop_name == name && wait_prop_value == value) {
-            LOG(INFO) << "Wait for property '" << wait_prop_name << "=" << wait_prop_value
-                      << "' took " << *waiting_for_prop;
-            ResetWaitForProp();
-        }
-    }
+    prop_waiter_state.CheckAndResetWait(name, value);
 }
 
 static std::optional<boot_clock::time_point> HandleProcessActions() {
     std::optional<boot_clock::time_point> next_process_action_time;
+    auto lock = std::lock_guard{service_lock};
     for (const auto& s : ServiceList::GetInstance()) {
         if ((s->flags() & SVC_RUNNING) && s->timeout_period()) {
             auto timeout_time = s->time_started() + *s->timeout_period();
@@ -249,7 +341,7 @@
     return next_process_action_time;
 }
 
-static Result<void> DoControlStart(Service* service) {
+static Result<void> DoControlStart(Service* service) REQUIRES(service_lock) {
     return service->Start();
 }
 
@@ -258,7 +350,7 @@
     return {};
 }
 
-static Result<void> DoControlRestart(Service* service) {
+static Result<void> DoControlRestart(Service* service) REQUIRES(service_lock) {
     service->Restart();
     return {};
 }
@@ -292,7 +384,7 @@
     return control_message_functions;
 }
 
-bool HandleControlMessage(const std::string& msg, const std::string& name, pid_t pid) {
+bool HandleControlMessage(const std::string& msg, const std::string& name, pid_t from_pid) {
     const auto& map = get_control_message_map();
     const auto it = map.find(msg);
 
@@ -301,7 +393,7 @@
         return false;
     }
 
-    std::string cmdline_path = StringPrintf("proc/%d/cmdline", pid);
+    std::string cmdline_path = StringPrintf("proc/%d/cmdline", from_pid);
     std::string process_cmdline;
     if (ReadFileToString(cmdline_path, &process_cmdline)) {
         std::replace(process_cmdline.begin(), process_cmdline.end(), '\0', ' ');
@@ -312,6 +404,8 @@
 
     const ControlMessageFunction& function = it->second;
 
+    auto lock = std::lock_guard{service_lock};
+
     Service* svc = nullptr;
 
     switch (function.target) {
@@ -329,23 +423,24 @@
 
     if (svc == nullptr) {
         LOG(ERROR) << "Control message: Could not find '" << name << "' for ctl." << msg
-                   << " from pid: " << pid << " (" << process_cmdline << ")";
+                   << " from pid: " << from_pid << " (" << process_cmdline << ")";
         return false;
     }
 
     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();
+                   << "' from pid: " << from_pid << " (" << process_cmdline
+                   << "): " << result.error();
         return false;
     }
 
     LOG(INFO) << "Control message: Processed ctl." << msg << " for '" << name
-              << "' from pid: " << pid << " (" << process_cmdline << ")";
+              << "' from pid: " << from_pid << " (" << process_cmdline << ")";
     return true;
 }
 
 static Result<void> wait_for_coldboot_done_action(const BuiltinArguments& args) {
-    if (!start_waiting_for_property(kColdBootDoneProp, "true")) {
+    if (!prop_waiter_state.StartWaiting(kColdBootDoneProp, "true")) {
         LOG(FATAL) << "Could not wait for '" << kColdBootDoneProp << "'";
     }
 
@@ -493,6 +588,7 @@
     }
 
     auto found = false;
+    auto lock = std::lock_guard{service_lock};
     for (const auto& service : ServiceList::GetInstance()) {
         auto svc = service.get();
         if (svc->keycodes() == keycodes) {
@@ -579,44 +675,6 @@
     }
 }
 
-static void HandlePropertyFd() {
-    auto message = ReadMessage(property_fd);
-    if (!message.ok()) {
-        LOG(ERROR) << "Could not read message from property service: " << message.error();
-        return;
-    }
-
-    auto property_message = PropertyMessage{};
-    if (!property_message.ParseFromString(*message)) {
-        LOG(ERROR) << "Could not parse message from property service";
-        return;
-    }
-
-    switch (property_message.msg_case()) {
-        case PropertyMessage::kControlMessage: {
-            auto& control_message = property_message.control_message();
-            bool success = HandleControlMessage(control_message.msg(), control_message.name(),
-                                                control_message.pid());
-
-            uint32_t response = success ? PROP_SUCCESS : PROP_ERROR_HANDLE_CONTROL_MESSAGE;
-            if (control_message.has_fd()) {
-                int fd = control_message.fd();
-                TEMP_FAILURE_RETRY(send(fd, &response, sizeof(response), 0));
-                close(fd);
-            }
-            break;
-        }
-        case PropertyMessage::kChangedMessage: {
-            auto& changed_message = property_message.changed_message();
-            property_changed(changed_message.name(), changed_message.value());
-            break;
-        }
-        default:
-            LOG(ERROR) << "Unknown message type from property service: "
-                       << property_message.msg_case();
-    }
-}
-
 int SecondStageMain(int argc, char** argv) {
     if (REBOOT_BOOTLOADER_ON_PANIC) {
         InstallRebootSignalHandlers();
@@ -624,7 +682,7 @@
 
     boot_clock::time_point start_time = boot_clock::now();
 
-    trigger_shutdown = TriggerShutdown;
+    trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); };
 
     SetStdioToDevNull(argv);
     InitKernelLogging(argv);
@@ -684,11 +742,8 @@
     }
 
     InstallSignalFdHandler(&epoll);
-
+    InstallInitNotifier(&epoll);
     StartPropertyService(&property_fd);
-    if (auto result = epoll.RegisterHandler(property_fd, HandlePropertyFd); !result.ok()) {
-        LOG(FATAL) << "Could not register epoll handler for property fd: " << result.error();
-    }
 
     // Make the time that init stages started available for bootstat to log.
     RecordStageBoottimes(start_time);
@@ -742,6 +797,7 @@
     Keychords keychords;
     am.QueueBuiltinAction(
             [&epoll, &keychords](const BuiltinArguments& args) -> Result<void> {
+                auto lock = std::lock_guard{service_lock};
                 for (const auto& svc : ServiceList::GetInstance()) {
                     keychords.Register(svc->keycodes());
                 }
@@ -772,12 +828,12 @@
         // By default, sleep until something happens.
         auto epoll_timeout = std::optional<std::chrono::milliseconds>{};
 
-        if (do_shutdown && !IsShuttingDown()) {
-            do_shutdown = false;
-            HandlePowerctlMessage(shutdown_command);
+        auto shutdown_command = shutdown_state.CheckShutdown();
+        if (shutdown_command) {
+            HandlePowerctlMessage(*shutdown_command);
         }
 
-        if (!(waiting_for_prop || Service::is_exec_service_running())) {
+        if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
             am.ExecuteOneCommand();
         }
         if (!IsShuttingDown()) {
@@ -791,7 +847,7 @@
             }
         }
 
-        if (!(waiting_for_prop || Service::is_exec_service_running())) {
+        if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
             // If there's more work to do, wake up again immediately.
             if (am.HasMoreCommands()) epoll_timeout = 0ms;
         }
diff --git a/init/init.h b/init/init.h
index 4bbca6f..bcf24e7 100644
--- a/init/init.h
+++ b/init/init.h
@@ -41,6 +41,9 @@
 void SendStopSendingMessagesMessage();
 void SendStartSendingMessagesMessage();
 
+void PropertyChanged(const std::string& name, const std::string& value);
+bool HandleControlMessage(const std::string& msg, const std::string& name, pid_t from_pid);
+
 int SecondStageMain(int argc, char** argv);
 
 }  // namespace init
diff --git a/init/init_test.cpp b/init/init_test.cpp
index caf3e03..3053bd8 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -167,6 +167,7 @@
 
     ServiceList service_list;
     TestInitText(init_script, BuiltinFunctionMap(), {}, &service_list);
+    auto lock = std::lock_guard{service_lock};
     ASSERT_EQ(1, std::distance(service_list.begin(), service_list.end()));
 
     auto service = service_list.begin()->get();
diff --git a/init/lmkd_service.cpp b/init/lmkd_service.cpp
index dd1ab4d..a531d0a 100644
--- a/init/lmkd_service.cpp
+++ b/init/lmkd_service.cpp
@@ -79,7 +79,8 @@
 }
 
 static void RegisterServices(pid_t exclude_pid) {
-    for (const auto& service : ServiceList::GetInstance().services()) {
+    auto lock = std::lock_guard{service_lock};
+    for (const auto& service : ServiceList::GetInstance()) {
         auto svc = service.get();
         if (svc->oom_score_adjust() != DEFAULT_OOM_SCORE_ADJUST) {
             // skip if process is excluded or not yet forked (pid==0)
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index 0749fe3..aa36849 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -29,6 +29,7 @@
 #include <android-base/unique_fd.h>
 #include <apex_manifest.pb.h>
 
+#include "property_service.h"
 #include "util.h"
 
 namespace android {
@@ -290,6 +291,14 @@
         return true;
     }
     if (default_ns_id != GetMountNamespaceId()) {
+        // The property service thread and its descendent threads must be in the correct mount
+        // namespace to call Service::Start(), however setns() only operates on a single thread and
+        // fails when secondary threads attempt to join the same mount namespace.  Therefore, we
+        // must join the property service thread and its descendents before the setns() call.  Those
+        // threads are then started again after the setns() call, and they'll be in the proper
+        // namespace.
+        PausePropertyService();
+
         if (setns(default_ns_fd.get(), CLONE_NEWNS) == -1) {
             PLOG(ERROR) << "Failed to switch back to the default mount namespace.";
             return false;
@@ -299,6 +308,8 @@
             LOG(ERROR) << result.error();
             return false;
         }
+
+        ResumePropertyService();
     }
 
     LOG(INFO) << "Switched to default mount namespace";
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 84644e8..b140ee4 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -92,8 +92,10 @@
 static bool persistent_properties_loaded = false;
 
 static int property_set_fd = -1;
+static int from_init_socket = -1;
 static int init_socket = -1;
 static bool accept_messages = false;
+static std::thread property_service_thread;
 
 static PropertyInfoAreaFile property_info_area;
 
@@ -147,17 +149,6 @@
     return has_access;
 }
 
-static void SendPropertyChanged(const std::string& name, const std::string& value) {
-    auto property_msg = PropertyMessage{};
-    auto* changed_message = property_msg.mutable_changed_message();
-    changed_message->set_name(name);
-    changed_message->set_value(value);
-
-    if (auto result = SendMessage(init_socket, property_msg); !result.ok()) {
-        LOG(ERROR) << "Failed to send property changed message: " << result.error();
-    }
-}
-
 static uint32_t PropertySet(const std::string& name, const std::string& value, std::string* error) {
     size_t valuelen = value.size();
 
@@ -196,47 +187,137 @@
     // If init hasn't started its main loop, then it won't be handling property changed messages
     // anyway, so there's no need to try to send them.
     if (accept_messages) {
-        SendPropertyChanged(name, value);
+        PropertyChanged(name, value);
     }
     return PROP_SUCCESS;
 }
 
-class AsyncRestorecon {
+template <typename T>
+class SingleThreadExecutor {
   public:
-    void TriggerRestorecon(const std::string& path) {
-        auto guard = std::lock_guard{mutex_};
-        paths_.emplace(path);
+    virtual ~SingleThreadExecutor() {}
 
-        if (!thread_started_) {
-            thread_started_ = true;
-            std::thread{&AsyncRestorecon::ThreadFunction, this}.detach();
+    template <typename F = T>
+    void Run(F&& item) {
+        auto guard = std::lock_guard{mutex_};
+        items_.emplace(std::forward<F>(item));
+
+        if (thread_state_ == ThreadState::kRunning || thread_state_ == ThreadState::kStopped) {
+            return;
+        }
+
+        if (thread_state_ == ThreadState::kPendingJoin) {
+            thread_.join();
+        }
+
+        StartThread();
+    }
+
+    void StopAndJoin() {
+        auto lock = std::unique_lock{mutex_};
+        if (thread_state_ == ThreadState::kPendingJoin) {
+            thread_.join();
+        } else if (thread_state_ == ThreadState::kRunning) {
+            thread_state_ = ThreadState::kStopped;
+            lock.unlock();
+            thread_.join();
+            lock.lock();
+        }
+
+        thread_state_ = ThreadState::kStopped;
+    }
+
+    void Restart() {
+        auto guard = std::lock_guard{mutex_};
+        if (items_.empty()) {
+            thread_state_ = ThreadState::kNotStarted;
+        } else {
+            StartThread();
+        }
+    }
+
+    void MaybeJoin() {
+        auto guard = std::lock_guard{mutex_};
+        if (thread_state_ == ThreadState::kPendingJoin) {
+            thread_.join();
+            thread_state_ = ThreadState::kNotStarted;
         }
     }
 
   private:
+    virtual void Execute(T&& item) = 0;
+
+    void StartThread() {
+        thread_state_ = ThreadState::kRunning;
+        auto thread = std::thread{&SingleThreadExecutor::ThreadFunction, this};
+        std::swap(thread_, thread);
+    }
+
     void ThreadFunction() {
         auto lock = std::unique_lock{mutex_};
 
-        while (!paths_.empty()) {
-            auto path = paths_.front();
-            paths_.pop();
+        while (!items_.empty()) {
+            auto item = items_.front();
+            items_.pop();
 
             lock.unlock();
-            if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
-                LOG(ERROR) << "Asynchronous restorecon of '" << path << "' failed'";
-            }
-            android::base::SetProperty(kRestoreconProperty, path);
+            Execute(std::move(item));
             lock.lock();
         }
 
-        thread_started_ = false;
+        if (thread_state_ != ThreadState::kStopped) {
+            thread_state_ = ThreadState::kPendingJoin;
+        }
     }
 
     std::mutex mutex_;
-    std::queue<std::string> paths_;
-    bool thread_started_ = false;
+    std::queue<T> items_;
+    enum class ThreadState {
+        kNotStarted,  // Initial state when starting the program or when restarting with no items to
+                      // process.
+        kRunning,     // The thread is running and is in a state that it will process new items if
+                      // are run.
+        kPendingJoin,  // The thread has run to completion and is pending join().  A new thread must
+                       // be launched for new items to be processed.
+        kStopped,  // This executor has stopped and will not process more items until Restart() is
+                   // called.  Currently pending items will be processed and the thread will be
+                   // joined.
+    };
+    ThreadState thread_state_ = ThreadState::kNotStarted;
+    std::thread thread_;
 };
 
+class RestoreconThread : public SingleThreadExecutor<std::string> {
+    virtual void Execute(std::string&& path) override {
+        if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
+            LOG(ERROR) << "Asynchronous restorecon of '" << path << "' failed'";
+        }
+        android::base::SetProperty(kRestoreconProperty, path);
+    }
+};
+
+struct ControlMessageInfo {
+    std::string message;
+    std::string name;
+    pid_t pid;
+    int fd;
+};
+
+class ControlMessageThread : public SingleThreadExecutor<ControlMessageInfo> {
+    virtual void Execute(ControlMessageInfo&& info) override {
+        bool success = HandleControlMessage(info.message, info.name, info.pid);
+
+        uint32_t response = success ? PROP_SUCCESS : PROP_ERROR_HANDLE_CONTROL_MESSAGE;
+        if (info.fd != -1) {
+            TEMP_FAILURE_RETRY(send(info.fd, &response, sizeof(response), 0));
+            close(info.fd);
+        }
+    }
+};
+
+static RestoreconThread restorecon_thread;
+static ControlMessageThread control_message_thread;
+
 class SocketConnection {
   public:
     SocketConnection(int socket, const ucred& cred) : socket_(socket), cred_(cred) {}
@@ -378,29 +459,17 @@
         return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
     }
 
-    auto property_msg = PropertyMessage{};
-    auto* control_message = property_msg.mutable_control_message();
-    control_message->set_msg(msg);
-    control_message->set_name(name);
-    control_message->set_pid(pid);
-
-    // We must release the fd before sending it to init, otherwise there will be a race with init.
-    // If init calls close() before Release(), then fdsan will see the wrong tag and abort().
+    // We must release the fd before spawning the thread, otherwise there will be a race with the
+    // thread. If the thread calls close() before this function calls Release(), then fdsan will see
+    // the wrong tag and abort().
     int fd = -1;
     if (socket != nullptr && SelinuxGetVendorAndroidVersion() > __ANDROID_API_Q__) {
         fd = socket->Release();
-        control_message->set_fd(fd);
     }
 
-    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) {
-            close(fd);
-        }
-        *error = "Failed to send control message: " + result.error().message();
-        return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
-    }
+    // Handling a control message likely calls SetProperty, which we must synchronously handle,
+    // therefore we must fork a thread to handle it.
+    control_message_thread.Run({msg, name, pid, fd});
 
     return PROP_SUCCESS;
 }
@@ -502,8 +571,7 @@
     // We use a thread to do this restorecon operation to prevent holding up init, as it may take
     // a long time to complete.
     if (name == kRestoreconProperty && cr.pid != 1 && !value.empty()) {
-        static AsyncRestorecon async_restorecon;
-        async_restorecon.TriggerRestorecon(value);
+        restorecon_thread.Run(value);
         return PROP_SUCCESS;
     }
 
@@ -1082,6 +1150,8 @@
     PropertyLoadBootDefaults();
 }
 
+static bool pause_property_service = false;
+
 static void HandleInitSocket() {
     auto message = ReadMessage(init_socket);
     if (!message.ok()) {
@@ -1116,6 +1186,10 @@
             accept_messages = true;
             break;
         }
+        case InitMessage::kPausePropertyService: {
+            pause_property_service = true;
+            break;
+        }
         default:
             LOG(ERROR) << "Unknown message type from init: " << init_message.msg_case();
     }
@@ -1136,7 +1210,7 @@
         LOG(FATAL) << result.error();
     }
 
-    while (true) {
+    while (!pause_property_service) {
         auto pending_functions = epoll.Wait(std::nullopt);
         if (!pending_functions.ok()) {
             LOG(ERROR) << pending_functions.error();
@@ -1145,9 +1219,34 @@
                 (*function)();
             }
         }
+        control_message_thread.MaybeJoin();
+        restorecon_thread.MaybeJoin();
     }
 }
 
+void SendStopPropertyServiceMessage() {
+    auto init_message = InitMessage{};
+    init_message.set_pause_property_service(true);
+    if (auto result = SendMessage(from_init_socket, init_message); !result.ok()) {
+        LOG(ERROR) << "Failed to send stop property service message: " << result.error();
+    }
+}
+
+void PausePropertyService() {
+    control_message_thread.StopAndJoin();
+    restorecon_thread.StopAndJoin();
+    SendStopPropertyServiceMessage();
+    property_service_thread.join();
+}
+
+void ResumePropertyService() {
+    pause_property_service = false;
+    auto new_thread = std::thread{PropertyServiceThread};
+    property_service_thread.swap(new_thread);
+    restorecon_thread.Restart();
+    control_message_thread.Restart();
+}
+
 void StartPropertyService(int* epoll_socket) {
     InitPropertySet("ro.property_service.version", "2");
 
@@ -1155,7 +1254,7 @@
     if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sockets) != 0) {
         PLOG(FATAL) << "Failed to socketpair() between property_service and init";
     }
-    *epoll_socket = sockets[0];
+    *epoll_socket = from_init_socket = sockets[0];
     init_socket = sockets[1];
     accept_messages = true;
 
@@ -1169,7 +1268,8 @@
 
     listen(property_set_fd, 8);
 
-    std::thread{PropertyServiceThread}.detach();
+    auto new_thread = std::thread{PropertyServiceThread};
+    property_service_thread.swap(new_thread);
 }
 
 }  // namespace init
diff --git a/init/property_service.h b/init/property_service.h
index 506d116..e921326 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -31,6 +31,8 @@
 
 void PropertyInit();
 void StartPropertyService(int* epoll_socket);
+void ResumePropertyService();
+void PausePropertyService();
 
 }  // namespace init
 }  // namespace android
diff --git a/init/property_service.proto b/init/property_service.proto
index 08268d9..36245b2 100644
--- a/init/property_service.proto
+++ b/init/property_service.proto
@@ -41,5 +41,6 @@
         bool load_persistent_properties = 1;
         bool stop_sending_messages = 2;
         bool start_sending_messages = 3;
+        bool pause_property_service = 4;
     };
 }
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 38e8227..f7cc36e 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -85,7 +85,7 @@
 
 static const std::set<std::string> kDebuggingServices{"tombstoned", "logd", "adbd", "console"};
 
-static std::vector<Service*> GetDebuggingServices(bool only_post_data) {
+static std::vector<Service*> GetDebuggingServices(bool only_post_data) REQUIRES(service_lock) {
     std::vector<Service*> ret;
     ret.reserve(kDebuggingServices.size());
     for (const auto& s : ServiceList::GetInstance()) {
@@ -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. */
@@ -174,7 +179,7 @@
 };
 
 // Turn off backlight while we are performing power down cleanup activities.
-static void TurnOffBacklight() {
+static void TurnOffBacklight() REQUIRES(service_lock) {
     Service* service = ServiceList::GetInstance().FindService("blank_screen");
     if (service == nullptr) {
         LOG(WARNING) << "cannot find blank_screen in TurnOffBacklight";
@@ -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;
@@ -582,6 +587,7 @@
     // Start reboot monitor thread
     sem_post(&reboot_semaphore);
 
+    auto lock = std::lock_guard{service_lock};
     // watchdogd is a vendor specific component but should be alive to complete shutdown safely.
     const std::set<std::string> to_starts{"watchdogd"};
     std::vector<Service*> stop_first;
@@ -701,6 +707,7 @@
     // Skip wait for prop if it is in progress
     ResetWaitForProp();
     // Clear EXEC flag if there is one pending
+    auto lock = std::lock_guard{service_lock};
     for (const auto& s : ServiceList::GetInstance()) {
         s->UnSetExec();
     }
@@ -744,6 +751,7 @@
         return Error() << "Failed to set sys.init.userspace_reboot.in_progress property";
     }
     EnterShutdown();
+    auto lock = std::lock_guard{service_lock};
     if (!SetProperty("sys.powerctl", "")) {
         return Error() << "Failed to reset sys.powerctl property";
     }
@@ -828,6 +836,7 @@
     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.
+        PersistRebootReason("userspace_failed,watchdog_triggered");
         RebootSystem(ANDROID_RB_RESTART2, "userspace_failed,watchdog_triggered");
     }
     LOG(INFO) << "Device booted, stopping userspace reboot watchdog";
@@ -901,6 +910,7 @@
                 run_fsck = true;
             } else if (cmd_params[1] == "thermal") {
                 // Turn off sources of heat immediately.
+                auto lock = std::lock_guard{service_lock};
                 TurnOffBacklight();
                 // run_fsck is false to avoid delay
                 cmd = ANDROID_RB_THERMOFF;
diff --git a/init/security.cpp b/init/security.cpp
index 6cbe642..2450d65 100644
--- a/init/security.cpp
+++ b/init/security.cpp
@@ -128,8 +128,7 @@
 #define MMAP_RND_PATH "/proc/sys/vm/mmap_rnd_bits"
 #define MMAP_RND_COMPAT_PATH "/proc/sys/vm/mmap_rnd_compat_bits"
 
-// __attribute__((unused)) due to lack of mips support: see mips block in SetMmapRndBitsAction
-static bool __attribute__((unused)) SetMmapRndBitsMin(int start, int min, bool compat) {
+static bool SetMmapRndBitsMin(int start, int min, bool compat) {
     std::string path;
     if (compat) {
         path = MMAP_RND_COMPAT_PATH;
@@ -174,9 +173,6 @@
     if (SetMmapRndBitsMin(16, 16, h64)) {
         return {};
     }
-#elif defined(__mips__) || defined(__mips64__)
-    // TODO: add mips support b/27788820
-    return {};
 #else
     LOG(ERROR) << "Unknown architecture";
 #endif
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 41007c1..2faa167 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"
@@ -533,6 +534,10 @@
     selinux_android_restorecon("/apex", 0);
 
     selinux_android_restorecon("/linkerconfig", 0);
+
+    // adb remount, snapshot-based updates, and DSUs all create files during
+    // first-stage init.
+    selinux_android_restorecon("/metadata", SELINUX_ANDROID_RESTORECON_RECURSE);
 }
 
 int SelinuxKlogCallback(int type, const char* fmt, ...) {
diff --git a/init/service.cpp b/init/service.cpp
index 665a1b0..b12d11a 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -40,7 +40,7 @@
 #include "service_list.h"
 #include "util.h"
 
-#if defined(__ANDROID__)
+#ifdef INIT_FULL_SOURCES
 #include <ApexProperties.sysprop.h>
 #include <android/api-level.h>
 
@@ -303,7 +303,7 @@
         return;
     }
 
-#if defined(__ANDROID__)
+#if INIT_FULL_SOURCES
     static bool is_apex_updatable = android::sysprop::ApexProperties::updatable().value_or(false);
 #else
     static bool is_apex_updatable = false;
diff --git a/init/service.h b/init/service.h
index cf3f0c2..d2a4462 100644
--- a/init/service.h
+++ b/init/service.h
@@ -27,12 +27,14 @@
 #include <vector>
 
 #include <android-base/chrono_utils.h>
+#include <android-base/thread_annotations.h>
 #include <cutils/iosched_policy.h>
 
 #include "action.h"
 #include "capabilities.h"
 #include "keyword_map.h"
 #include "parser.h"
+#include "service_lock.h"
 #include "service_utils.h"
 #include "subcontext.h"
 
@@ -77,17 +79,17 @@
 
     bool IsRunning() { return (flags_ & SVC_RUNNING) != 0; }
     bool IsEnabled() { return (flags_ & SVC_DISABLED) == 0; }
-    Result<void> ExecStart();
-    Result<void> Start();
-    Result<void> StartIfNotDisabled();
-    Result<void> StartIfPostData();
-    Result<void> Enable();
+    Result<void> ExecStart() REQUIRES(service_lock);
+    Result<void> Start() REQUIRES(service_lock);
+    Result<void> StartIfNotDisabled() REQUIRES(service_lock);
+    Result<void> StartIfPostData() REQUIRES(service_lock);
+    Result<void> Enable() REQUIRES(service_lock);
     void Reset();
     void ResetIfPostData();
     void Stop();
     void Terminate();
     void Timeout();
-    void Restart();
+    void Restart() REQUIRES(service_lock);
     void Reap(const siginfo_t& siginfo);
     void DumpState() const;
     void SetShutdownCritical() { flags_ |= SVC_SHUTDOWN_CRITICAL; }
diff --git a/init/service_list.h b/init/service_list.h
index 1838624..8cbc878 100644
--- a/init/service_list.h
+++ b/init/service_list.h
@@ -17,9 +17,13 @@
 #pragma once
 
 #include <memory>
+#include <mutex>
 #include <vector>
 
+#include <android-base/thread_annotations.h>
+
 #include "service.h"
+#include "service_lock.h"
 
 namespace android {
 namespace init {
@@ -32,16 +36,16 @@
     ServiceList();
     size_t CheckAllCommands();
 
-    void AddService(std::unique_ptr<Service> service);
-    void RemoveService(const Service& svc);
+    void AddService(std::unique_ptr<Service> service) REQUIRES(service_lock);
+    void RemoveService(const Service& svc) REQUIRES(service_lock);
     template <class UnaryPredicate>
-    void RemoveServiceIf(UnaryPredicate predicate) {
+    void RemoveServiceIf(UnaryPredicate predicate) REQUIRES(service_lock) {
         services_.erase(std::remove_if(services_.begin(), services_.end(), predicate),
                         services_.end());
     }
 
     template <typename T, typename F = decltype(&Service::name)>
-    Service* FindService(T value, F function = &Service::name) const {
+    Service* FindService(T value, F function = &Service::name) const REQUIRES(service_lock) {
         auto svc = std::find_if(services_.begin(), services_.end(),
                                 [&function, &value](const std::unique_ptr<Service>& s) {
                                     return std::invoke(function, s) == value;
@@ -52,7 +56,7 @@
         return nullptr;
     }
 
-    Service* FindInterface(const std::string& interface_name) {
+    Service* FindInterface(const std::string& interface_name) REQUIRES(service_lock) {
         for (const auto& svc : services_) {
             if (svc->interfaces().count(interface_name) > 0) {
                 return svc.get();
@@ -62,18 +66,20 @@
         return nullptr;
     }
 
-    void DumpState() const;
+    void DumpState() const REQUIRES(service_lock);
 
-    auto begin() const { return services_.begin(); }
-    auto end() const { return services_.end(); }
-    const std::vector<std::unique_ptr<Service>>& services() const { return services_; }
-    const std::vector<Service*> services_in_shutdown_order() const;
+    auto begin() const REQUIRES(service_lock) { return services_.begin(); }
+    auto end() const REQUIRES(service_lock) { return services_.end(); }
+    const std::vector<std::unique_ptr<Service>>& services() const REQUIRES(service_lock) {
+        return services_;
+    }
+    const std::vector<Service*> services_in_shutdown_order() const REQUIRES(service_lock);
 
     void MarkPostData();
     bool IsPostData();
-    void MarkServicesUpdate();
+    void MarkServicesUpdate() REQUIRES(service_lock);
     bool IsServicesUpdated() const { return services_update_finished_; }
-    void DelayService(const Service& service);
+    void DelayService(const Service& service) REQUIRES(service_lock);
 
   private:
     std::vector<std::unique_ptr<Service>> services_;
diff --git a/init/service_lock.cpp b/init/service_lock.cpp
new file mode 100644
index 0000000..404d439
--- /dev/null
+++ b/init/service_lock.cpp
@@ -0,0 +1,25 @@
+/*
+ * 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 "service_lock.h"
+
+namespace android {
+namespace init {
+
+RecursiveMutex service_lock;
+
+}  // namespace init
+}  // namespace android
diff --git a/init/service_lock.h b/init/service_lock.h
new file mode 100644
index 0000000..6b94271
--- /dev/null
+++ b/init/service_lock.h
@@ -0,0 +1,40 @@
+/*
+ * 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 <mutex>
+
+#include <android-base/thread_annotations.h>
+
+namespace android {
+namespace init {
+
+// This class exists to add thread annotations, since they're absent from std::recursive_mutex.
+
+class CAPABILITY("mutex") RecursiveMutex {
+  public:
+    void lock() ACQUIRE() { mutex_.lock(); }
+    void unlock() RELEASE() { mutex_.unlock(); }
+
+  private:
+    std::recursive_mutex mutex_;
+};
+
+extern RecursiveMutex service_lock;
+
+}  // namespace init
+}  // namespace android
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 4b04ba0..51f4c97 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -34,7 +34,7 @@
 #include "service_utils.h"
 #include "util.h"
 
-#if defined(__ANDROID__)
+#ifdef INIT_FULL_SOURCES
 #include <android/api-level.h>
 #include <sys/system_properties.h>
 
@@ -168,6 +168,7 @@
 
     const std::string fullname = interface_name + "/" + instance_name;
 
+    auto lock = std::lock_guard{service_lock};
     for (const auto& svc : *service_list_) {
         if (svc->interfaces().count(fullname) > 0) {
             return Error() << "Interface '" << fullname << "' redefined in " << service_->name()
@@ -598,6 +599,7 @@
         }
     }
 
+    auto lock = std::lock_guard{service_lock};
     Service* old_service = service_list_->FindService(service_->name());
     if (old_service) {
         if (!service_->is_override()) {
diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp
index 9b2c7d9..064d64d 100644
--- a/init/sigchld_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -64,6 +64,8 @@
     std::string wait_string;
     Service* service = nullptr;
 
+    auto lock = std::lock_guard{service_lock};
+
     if (SubcontextChildReap(pid)) {
         name = "Subcontext";
     } else {
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index 3260159..5263c14 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -31,7 +31,7 @@
 #include "proto_utils.h"
 #include "util.h"
 
-#if defined(__ANDROID__)
+#ifdef INIT_FULL_SOURCES
 #include <android/api-level.h>
 #include "property_service.h"
 #include "selabel.h"
diff --git a/init/test_utils/Android.bp b/init/test_utils/Android.bp
deleted file mode 100644
index 1cb05b6..0000000
--- a/init/test_utils/Android.bp
+++ /dev/null
@@ -1,27 +0,0 @@
-cc_library_static {
-    name: "libinit_test_utils",
-    cflags: [
-        "-Wall",
-        "-Wextra",
-        "-Wno-unused-parameter",
-        "-Werror",
-    ],
-    srcs: [
-        "service_utils.cpp",
-    ],
-    shared_libs: [
-        "libcutils",
-        "liblog",
-        "libjsoncpp",
-        "libprotobuf-cpp-lite",
-        "libhidl-gen-utils",
-    ],
-    whole_static_libs: [
-        "libinit",
-        "libpropertyinfoparser",
-    ],
-    static_libs: [
-        "libbase",
-    ],
-    export_include_dirs: ["include"], // for tests
-}
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 1b59c7ba..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,7 +20,6 @@
 #include <set>
 
 #include <android-base/result.h>
-#include <hidl-util/FqInstance.h>
 
 namespace android {
 namespace init {
diff --git a/init/util.cpp b/init/util.cpp
index 503c705..24f94ec 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -41,7 +41,7 @@
 #include <cutils/sockets.h>
 #include <selinux/android.h>
 
-#if defined(__ANDROID__)
+#ifdef INIT_FULL_SOURCES
 #include <android/api-level.h>
 #include <sys/system_properties.h>
 
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 65af2b3..8e90ddf 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",
@@ -126,19 +199,6 @@
             },
         },
 
-        android_mips: {
-            srcs: ["arch-mips/android_memset.c"],
-            sanitize: {
-                misc_undefined: ["integer"],
-            },
-        },
-        android_mips64: {
-            srcs: ["arch-mips/android_memset.c"],
-            sanitize: {
-                misc_undefined: ["integer"],
-            },
-        },
-
         android_x86: {
             srcs: [
                 "arch-x86/android_memset16.S",
@@ -172,6 +232,7 @@
         }
     },
 
+    whole_static_libs: ["libcutils_sockets"],
     shared_libs: [
         "liblog",
         "libbase",
diff --git a/libcutils/KernelLibcutilsTest.xml b/libcutils/KernelLibcutilsTest.xml
index e27fac6..40e4ef4 100644
--- a/libcutils/KernelLibcutilsTest.xml
+++ b/libcutils/KernelLibcutilsTest.xml
@@ -13,7 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<configuration description="Runs libcutils_test_static.">
+<configuration description="Runs KernelLibcutilsTest.">
     <option name="test-suite-tag" value="apct" />
     <option name="test-suite-tag" value="apct-native" />
 
@@ -22,12 +22,12 @@
 
     <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
         <option name="cleanup" value="true" />
-        <option name="push" value="libcutils_test_static->/data/local/tmp/libcutils_test_static" />
+        <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="libcutils_test_static" />
+        <option name="module-name" value="KernelLibcutilsTest" />
         <option name="include-filter" value="*AshmemTest*" />
     </test>
 </configuration>
diff --git a/libcutils/arch-mips/android_memset.c b/libcutils/arch-mips/android_memset.c
deleted file mode 100644
index c0fe3d1..0000000
--- a/libcutils/arch-mips/android_memset.c
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/* generic C version for any machine */
-
-#include <cutils/memory.h>
-
-#ifdef __clang__
-__attribute__((no_sanitize("integer")))
-#endif
-void android_memset16(uint16_t* dst, uint16_t value, size_t size)
-{
-   /* optimized version of
-      size >>= 1;
-      while (size--)
-        *dst++ = value;
-   */
-
-   size >>= 1;
-   if (((uintptr_t)dst & 2) && size) {
-      /* fill unpaired first elem separately */
-      *dst++ = value;
-      size--;
-   }
-   /* dst is now 32-bit-aligned */
-   /* fill body with 32-bit pairs */
-   uint32_t value32 = (((uint32_t)value) << 16) | ((uint32_t)value);
-   android_memset32((uint32_t*) dst, value32, size<<1);
-   if (size & 1) {
-      dst[size-1] = value;  /* fill unpaired last elem */
-   }
-}
-
-
-#ifdef __clang__
-__attribute__((no_sanitize("integer")))
-#endif
-void android_memset32(uint32_t* dst, uint32_t value, size_t size)
-{
-   /* optimized version of
-      size >>= 2;
-      while (size--)
-         *dst++ = value;
-   */
-
-   size >>= 2;
-   if (((uintptr_t)dst & 4) && size) {
-      /* fill unpaired first 32-bit elem separately */
-      *dst++ = value;
-      size--;
-   }
-   /* dst is now 64-bit aligned */
-   /* fill body with 64-bit pairs */
-   uint64_t value64 = (((uint64_t)value) << 32) | ((uint64_t)value);
-   uint64_t* dst64 = (uint64_t*)dst;
-
-   while (size >= 12) {
-      dst64[0] = value64;
-      dst64[1] = value64;
-      dst64[2] = value64;
-      dst64[3] = value64;
-      dst64[4] = value64;
-      dst64[5] = value64;
-      size  -= 12;
-      dst64 += 6;
-   }
-
-   /* fill remainder with original 32-bit single-elem loop */
-   dst = (uint32_t*) dst64;
-   while (size != 0) {
-       size--;
-      *dst++ = value;
-   }
-
-}
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/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index a2d36ff..b73a29b 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -133,6 +133,7 @@
 #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 */
+#define AID_CONTEXT_HUB 1080      /* GID for access to the Context Hub */
 /* 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/liblog/Android.bp b/liblog/Android.bp
index 2cf60e0..7f183c2 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -121,6 +121,10 @@
     ],
     logtags: ["event.logtags"],
     compile_multilib: "both",
+    apex_available: [
+        "//apex_available:anyapex",
+        "//apex_available:platform",
+    ],
 }
 
 ndk_headers {
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
index c84ddf7..c98455d 100644
--- a/liblog/include/android/log.h
+++ b/liblog/include/android/log.h
@@ -274,7 +274,7 @@
  * Gets the minimum priority that will be logged for this process.  If none has been set by a
  * previous __android_log_set_minimum_priority() call, this returns ANDROID_LOG_DEFAULT.
  */
-int __android_log_get_minimum_priority();
+int __android_log_get_minimum_priority(void);
 
 /**
  * Sets the default tag if no tag is provided when writing a log message.  Defaults to
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
index cf82e0f..1b6b0c6 100644
--- a/liblog/logger_write.cpp
+++ b/liblog/logger_write.cpp
@@ -348,7 +348,7 @@
     return 0;
   }
 
-  char buf[LOG_BUF_SIZE];
+  __attribute__((uninitialized)) char buf[LOG_BUF_SIZE];
 
   vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
 
@@ -366,7 +366,7 @@
   }
 
   va_list ap;
-  char buf[LOG_BUF_SIZE];
+  __attribute__((uninitialized)) char buf[LOG_BUF_SIZE];
 
   va_start(ap, fmt);
   vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
@@ -386,7 +386,7 @@
   }
 
   va_list ap;
-  char buf[LOG_BUF_SIZE];
+  __attribute__((uninitialized)) char buf[LOG_BUF_SIZE];
 
   va_start(ap, fmt);
   vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
@@ -398,7 +398,7 @@
 }
 
 void __android_log_assert(const char* cond, const char* tag, const char* fmt, ...) {
-  char buf[LOG_BUF_SIZE];
+  __attribute__((uninitialized)) char buf[LOG_BUF_SIZE];
 
   if (fmt) {
     va_list ap;
diff --git a/liblog/properties.cpp b/liblog/properties.cpp
index f30058a..abd48fc 100644
--- a/liblog/properties.cpp
+++ b/liblog/properties.cpp
@@ -405,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);
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 39ac7a5..4366f3d 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -27,6 +27,7 @@
 #include <unordered_set>
 
 #include <android-base/file.h>
+#include <android-base/properties.h>
 #include <benchmark/benchmark.h>
 #include <cutils/sockets.h>
 #include <log/event_tag_map.h>
@@ -1025,3 +1026,14 @@
   }
 }
 BENCHMARK(BM_lookupEventTagNum_logd_existing);
+
+static void BM_log_verbose_overhead(benchmark::State& state) {
+  std::string test_log_tag = "liblog_verbose_tag";
+  android::base::SetProperty("log.tag." + test_log_tag, "I");
+  for (auto _ : state) {
+    __android_log_print(ANDROID_LOG_VERBOSE, test_log_tag.c_str(), "%s test log message %d %d",
+                        "test test", 123, 456);
+  }
+  android::base::SetProperty("log.tag." + test_log_tag, "");
+}
+BENCHMARK(BM_log_verbose_overhead);
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 75a26bf..a60d2df 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -1637,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_";
@@ -1657,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/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/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 72f01af..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,18 @@
     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_;
@@ -527,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 a64ca50..28bc00c 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -163,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_;
@@ -176,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/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/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index e6263f8..18bd490 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -621,29 +621,9 @@
   uint64_t start = fde->pc_start;
   uint64_t end = fde->pc_end;
   auto it = fdes_.upper_bound(start);
-  bool add_element = false;
-  while (it != fdes_.end() && start < end) {
-    if (add_element) {
-      add_element = false;
-      if (end < it->second.first) {
-        if (it->first == end) {
-          return;
-        }
-        fdes_[end] = std::make_pair(start, fde);
-        return;
-      }
-      if (start != it->second.first) {
-        fdes_[it->second.first] = std::make_pair(start, fde);
-      }
-    }
-    if (start < it->first) {
-      if (end < it->second.first) {
-        if (it->first != end) {
-          fdes_[end] = std::make_pair(start, fde);
-        }
-        return;
-      }
-      add_element = true;
+  while (it != fdes_.end() && start < end && it->second.first < end) {
+    if (start < it->second.first) {
+      fdes_[it->second.first] = std::make_pair(start, fde);
     }
     start = it->first;
     ++it;
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp
index b6f574a..fac8a0e 100644
--- a/libunwindstack/tests/DwarfDebugFrameTest.cpp
+++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -754,16 +754,24 @@
   SetFde32(&this->memory_, 0x5400, 0xfc, 0, 0xa00, 0x100);
   // FDE 4 (0x100 - 0xb00)
   SetFde32(&this->memory_, 0x5500, 0xfc, 0, 0x150, 0xa00);
-  // FDE 5 (0x0 - 0x50)
-  SetFde32(&this->memory_, 0x5600, 0xfc, 0, 0, 0x50);
+  // FDE 5 (0x50 - 0xa0)
+  SetFde32(&this->memory_, 0x5600, 0xfc, 0, 0x50, 0x50);
+  // FDE 6 (0x0 - 0x50)
+  SetFde32(&this->memory_, 0x5700, 0xfc, 0, 0, 0x50);
 
-  this->debug_frame_->Init(0x5000, 0x700, 0);
+  this->debug_frame_->Init(0x5000, 0x800, 0);
 
   // Force reading all entries so no entries are found.
   const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0xfffff);
   ASSERT_TRUE(fde == nullptr);
 
-  //   0x0   - 0x50   FDE 5
+  //   0x50  - 0xa0  FDE 5
+  fde = this->debug_frame_->GetFdeFromPc(0x60);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x50U, fde->pc_start);
+  EXPECT_EQ(0xa0U, fde->pc_end);
+
+  //   0x0   - 0x50   FDE 6
   fde = this->debug_frame_->GetFdeFromPc(0x10);
   ASSERT_TRUE(fde != nullptr);
   EXPECT_EQ(0U, fde->pc_start);
@@ -812,6 +820,56 @@
   EXPECT_EQ(0xb50U, fde->pc_end);
 }
 
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc_overlap) {
+  SetCie32(&this->memory_, 0x5000, 0xfc, std::vector<uint8_t>{1, '\0', 0, 0, 1});
+
+  // FDE 0 (0x100 - 0x200)
+  SetFde32(&this->memory_, 0x5100, 0xfc, 0, 0x100, 0x100);
+  // FDE 1 (0x50 - 0x550)
+  SetFde32(&this->memory_, 0x5200, 0xfc, 0, 0x50, 0x500);
+  // FDE 2 (0x00 - 0x800)
+  SetFde32(&this->memory_, 0x5300, 0xfc, 0, 0x0, 0x800);
+
+  this->debug_frame_->Init(0x5000, 0x400, 0);
+
+  // Force reading all entries so no entries are found.
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0xfffff);
+  ASSERT_TRUE(fde == nullptr);
+
+  //   0x0  - 0x50  FDE 2
+  fde = this->debug_frame_->GetFdeFromPc(0x10);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x0U, fde->pc_start);
+  EXPECT_EQ(0x800U, fde->pc_end);
+
+  //   0x50  - 0x100  FDE 1
+  fde = this->debug_frame_->GetFdeFromPc(0x60);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x50U, fde->pc_start);
+  EXPECT_EQ(0x550U, fde->pc_end);
+
+  //   0x100 - 0x200  FDE 0
+  fde = this->debug_frame_->GetFdeFromPc(0x170);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x100U, fde->pc_start);
+  EXPECT_EQ(0x200U, fde->pc_end);
+
+  //   0x200 - 0x550  FDE 1
+  fde = this->debug_frame_->GetFdeFromPc(0x210);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x50U, fde->pc_start);
+  EXPECT_EQ(0x550U, fde->pc_end);
+
+  //   0x550 - 0x800  FDE 2
+  fde = this->debug_frame_->GetFdeFromPc(0x580);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x0U, fde->pc_start);
+  EXPECT_EQ(0x800U, fde->pc_end);
+
+  fde = this->debug_frame_->GetFdeFromPc(0x810);
+  ASSERT_TRUE(fde == nullptr);
+}
+
 REGISTER_TYPED_TEST_SUITE_P(
     DwarfDebugFrameTest, GetFdes32, GetFdes32_after_GetFdeFromPc, GetFdes32_not_in_section,
     GetFdeFromPc32, GetFdeFromPc32_reverse, GetFdeFromPc32_not_in_section, GetFdes64,
@@ -822,7 +880,7 @@
     GetCieFromOffset64_version4, GetCieFromOffset32_version5, GetCieFromOffset64_version5,
     GetCieFromOffset_version_invalid, GetCieFromOffset32_augment, GetCieFromOffset64_augment,
     GetFdeFromOffset32_augment, GetFdeFromOffset64_augment, GetFdeFromOffset32_lsda_address,
-    GetFdeFromOffset64_lsda_address, GetFdeFromPc_interleaved);
+    GetFdeFromOffset64_lsda_address, GetFdeFromPc_interleaved, GetFdeFromPc_overlap);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfDebugFrameTestTypes;
 INSTANTIATE_TYPED_TEST_SUITE_P(Libunwindstack, DwarfDebugFrameTest, DwarfDebugFrameTestTypes);
diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c
index 415488f..3bed0e3 100644
--- a/libusbhost/usbhost.c
+++ b/libusbhost/usbhost.c
@@ -597,6 +597,11 @@
     if (iter->curr_desc >= iter->config_end)
         return NULL;
     next = (struct usb_descriptor_header*)iter->curr_desc;
+    // Corrupt descriptor with zero length, cannot continue iterating
+    if (next->bLength == 0) {
+       D("usb_descriptor_iter_next got zero length USB descriptor, ending iteration\n");
+       return NULL;
+    }
     iter->curr_desc += next->bLength;
     return next;
 }
diff --git a/libutils/Android.bp b/libutils/Android.bp
index efa4c41..9ddbedf 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -74,12 +74,6 @@
         "liblog",
     ],
 
-    arch: {
-        mips: {
-            cflags: ["-DALIGN_DOUBLE"],
-        },
-    },
-
     target: {
         android: {
             cflags: ["-fvisibility=protected"],
@@ -159,6 +153,11 @@
             ],
         },
     },
+
+    apex_available: [
+        "//apex_available:anyapex",
+        "//apex_available:platform",
+    ],
 }
 
 cc_library {
@@ -169,12 +168,6 @@
         "CallStack.cpp",
     ],
 
-    arch: {
-        mips: {
-            cflags: ["-DALIGN_DOUBLE"],
-        },
-    },
-
     shared_libs: [
          "libutils",
          "libbacktrace",
diff --git a/libutils/StrongPointer_test.cpp b/libutils/StrongPointer_test.cpp
index 7b2e37f..d37c1de 100644
--- a/libutils/StrongPointer_test.cpp
+++ b/libutils/StrongPointer_test.cpp
@@ -36,10 +36,8 @@
 
 TEST(StrongPointer, move) {
     bool isDeleted;
-    SPFoo* foo = new SPFoo(&isDeleted);
-    ASSERT_EQ(0, foo->getStrongCount());
-    ASSERT_FALSE(isDeleted) << "Already deleted...?";
-    sp<SPFoo> sp1(foo);
+    sp<SPFoo> sp1 = sp<SPFoo>::make(&isDeleted);
+    SPFoo* foo = sp1.get();
     ASSERT_EQ(1, foo->getStrongCount());
     {
         sp<SPFoo> sp2 = std::move(sp1);
@@ -65,7 +63,7 @@
 
 TEST(StrongPointer, PointerComparison) {
     bool isDeleted;
-    sp<SPFoo> foo = new SPFoo(&isDeleted);
+    sp<SPFoo> foo = sp<SPFoo>::make(&isDeleted);
     ASSERT_EQ(foo.get(), foo);
     ASSERT_EQ(foo, foo.get());
     ASSERT_NE(nullptr, foo);
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/RefBase.h b/libutils/include/utils/RefBase.h
index 89f048d..e7acd17 100644
--- a/libutils/include/utils/RefBase.h
+++ b/libutils/include/utils/RefBase.h
@@ -297,6 +297,11 @@
     }
 
 protected:
+    // When constructing these objects, prefer using sp::make<>. Using a RefBase
+    // object on the stack or with other refcount mechanisms (e.g.
+    // std::shared_ptr) is inherently wrong. RefBase types have an implicit
+    // ownership model and cannot be safely used with other ownership models.
+
                             RefBase();
     virtual                 ~RefBase();
     
diff --git a/libutils/include/utils/String16.h b/libutils/include/utils/String16.h
index 1368096..013705b 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;
 
@@ -193,6 +186,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 6f4fb47..11128f2 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -32,6 +32,12 @@
 public:
     inline sp() : m_ptr(nullptr) { }
 
+    // TODO: switch everyone to using this over new, and make RefBase operator
+    // new private to that class so that we can avoid RefBase being used with
+    // other memory management mechanisms.
+    template <typename... Args>
+    static inline sp<T> make(Args&&... args);
+
     sp(T* other);  // NOLINT(implicit)
     sp(const sp<T>& other);
     sp(sp<T>&& other) noexcept;
@@ -160,9 +166,6 @@
 // It does not appear safe to broaden this check to include adjacent pages; apparently this code
 // is used in environments where there may not be a guard page below (at higher addresses than)
 // the bottom of the stack.
-//
-// TODO: Consider adding make_sp<T>() to allocate an object and wrap the resulting pointer safely
-// without checking overhead.
 template <typename T>
 void sp<T>::check_not_on_stack(const void* ptr) {
     static constexpr int MIN_PAGE_SIZE = 0x1000;  // 4K. Safer than including sys/user.h.
@@ -174,6 +177,18 @@
     }
 }
 
+// TODO: Ideally we should find a way to increment the reference count before running the
+// constructor, so that generating an sp<> to this in the constructor is no longer dangerous.
+template <typename T>
+template <typename... Args>
+sp<T> sp<T>::make(Args&&... args) {
+    T* t = new T(std::forward<Args>(args)...);
+    sp<T> result;
+    result.m_ptr = t;
+    t->incStrong(t);  // bypass check_not_on_stack for heap allocation
+    return result;
+}
+
 template<typename T>
 sp<T>::sp(T* other)
         : m_ptr(other) {
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 08e3d22..76a970f 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -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/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 834b20b..1cf2061 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -207,31 +207,37 @@
     // exact entry with time specified in ms or us precision.
     if ((realtime.tv_nsec % 1000) == 0) ++realtime.tv_nsec;
 
-    LogBufferElement* elem =
-        new LogBufferElement(log_id, realtime, uid, pid, tid, msg, len);
-    if (log_id != LOG_ID_SECURITY) {
-        int prio = ANDROID_LOG_INFO;
-        const char* tag = nullptr;
-        size_t tag_len = 0;
-        if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) {
-            tag = tagToName(elem->getTag());
-            if (tag) {
-                tag_len = strlen(tag);
-            }
-        } else {
-            prio = *msg;
-            tag = msg + 1;
-            tag_len = strnlen(tag, len - 1);
+    LogBufferElement* elem = new LogBufferElement(log_id, realtime, uid, pid, tid, msg, len);
+
+    // b/137093665: don't coalesce security messages.
+    if (log_id == LOG_ID_SECURITY) {
+        wrlock();
+        log(elem);
+        unlock();
+
+        return len;
+    }
+
+    int prio = ANDROID_LOG_INFO;
+    const char* tag = nullptr;
+    size_t tag_len = 0;
+    if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) {
+        tag = tagToName(elem->getTag());
+        if (tag) {
+            tag_len = strlen(tag);
         }
-        if (!__android_log_is_loggable_len(prio, tag, tag_len,
-                                           ANDROID_LOG_VERBOSE)) {
-            // Log traffic received to total
-            wrlock();
-            stats.addTotal(elem);
-            unlock();
-            delete elem;
-            return -EACCES;
-        }
+    } else {
+        prio = *msg;
+        tag = msg + 1;
+        tag_len = strnlen(tag, len - 1);
+    }
+    if (!__android_log_is_loggable_len(prio, tag, tag_len, ANDROID_LOG_VERBOSE)) {
+        // Log traffic received to total
+        wrlock();
+        stats.addTotal(elem);
+        unlock();
+        delete elem;
+        return -EACCES;
     }
 
     wrlock();
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/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/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.rc b/rootdir/init.rc
index 896745b..91dd7a5 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -114,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
@@ -144,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.
@@ -211,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
 
@@ -388,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.
@@ -498,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
 
@@ -613,7 +616,9 @@
     mkdir /data/misc/installd 0700 root root
     mkdir /data/misc/apexdata 0711 root root
     mkdir /data/misc/apexrollback 0700 root root
-    mkdir /data/misc/snapshotctl_log 0770 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
 
@@ -764,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
@@ -821,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
diff --git a/storaged/main.cpp b/storaged/main.cpp
index e35bd6f..a7bda14 100644
--- a/storaged/main.cpp
+++ b/storaged/main.cpp
@@ -46,11 +46,6 @@
 
 // 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(INFO) << "storaged: Start";
 
     for (;;) {
@@ -123,6 +118,9 @@
 
     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) {