Merge "Add wrapped key support for metadata encryption"
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/client/adb_client.h b/adb/client/adb_client.h
index ba53041..758fcab 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,16 @@
 // 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 host_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 host_name,
+                                        adb_secure_foreach_service_callback cb);
+void adb_secure_foreach_connect_service(const char* _Nullable host_name,
+                                        adb_secure_foreach_service_callback cb);
diff --git a/adb/client/transport_mdns.cpp b/adb/client/transport_mdns.cpp
index 1a34384..f5811a4 100644
--- a/adb/client/transport_mdns.cpp
+++ b/adb/client/transport_mdns.cpp
@@ -25,17 +25,33 @@
 #endif
 
 #include <thread>
+#include <vector>
 
 #include <android-base/stringprintf.h>
 #include <dns_sd.h>
 
+#include "adb_client.h"
 #include "adb_mdns.h"
 #include "adb_trace.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) {
+    int index = adb_DNSServiceIndexByName(regType);
+    return index == kADBTransportServiceRefIndex;
+}
 
 // Use adb_DNSServiceRefSockFD() instead of calling DNSServiceRefSockFD()
 // directly so that the socket is put through the appropriate compatibility
@@ -94,10 +110,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 +138,135 @@
         } else {
             Initialize();
         }
+
+        D("Client version: %d Service version: %d\n", clientVersion_, serviceVersion_);
     }
 
     void Connect(const sockaddr* address) {
-        char ip_addr[INET6_ADDRSTRLEN];
-        const void* ip_addr_data;
+        sa_family_ = address->sa_family;
         const char* addr_format;
 
-        if (address->sa_family == AF_INET) {
-            ip_addr_data =
-                &reinterpret_cast<const sockaddr_in*>(address)->sin_addr;
+        if (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;
+        } 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
+        } 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())) {
+            std::string response;
+            connect_device(android::base::StringPrintf(addr_format, 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 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);
+
   private:
-    std::string name_;
+    int clientVersion_ = ADB_SECURE_CLIENT_VERSION;
+    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_host,
+                                     adb_secure_foreach_service_callback cb) {
+    initAdbSecure();
+
+    for (auto service : services) {
+        auto hostname = service->hostTarget();
+        auto ip = service->ipAddress();
+        auto port = service->port();
+
+        if (wanted_host == "") {
+            cb(hostname.c_str(), ip.c_str(), port);
+        } else if (hostname == wanted_host) {
+            cb(hostname.c_str(), ip.c_str(), port);
+        }
+    }
+}
+
+// static
+void adb_secure_foreach_pairing_service(const char* host_name,
+                                        adb_secure_foreach_service_callback cb) {
+    ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices,
+                                    host_name ? host_name : "", cb);
+}
+
+// static
+void adb_secure_foreach_connect_service(const char* host_name,
+                                        adb_secure_foreach_service_callback cb) {
+    ResolvedService::forEachService(*ResolvedService::sAdbSecureConnectServices,
+                                    host_name ? host_name : "", cb);
+}
+
 static void DNSSD_API register_service_ip(DNSServiceRef /*sdRef*/,
                                           DNSServiceFlags /*flags*/,
                                           uint32_t /*interfaceIndex*/,
@@ -167,6 +279,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 +300,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 +325,62 @@
         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) {
+// 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,10 +390,14 @@
         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()) {
         delete resolved;
@@ -239,19 +408,18 @@
     }
 }
 
-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*/) {
+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*/) {
     D("Registering a transport.");
     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;
     }
 
@@ -262,21 +430,27 @@
 }
 
 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/daemon/mdns.cpp b/adb/daemon/mdns.cpp
index 3530f48..fa98340 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"
 
@@ -32,8 +33,8 @@
 
 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() {
     if (android::base::GetProperty("init.svc.mdnsd", "") == "running") {
@@ -60,34 +61,86 @@
     }
 }
 
