Merge "Add UNSUPPORTED to health HAL 2.1 values"
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/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/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");