-static void setup_mdns_thread() {
-    start_mdns();
+static void register_mdns_service(int index, int port) {
     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, hostname.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() {
+    register_mdns_service(kADBTransportServiceRefIndex, port);
+}
+
+static void setup_mdns_thread() {
+    start_mdns();
+
+    // 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);
+    }
+}
+
+// Public interface/////////////////////////////////////////////////////////////
+
 void setup_mdns(int port_in) {
     port = port_in;
     std::thread(setup_mdns_thread).detach();
@@ -95,3 +148,33 @@
     // TODO: Make this more robust against a hard kill.
     atexit(teardown_mdns);
 }
+
+void register_adb_secure_pairing_service(int port) {
+    std::thread([port]() {
+        register_mdns_service(kADBSecurePairingServiceRefIndex, port);
+    }).detach();
+}
+
+void unregister_adb_secure_pairing_service() {
+    std::thread([]() { unregister_mdns_service(kADBSecurePairingServiceRefIndex); }).detach();
+}
+
+bool is_adb_secure_pairing_service_registered() {
+    std::lock_guard<std::mutex> lock(mdns_lock);
+    return mdns_registered[kADBSecurePairingServiceRefIndex];
+}
+
+void register_adb_secure_connect_service(int port) {
+    std::thread([port]() {
+        register_mdns_service(kADBSecureConnectServiceRefIndex, port);
+    }).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..a18093b 100644
--- a/adb/daemon/mdns.h
+++ b/adb/daemon/mdns.h
@@ -19,4 +19,12 @@
 
 void setup_mdns(int port);
 
+void register_adb_secure_pairing_service(int port);
+void unregister_adb_secure_pairing_service(int port);
+bool is_adb_secure_pairing_service_registered();
+
+void register_adb_secure_connect_service(int port);
+void unregister_adb_secure_connect_service(int port);
+bool is_adb_secure_connect_service_registered();
+
 #endif  // _DAEMON_MDNS_H_
diff --git a/adb/tls/adb_ca_list.cpp b/adb/tls/adb_ca_list.cpp
index 8d37bbe..36afe42 100644
--- a/adb/tls/adb_ca_list.cpp
+++ b/adb/tls/adb_ca_list.cpp
@@ -32,13 +32,13 @@
 // 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 kAdbKeyIdentifierPrefix[] = "AdbKey-";
-static constexpr int kAdbKeyVersion = 0;
+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,
@@ -55,13 +55,13 @@
     // |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 {};
+        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 buf.data();
+    return std::make_optional(std::string(buf.data()));
 }
 
 }  // namespace
@@ -73,8 +73,7 @@
     // "O=AdbKey-0;CN=<key>;"
     CHECK(!key.empty());
 
-    std::string identifier = kAdbKeyIdentifierPrefix;
-    identifier += std::to_string(kAdbKeyVersion);
+    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()),
@@ -91,27 +90,34 @@
     CHECK(issuer);
 
     auto buf = GetX509NameTextByNid(issuer, kAdbKeyIdentifierNid);
-    if (!buf || !android::base::StartsWith(*buf, kAdbKeyIdentifierPrefix)) {
-        return {};
+    if (!buf) {
+        return std::nullopt;
     }
 
-    return GetX509NameTextByNid(issuer, kAdbKeyValueNid);
+    // 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) {
-        ss << std::setw(2) << (0x00FF & sha256[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 {};
+        return std::nullopt;
     }
 
     std::string result;
@@ -119,7 +125,7 @@
         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 {};
+            return std::nullopt;
         }
         result += static_cast<char>(std::stol(bytestr, nullptr, 16));
     }
diff --git a/adb/tls/include/adb/tls/tls_connection.h b/adb/tls/include/adb/tls/tls_connection.h
index ae70857..bc5b98a 100644
--- a/adb/tls/include/adb/tls/tls_connection.h
+++ b/adb/tls/include/adb/tls/tls_connection.h
@@ -55,16 +55,15 @@
 
     // 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. Use
-    // |EnableCertificateVerification(false)| to disable certificate
-    // verification.
+    // 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. Note that |cb| is
-    // only used if EnableCertificateVerification(false).
+    // 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
diff --git a/adb/tls/tests/tls_connection_test.cpp b/adb/tls/tests/tls_connection_test.cpp
index 880904b..27bc1c9 100644
--- a/adb/tls/tests/tls_connection_test.cpp
+++ b/adb/tls/tests/tls_connection_test.cpp
@@ -199,24 +199,10 @@
 static std::vector<CAIssuer> kCAIssuers = {
         {
                 {NID_commonName, {'a', 'b', 'c', 'd', 'e'}},
-                {NID_organizationName,
-                 {
-                         'd',
-                         'e',
-                         'f',
-                         'g',
-                 }},
+                {NID_organizationName, {'d', 'e', 'f', 'g'}},
         },
         {
-                {NID_commonName,
-                 {
-                         'h',
-                         'i',
-                         'j',
-                         'k',
-                         'l',
-                         'm',
-                 }},
+                {NID_commonName, {'h', 'i', 'j', 'k', 'l', 'm'}},
                 {NID_countryName, {'n', 'o'}},
         },
 };
@@ -224,8 +210,6 @@
 class AdbWifiTlsConnectionTest : public testing::Test {
   protected:
     virtual void SetUp() override {
-        // TODO: move client code in each test into its own thread, as the
-        // socket pair buffer is limited.
         android::base::Socketpair(SOCK_STREAM, &server_fd_, &client_fd_);
         server_ = TlsConnection::Create(TlsConnection::Role::Server, kTestRsa2048ServerCert,
                                         kTestRsa2048ServerPrivKey, server_fd_);
@@ -257,14 +241,8 @@
         return ret;
     }
 
-    void StartClientHandshakeAsync(bool expect_success) {
-        client_thread_ = std::thread([=]() {
-            if (expect_success) {
-                EXPECT_EQ(client_->DoHandshake(), TlsError::Success);
-            } else {
-                EXPECT_NE(client_->DoHandshake(), TlsError::Success);
-            }
-        });
+    void StartClientHandshakeAsync(TlsError expected) {
+        client_thread_ = std::thread([=]() { EXPECT_EQ(client_->DoHandshake(), expected); });
     }
 
     void WaitForClientConnection() {
@@ -313,45 +291,52 @@
     // Allow any certificate
     server_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
     client_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
-    StartClientHandshakeAsync(true);
+    StartClientHandshakeAsync(TlsError::Success);
 
     // Handshake should succeed
-    EXPECT_EQ(server_->DoHandshake(), TlsError::Success);
+    ASSERT_EQ(server_->DoHandshake(), TlsError::Success);
     WaitForClientConnection();
 
-    // Client write, server read
-    EXPECT_TRUE(client_->WriteFully(
-            std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+    // 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_);
-
-    // Client read, server write
     EXPECT_TRUE(server_->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_);
+
+    WaitForClientConnection();
 }
 
 TEST_F(AdbWifiTlsConnectionTest, NoTrustedCertificates) {
-    StartClientHandshakeAsync(false);
+    StartClientHandshakeAsync(TlsError::CertificateRejected);
 
     // Handshake should not succeed
-    EXPECT_NE(server_->DoHandshake(), TlsError::Success);
+    ASSERT_EQ(server_->DoHandshake(), TlsError::PeerRejectedCertificate);
     WaitForClientConnection();
 
-    // Client write, server read should fail
-    EXPECT_FALSE(client_->WriteFully(
-            std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+    // 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);
-
-    // Client read, server write should fail
     EXPECT_FALSE(server_->WriteFully(
             std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
-    data = client_->ReadFully(msg_.size());
-    EXPECT_EQ(data.size(), 0);
+
+    WaitForClientConnection();
 }
 
 TEST_F(AdbWifiTlsConnectionTest, AddTrustedCertificates) {
@@ -359,23 +344,26 @@
     EXPECT_TRUE(client_->AddTrustedCertificate(kTestRsa2048ServerCert));
     EXPECT_TRUE(server_->AddTrustedCertificate(kTestRsa2048ClientCert));
 
-    StartClientHandshakeAsync(true);
+    StartClientHandshakeAsync(TlsError::Success);
 
     // Handshake should succeed
-    EXPECT_EQ(server_->DoHandshake(), TlsError::Success);
+    ASSERT_EQ(server_->DoHandshake(), TlsError::Success);
     WaitForClientConnection();
 
-    // Client write, server read
-    EXPECT_TRUE(client_->WriteFully(
-            std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+    // 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_);
-
-    // Client read, server write
     EXPECT_TRUE(server_->WriteFully(
             std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
-    data = client_->ReadFully(msg_.size());
-    EXPECT_EQ(data, msg_);
+
+    WaitForClientConnection();
 }
 
 TEST_F(AdbWifiTlsConnectionTest, AddTrustedCertificates_ClientWrongCert) {
@@ -387,23 +375,26 @@
     // 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(true);
+    StartClientHandshakeAsync(TlsError::Success);
 
     // Handshake should fail for server, succeed for client
-    EXPECT_NE(server_->DoHandshake(), TlsError::Success);
+    ASSERT_EQ(server_->DoHandshake(), TlsError::CertificateRejected);
     WaitForClientConnection();
 
-    // Client write succeeds, server read should fail
-    EXPECT_TRUE(client_->WriteFully(
-            std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+    // 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);
-
-    // Client read, server write should fail
     EXPECT_FALSE(server_->WriteFully(
             std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
-    data = client_->ReadFully(msg_.size());
-    EXPECT_EQ(data.size(), 0);
+
+    WaitForClientConnection();
 }
 
 TEST_F(AdbWifiTlsConnectionTest, ExportKeyingMaterial) {
@@ -415,10 +406,10 @@
     EXPECT_TRUE(client_->AddTrustedCertificate(kTestRsa2048ServerCert));
     EXPECT_TRUE(server_->AddTrustedCertificate(kTestRsa2048ClientCert));
 
-    StartClientHandshakeAsync(true);
+    StartClientHandshakeAsync(TlsError::Success);
 
     // Handshake should succeed
-    EXPECT_EQ(server_->DoHandshake(), TlsError::Success);
+    ASSERT_EQ(server_->DoHandshake(), TlsError::Success);
     WaitForClientConnection();
 
     // Verify the client and server's exported key material match.
@@ -439,10 +430,10 @@
     // Client handshake should succeed, because in TLS 1.3, client does not
     // realize that the peer rejected the certificate until after a read
     // operation.
-    client_thread_ = std::thread([&]() { EXPECT_EQ(client_->DoHandshake(), TlsError::Success); });
+    StartClientHandshakeAsync(TlsError::Success);
 
     // Server handshake should fail
-    EXPECT_EQ(server_->DoHandshake(), TlsError::CertificateRejected);
+    ASSERT_EQ(server_->DoHandshake(), TlsError::CertificateRejected);
     WaitForClientConnection();
 }
 
@@ -455,11 +446,10 @@
     server_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 0; });
 
     // Client handshake should fail because server rejects everything
-    client_thread_ = std::thread(
-            [&]() { EXPECT_EQ(client_->DoHandshake(), TlsError::PeerRejectedCertificate); });
+    StartClientHandshakeAsync(TlsError::PeerRejectedCertificate);
 
     // Server handshake should fail
-    EXPECT_EQ(server_->DoHandshake(), TlsError::CertificateRejected);
+    ASSERT_EQ(server_->DoHandshake(), TlsError::CertificateRejected);
     WaitForClientConnection();
 }
 
@@ -469,11 +459,10 @@
     // Server accepts all
     server_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
     // Client handshake should fail
-    client_thread_ = std::thread(
-            [&]() { EXPECT_EQ(client_->DoHandshake(), TlsError::CertificateRejected); });
+    StartClientHandshakeAsync(TlsError::CertificateRejected);
 
     // Server handshake should fail
-    EXPECT_EQ(server_->DoHandshake(), TlsError::PeerRejectedCertificate);
+    ASSERT_EQ(server_->DoHandshake(), TlsError::PeerRejectedCertificate);
     WaitForClientConnection();
 }
 
@@ -488,15 +477,15 @@
     server_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
 
     // Client handshake should fail
-    client_thread_ = std::thread(
-            [&]() { EXPECT_EQ(client_->DoHandshake(), TlsError::CertificateRejected); });
+    StartClientHandshakeAsync(TlsError::CertificateRejected);
 
     // Server handshake should fail
-    EXPECT_EQ(server_->DoHandshake(), TlsError::PeerRejectedCertificate);
+    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);
 
@@ -504,23 +493,26 @@
     EXPECT_TRUE(server_->AddTrustedCertificate(kTestRsa2048UnknownCert));
 
     // Handshake should fail for client
-    StartClientHandshakeAsync(false);
+    StartClientHandshakeAsync(TlsError::PeerRejectedCertificate);
 
     // Handshake should fail for server
-    EXPECT_NE(server_->DoHandshake(), TlsError::Success);
+    ASSERT_EQ(server_->DoHandshake(), TlsError::CertificateRejected);
     WaitForClientConnection();
 
-    // Client write fails, server read should fail
-    EXPECT_FALSE(client_->WriteFully(
-            std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+    // 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);
-
-    // Client read, server write should fail
     EXPECT_FALSE(server_->WriteFully(
             std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
-    data = client_->ReadFully(msg_.size());
-    EXPECT_EQ(data.size(), 0);
+
+    WaitForClientConnection();
 }
 
 TEST_F(AdbWifiTlsConnectionTest, SetClientCAList_Empty) {
@@ -569,12 +561,12 @@
             return 1;
         });
         // Client handshake should succeed
-        EXPECT_EQ(client_->DoHandshake(), TlsError::Success);
+        ASSERT_EQ(client_->DoHandshake(), TlsError::Success);
     });
 
     EXPECT_TRUE(server_->AddTrustedCertificate(kTestRsa2048UnknownCert));
     // Server handshake should succeed
-    EXPECT_EQ(server_->DoHandshake(), TlsError::Success);
+    ASSERT_EQ(server_->DoHandshake(), TlsError::Success);
     client_thread_.join();
 }
 
@@ -604,12 +596,12 @@
             return 1;
         });
         // Client handshake should succeed
-        EXPECT_EQ(client_->DoHandshake(), TlsError::Success);
+        ASSERT_EQ(client_->DoHandshake(), TlsError::Success);
     });
 
     server_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
     // Server handshake should succeed
-    EXPECT_EQ(server_->DoHandshake(), TlsError::Success);
+    ASSERT_EQ(server_->DoHandshake(), TlsError::Success);
     client_thread_.join();
 }
 }  // namespace tls
diff --git a/adb/tls/tls_connection.cpp b/adb/tls/tls_connection.cpp
index 7df6ef4..853cdac 100644
--- a/adb/tls/tls_connection.cpp
+++ b/adb/tls/tls_connection.cpp
@@ -61,6 +61,7 @@
     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_;
@@ -75,15 +76,19 @@
     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) << "Initializing adbwifi TlsConnection";
+    LOG(INFO) << RoleToString() << "Initializing adbwifi TlsConnection";
     cert_ = BufferFromPEM(cert);
+    CHECK(cert_);
     priv_key_ = EvpPkeyFromPEM(priv_key);
+    CHECK(priv_key_);
 }
 
 TlsConnectionImpl::~TlsConnectionImpl() {
@@ -149,7 +154,7 @@
     // Create X509 buffer from the certificate string
     auto buf = X509FromBuffer(BufferFromPEM(cert));
     if (buf == nullptr) {
-        LOG(ERROR) << "Failed to create a X509 buffer for the certificate.";
+        LOG(ERROR) << RoleToString() << "Failed to create a X509 buffer for the certificate.";
         return false;
     }
     known_certificates_.push_back(std::move(buf));
@@ -205,8 +210,7 @@
 }
 
 TlsConnection::TlsError TlsConnectionImpl::DoHandshake() {
-    int err = -1;
-    LOG(INFO) << "Starting adbwifi tls handshake";
+    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
@@ -214,14 +218,14 @@
     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) << "Failed to create SSL context";
+        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) << "Unable to add certificates into the X509_STORE";
+            LOG(ERROR) << RoleToString() << "Unable to add certificates into the X509_STORE";
             return TlsError::UnknownFailure;
         }
     }
@@ -248,7 +252,8 @@
     };
     if (!SSL_CTX_set_chain_and_key(ssl_ctx_.get(), cert_chain.data(), cert_chain.size(),
                                    priv_key_.get(), nullptr)) {
-        LOG(ERROR) << "Unable to register the certificate chain file and private key ["
+        LOG(ERROR) << RoleToString()
+                   << "Unable to register the certificate chain file and private key ["
                    << SSLErrorString() << "]";
         Invalidate();
         return TlsError::UnknownFailure;
@@ -259,19 +264,21 @@
     // 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) << "SSL_set_fd failed. [" << SSLErrorString() << "]";
+        LOG(ERROR) << RoleToString() << "SSL_set_fd failed. [" << SSLErrorString() << "]";
         return TlsError::UnknownFailure;
     }
+
     switch (role_) {
         case Role::Server:
-            err = SSL_accept(ssl_.get());
+            SSL_set_accept_state(ssl_.get());
             break;
         case Role::Client:
-            err = SSL_connect(ssl_.get());
+            SSL_set_connect_state(ssl_.get());
             break;
     }
-    if (err != 1) {
-        LOG(ERROR) << "Handshake failed in SSL_accept/SSL_connect [" << SSLErrorString() << "]";
+    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);
@@ -281,16 +288,16 @@
         uint8_t check;
         // Try to peek one byte for any failures. This assumes on success that
         // the server actually sends something.
-        err = SSL_peek(ssl_.get(), &check, 1);
-        if (err <= 0) {
-            LOG(ERROR) << "Post-handshake SSL_peek failed [" << SSLErrorString() << "]";
+        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) << "Handshake succeeded.";
+    LOG(INFO) << RoleToString() << "Handshake succeeded.";
     return TlsError::Success;
 }
 
@@ -311,7 +318,7 @@
 bool TlsConnectionImpl::ReadFully(void* buf, size_t size) {
     CHECK_GT(size, 0U);
     if (!ssl_) {
-        LOG(ERROR) << "Tried to read on a null SSL connection";
+        LOG(ERROR) << RoleToString() << "Tried to read on a null SSL connection";
         return false;
     }
 
@@ -321,7 +328,7 @@
         int bytes_read =
                 SSL_read(ssl_.get(), p8 + offset, std::min(static_cast<size_t>(INT_MAX), size));
         if (bytes_read <= 0) {
-            LOG(WARNING) << "SSL_read failed [" << SSLErrorString() << "]";
+            LOG(ERROR) << RoleToString() << "SSL_read failed [" << SSLErrorString() << "]";
             return false;
         }
         size -= bytes_read;
@@ -333,7 +340,7 @@
 bool TlsConnectionImpl::WriteFully(std::string_view data) {
     CHECK(!data.empty());
     if (!ssl_) {
-        LOG(ERROR) << "Tried to read on a null SSL connection";
+        LOG(ERROR) << RoleToString() << "Tried to read on a null SSL connection";
         return false;
     }
 
@@ -341,7 +348,7 @@
         int bytes_out = SSL_write(ssl_.get(), data.data(),
                                   std::min(static_cast<size_t>(INT_MAX), data.size()));
         if (bytes_out <= 0) {
-            LOG(WARNING) << "SSL_write failed [" << SSLErrorString() << "]";
+            LOG(ERROR) << RoleToString() << "SSL_write failed [" << SSLErrorString() << "]";
             return false;
         }
         data = data.substr(bytes_out);
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/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 ba53615..2fe06fb 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 {
@@ -1739,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_;
 }
@@ -2383,7 +2388,7 @@
     return AutoUnmountDevice::New(device_->GetMetadataDir());
 }
 
-UpdateState SnapshotManager::InitiateMergeAndWait() {
+UpdateState SnapshotManager::InitiateMergeAndWait(SnapshotMergeReport* stats_report) {
     {
         auto lock = LockExclusive();
         // Sync update state from file with bootloader.
@@ -2393,6 +2398,8 @@
         }
     }
 
+    SnapshotMergeStats merge_stats(*this);
+
     unsigned int last_progress = 0;
     auto callback = [&]() -> void {
         double progress;
@@ -2405,7 +2412,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;
@@ -2415,6 +2424,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;
@@ -2423,9 +2437,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..c48509e
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_stats.cpp
@@ -0,0 +1,80 @@
+// 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;
+    }
+}
+
+void SnapshotMergeStats::Start() {
+    SnapshotMergeReport report;
+    report.set_resume_count(0);
+    report.set_state(UpdateState::None);
+
+    std::string contents;
+    if (!report.SerializeToString(&contents)) {
+        LOG(ERROR) << "Unable to serialize SnapshotMergeStats.";
+        return;
+    }
+    auto file_path = parent_.GetMergeStateFilePath();
+    if (!WriteStringToFileAtomic(contents, file_path)) {
+        PLOG(ERROR) << "Could not write to merge statistics file";
+        return;
+    }
+}
+
+void SnapshotMergeStats::Resume() {
+    std::string contents;
+    if (!android::base::ReadFileToString(parent_.GetMergeStateFilePath(), &contents)) {
+        PLOG(INFO) << "Read merge statistics file failed";
+        return;
+    }
+
+    if (!report_.ParseFromString(contents)) {
+        LOG(ERROR) << "Unable to parse merge statistics file as SnapshotMergeReport";
+        return;
+    }
+
+    report_.set_resume_count(report_.resume_count() + 1);
+}
+
+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..1ca9156
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_stats.h
@@ -0,0 +1,42 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <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:
+    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/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index b85f23f..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;
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/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");