Openscreen discovery integration for adb client.

The openscreen discovery library will allow adb client on all three
platforms to discover mdns services without relying on having Bonjour on
the host machine.

If the host does have bonjour running, we will fallback to using the
MdnsResponder client.

The openscreen discovery code path will be disabled by default for now.
You can enable it by setting ADB_MDNS_OPENSCREEN=1. Check `adb
host-features` string for 'openscreen_mdns' feature.

Test: test_adb.py

Bug: 152884934
Change-Id: Ief153afe7f080cb21134897d8eaf00d90c6983d8
diff --git a/Android.bp b/Android.bp
index 5df1cf0..4c61d5e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -15,6 +15,7 @@
 tidy_errors = [
     "-*",
     "bugprone-inaccurate-erase",
+    "bugprone-use-after-move",
 ]
 
 cc_defaults {
@@ -76,6 +77,7 @@
                 "-lws2_32",
                 "-lgdi32",
                 "-luserenv",
+                "-liphlpapi",
             ],
         },
     },
@@ -181,6 +183,7 @@
     "adb.cpp",
     "adb_io.cpp",
     "adb_listeners.cpp",
+    "adb_mdns.cpp",
     "adb_trace.cpp",
     "adb_unique_fd.cpp",
     "adb_utils.cpp",
@@ -233,13 +236,19 @@
     defaults: ["adb_defaults"],
 
     srcs: libadb_srcs + [
+        "client/openscreen/mdns_service_info.cpp",
+        "client/openscreen/mdns_service_watcher.cpp",
+        "client/openscreen/platform/logging.cpp",
+        "client/openscreen/platform/task_runner.cpp",
+        "client/openscreen/platform/udp_socket.cpp",
         "client/auth.cpp",
         "client/adb_wifi.cpp",
         "client/usb_libusb.cpp",
         "client/usb_dispatch.cpp",
         "client/transport_local.cpp",
-        "client/transport_mdns.cpp",
+        "client/mdnsresponder_client.cpp",
         "client/mdns_utils.cpp",
+        "client/transport_mdns.cpp",
         "client/transport_usb.cpp",
         "client/pairing/pairing_client.cpp",
     ],
@@ -279,6 +288,8 @@
         "libutils",
         "liblog",
         "libcutils",
+        "libopenscreen-discovery",
+        "libopenscreen-platform-impl",
         "libprotobuf-cpp-lite",
     ],
 }
@@ -344,6 +355,8 @@
         "liblog",
         "libmdnssd",
         "libdiagnose_usb",
+        "libopenscreen-discovery",
+        "libopenscreen-platform-impl",
         "libprotobuf-cpp-lite",
         "libssl",
         "libusb",
@@ -406,6 +419,8 @@
         "liblog",
         "liblz4",
         "libmdnssd",
+        "libopenscreen-discovery",
+        "libopenscreen-platform-impl",
         "libprotobuf-cpp-full",
         "libssl",
         "libusb",
@@ -907,6 +922,8 @@
         "libfastdeploy_host",
         "liblog",
         "libmdnssd",
+        "libopenscreen-discovery",
+        "libopenscreen-platform-impl",
         "libprotobuf-cpp-lite",
         "libssl",
         "libusb",
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
new file mode 100644
index 0000000..dcf92be
--- /dev/null
+++ b/PREUPLOAD.cfg
@@ -0,0 +1,8 @@
+[Builtin Hooks]
+clang_format = true
+
+[Builtin Hooks Options]
+clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
+
+[Hook Scripts]
+aosp_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "."
diff --git a/adb.cpp b/adb.cpp
index 08986b7..0f0fc43 100644
--- a/adb.cpp
+++ b/adb.cpp
@@ -51,9 +51,9 @@
 #include "adb_auth.h"
 #include "adb_io.h"
 #include "adb_listeners.h"
+#include "adb_mdns.h"
 #include "adb_unique_fd.h"
 #include "adb_utils.h"
-#include "adb_wifi.h"
 #include "sysdeps/chrono.h"
 #include "transport.h"
 
diff --git a/adb_mdns.cpp b/adb_mdns.cpp
new file mode 100644
index 0000000..087a9ab
--- /dev/null
+++ b/adb_mdns.cpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2020 Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG TRANSPORT
+
+#include "adb_mdns.h"
+
+#include <set>
+
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include "adb_trace.h"
+
+#define ADB_SECURE_SERVICE_VERSION_TXT_RECORD(ver) ("v=" #ver)
+
+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);
+
+#define ADB_FULL_MDNS_SERVICE_TYPE(atype) ("_" atype "._tcp")
+const char* kADBDNSServices[] = {ADB_FULL_MDNS_SERVICE_TYPE(ADB_MDNS_SERVICE_TYPE),
+                                 ADB_FULL_MDNS_SERVICE_TYPE(ADB_MDNS_TLS_PAIRING_TYPE),
+                                 ADB_FULL_MDNS_SERVICE_TYPE(ADB_MDNS_TLS_CONNECT_TYPE)};
+
+const char* kADBDNSServiceTxtRecords[] = {
+        nullptr,
+        kADBSecurePairingServiceTxtRecord,
+        kADBSecureConnectServiceTxtRecord,
+};
+
+#if ADB_HOST
+namespace {
+
+std::atomic<bool> g_allowedlist_configured{false};
+[[clang::no_destroy]] std::set<int> g_autoconn_allowedlist;
+
+void config_auto_connect_services() {
+    bool expected = false;
+    if (!g_allowedlist_configured.compare_exchange_strong(expected, true)) {
+        return;
+    }
+
+    // ADB_MDNS_AUTO_CONNECT is a comma-delimited list of mdns services
+    // that are allowed to auto-connect. By default, only allow "adb-tls-connect"
+    // to auto-connect, since this is filtered down to auto-connect only to paired
+    // devices.
+    g_autoconn_allowedlist.insert(kADBSecureConnectServiceRefIndex);
+    const char* srvs = getenv("ADB_MDNS_AUTO_CONNECT");
+    if (!srvs) {
+        return;
+    }
+
+    if (strcmp(srvs, "0") == 0) {
+        D("Disabling all auto-connecting");
+        g_autoconn_allowedlist.clear();
+        return;
+    }
+
+    if (strcmp(srvs, "all") == 0) {
+        D("Allow all auto-connecting");
+        g_autoconn_allowedlist.insert(kADBTransportServiceRefIndex);
+        return;
+    }
+
+    // Selectively choose which services to allow auto-connect.
+    // E.g. ADB_MDNS_AUTO_CONNECT=adb,adb-tls-connect would allow
+    // _adb._tcp and _adb-tls-connnect._tcp services to auto-connect.
+    auto srvs_list = android::base::Split(srvs, ",");
+    std::set<int> new_allowedlist;
+    for (const auto& item : srvs_list) {
+        auto full_srv = android::base::StringPrintf("_%s._tcp", item.data());
+        std::optional<int> idx = adb_DNSServiceIndexByName(full_srv);
+        if (idx.has_value()) {
+            new_allowedlist.insert(*idx);
+        }
+    }
+
+    if (!new_allowedlist.empty()) {
+        g_autoconn_allowedlist = std::move(new_allowedlist);
+    }
+
+    std::string res;
+    std::for_each(g_autoconn_allowedlist.begin(), g_autoconn_allowedlist.end(), [&](const int& i) {
+        res += kADBDNSServices[i];
+        res += ",";
+    });
+    D("mdns auto-connect allowedlist: [%s]", res.data());
+}
+
+}  // namespace
+
+std::optional<int> adb_DNSServiceIndexByName(std::string_view reg_type) {
+    for (int i = 0; i < kNumADBDNSServices; ++i) {
+        if (!strncmp(reg_type.data(), kADBDNSServices[i], strlen(kADBDNSServices[i]))) {
+            return i;
+        }
+    }
+    return std::nullopt;
+}
+
+bool adb_DNSServiceShouldAutoConnect(std::string_view reg_type, std::string_view service_name) {
+    config_auto_connect_services();
+
+    // Try to auto-connect to any "_adb" or "_adb-tls-connect" services excluding emulator services.
+    std::optional<int> index = adb_DNSServiceIndexByName(reg_type);
+    if (!index ||
+        (index != kADBTransportServiceRefIndex && index != kADBSecureConnectServiceRefIndex)) {
+        return false;
+    }
+    if (g_autoconn_allowedlist.find(*index) == g_autoconn_allowedlist.end()) {
+        D("Auto-connect for reg_type '%s' disabled", reg_type.data());
+        return false;
+    }
+    // Ignore adb-EMULATOR* service names, as it interferes with the
+    // emulator ports that are already connected.
+    if (android::base::StartsWith(service_name, "adb-EMULATOR")) {
+        LOG(INFO) << "Ignoring emulator transport service [" << service_name << "]";
+        return false;
+    }
+    return true;
+}
+
+#endif  // ADB_HOST
diff --git a/adb_mdns.h b/adb_mdns.h
index 3111248..471ec25 100644
--- a/adb_mdns.h
+++ b/adb_mdns.h
@@ -14,10 +14,11 @@
  * limitations under the License.
  */
 
-#ifndef _ADB_MDNS_H_
-#define _ADB_MDNS_H_
+#pragma once
 
-#include <android-base/macros.h>
+#include <optional>
+#include <string>
+#include <string_view>
 
 // The rules for Service Names [RFC6335] state that they may be no more
 // than fifteen characters long (not counting the mandatory underscore),
@@ -28,65 +29,70 @@
 #define ADB_MDNS_TLS_PAIRING_TYPE "adb-tls-pairing"
 #define ADB_MDNS_TLS_CONNECT_TYPE "adb-tls-connect"
 
-const int kADBTransportServiceRefIndex = 0;
-const int kADBSecurePairingServiceRefIndex = 1;
-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);
+constexpr int kADBTransportServiceRefIndex = 0;
+constexpr int kADBSecurePairingServiceRefIndex = 1;
+constexpr int kADBSecureConnectServiceRefIndex = 2;
+constexpr int kNumADBDNSServices = 3;
 
-#define ADB_FULL_MDNS_SERVICE_TYPE(atype) ("_" atype "._tcp")
-const char* kADBDNSServices[] = {ADB_FULL_MDNS_SERVICE_TYPE(ADB_MDNS_SERVICE_TYPE),
-                                 ADB_FULL_MDNS_SERVICE_TYPE(ADB_MDNS_TLS_PAIRING_TYPE),
-                                 ADB_FULL_MDNS_SERVICE_TYPE(ADB_MDNS_TLS_CONNECT_TYPE)};
+extern const char* _Nonnull kADBSecurePairingServiceTxtRecord;
+extern const char* _Nonnull kADBSecureConnectServiceTxtRecord;
 
-const char* kADBDNSServiceTxtRecords[] = {
-        nullptr,
-        kADBSecurePairingServiceTxtRecord,
-        kADBSecureConnectServiceTxtRecord,
+extern const char* _Nonnull kADBDNSServices[kNumADBDNSServices];
+extern const char* _Nonnull kADBDNSServiceTxtRecords[kNumADBDNSServices];
+
+#if ADB_HOST
+// 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 std::string& service_name, const std::string& reg_type,
+                           const std::string& ip_address, uint16_t port)>;
+
+// Tries to connect to a |service_name| if found. Returns true if found and
+// connected, false otherwise.
+bool adb_secure_connect_by_service_name(const std::string& instance_name);
+
+// Returns the index in kADBDNSServices array if |reg_type| matches a service name, otherwise
+// std::nullopt.
+std::optional<int> adb_DNSServiceIndexByName(std::string_view reg_type);
+// Returns true if auto-connect is allowed for |service_name| and |instance_name|.
+// See ADB_MDNS_AUTO_CONNECT environment variable for more info.
+bool adb_DNSServiceShouldAutoConnect(std::string_view service_name, std::string_view instance_name);
+
+void mdns_cleanup(void);
+std::string mdns_check();
+std::string mdns_list_discovered_services();
+
+struct MdnsInfo {
+    std::string service_name;
+    std::string service_type;
+    std::string addr;
+    uint16_t port = 0;
+
+    MdnsInfo(std::string_view name, std::string_view type, std::string_view addr, uint16_t port)
+        : service_name(name), service_type(type), addr(addr), port(port) {}
 };
 
-const int kNumADBDNSServices = arraysize(kADBDNSServices);
+std::optional<MdnsInfo> mdns_get_connect_service_info(const std::string& name);
+std::optional<MdnsInfo> mdns_get_pairing_service_info(const std::string& name);
 
-#endif
+// TODO: Remove once openscreen has support for bonjour client APIs.
+struct AdbMdnsResponderFuncs {
+    std::string (*_Nonnull mdns_check)(void);
+    std::string (*_Nonnull mdns_list_discovered_services)(void);
+    std::optional<MdnsInfo> (*_Nonnull mdns_get_connect_service_info)(const std::string&);
+    std::optional<MdnsInfo> (*_Nonnull mdns_get_pairing_service_info)(const std::string&);
+    void (*_Nonnull mdns_cleanup)(void);
+    bool (*_Nonnull adb_secure_connect_by_service_name)(const std::string&);
+};  // AdbBonjourCallbacks
+
+// TODO: Remove once openscreen has support for bonjour client APIs.
+// Start mdns discovery using MdnsResponder backend. Fills in AdbMdnsResponderFuncs for adb mdns
+// related functions.
+AdbMdnsResponderFuncs StartMdnsResponderDiscovery();
+#endif  // ADB_HOST
diff --git a/adb_wifi.h b/adb_wifi.h
index 42f414b..b905e86 100644
--- a/adb_wifi.h
+++ b/adb_wifi.h
@@ -28,22 +28,6 @@
                           std::string& response);
 bool adb_wifi_is_known_host(const std::string& host);
 
-std::string mdns_check();
-std::string mdns_list_discovered_services();
-
-struct MdnsInfo {
-    std::string service_name;
-    std::string service_type;
-    std::string addr;
-    uint16_t port = 0;
-
-    MdnsInfo(std::string_view name, std::string_view type, std::string_view addr, uint16_t port)
-        : service_name(name), service_type(type), addr(addr), port(port) {}
-};
-
-std::optional<MdnsInfo> mdns_get_connect_service_info(std::string_view name);
-std::optional<MdnsInfo> mdns_get_pairing_service_info(std::string_view name);
-
 #else  // !ADB_HOST
 
 struct AdbdAuthContext;
diff --git a/client/adb_client.h b/client/adb_client.h
index caf4e86..d0a4c4e 100644
--- a/client/adb_client.h
+++ b/client/adb_client.h
@@ -87,20 +87,3 @@
 // Globally acccesible argv/envp, for the purpose of re-execing adb.
 extern const char* _Nullable * _Nullable __adb_argv;
 extern const char* _Nullable * _Nullable __adb_envp;
-
-// ADB Secure DNS service interface. Used to query what ADB Secure DNS services have been
-// resolved, and to run some kind of callback for each one.
-using adb_secure_foreach_service_callback =
-        std::function<void(const char* _Nonnull service_name, const char* _Nonnull reg_type,
-                           const char* _Nonnull ip_address, uint16_t port)>;
-
-// Queries pairing/connect services that have been discovered and resolved.
-// If |host_name| is not null, run |cb| only for services
-// matching |host_name|. Otherwise, run for all services.
-void adb_secure_foreach_pairing_service(const char* _Nullable service_name,
-                                        adb_secure_foreach_service_callback cb);
-void adb_secure_foreach_connect_service(const char* _Nullable service_name,
-                                        adb_secure_foreach_service_callback cb);
-// Tries to connect to a |service_name| if found. Returns true if found and
-// connected, false otherwise.
-bool adb_secure_connect_by_service_name(const char* _Nonnull service_name);
diff --git a/client/adb_wifi.cpp b/client/adb_wifi.cpp
index 61a9a48..e3e853a 100644
--- a/client/adb_wifi.cpp
+++ b/client/adb_wifi.cpp
@@ -28,6 +28,7 @@
 
 #include "adb_auth.h"
 #include "adb_known_hosts.pb.h"
+#include "adb_mdns.h"
 #include "adb_utils.h"
 #include "client/adb_client.h"
 #include "sysdeps.h"
@@ -250,5 +251,5 @@
     // Write to adb_known_hosts
     write_known_host_to_file(device_guid);
     // Try to auto-connect.
-    adb_secure_connect_by_service_name(device_guid.c_str());
+    adb_secure_connect_by_service_name(device_guid);
 }
diff --git a/client/main.cpp b/client/main.cpp
index a19bd6d..0f6e7d7 100644
--- a/client/main.cpp
+++ b/client/main.cpp
@@ -34,6 +34,7 @@
 #include "adb_auth.h"
 #include "adb_client.h"
 #include "adb_listeners.h"
+#include "adb_mdns.h"
 #include "adb_utils.h"
 #include "adb_wifi.h"
 #include "client/usb.h"
@@ -67,9 +68,11 @@
     //   1. close_smartsockets, so that we don't get any new clients
     //   2. kick_all_transports, to avoid writing only part of a packet to a transport.
     //   3. usb_cleanup, to tear down the USB stack.
+    //   4. mdns_cleanup, to tear down mdns stack.
     close_smartsockets();
     kick_all_transports();
     usb_cleanup();
+    mdns_cleanup();
 }
 
 static void intentionally_leak() {
diff --git a/client/mdnsresponder_client.cpp b/client/mdnsresponder_client.cpp
new file mode 100644
index 0000000..6a95f70
--- /dev/null
+++ b/client/mdnsresponder_client.cpp
@@ -0,0 +1,697 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG TRANSPORT
+
+#include "transport.h"
+
+#ifdef _WIN32
+#include <winsock2.h>
+#else
+#include <arpa/inet.h>
+#endif
+
+#include <memory>
+#include <thread>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <dns_sd.h>
+
+#include "adb_client.h"
+#include "adb_mdns.h"
+#include "adb_trace.h"
+#include "adb_utils.h"
+#include "adb_wifi.h"
+#include "client/mdns_utils.h"
+#include "fdevent/fdevent.h"
+#include "sysdeps.h"
+
+// TODO: Remove this file once openscreen has bonjour client APIs implemented.
+namespace {
+
+DNSServiceRef g_service_refs[kNumADBDNSServices];
+fdevent* g_service_ref_fdes[kNumADBDNSServices];
+
+// Use adb_DNSServiceRefSockFD() instead of calling DNSServiceRefSockFD()
+// directly so that the socket is put through the appropriate compatibility
+// layers to work with the rest of ADB's internal APIs.
+int adb_DNSServiceRefSockFD(DNSServiceRef ref) {
+    return adb_register_socket(DNSServiceRefSockFD(ref));
+}
+#define DNSServiceRefSockFD ___xxx_DNSServiceRefSockFD
+
+void DNSSD_API register_service_ip(DNSServiceRef sdref, DNSServiceFlags flags,
+                                   uint32_t interface_index, DNSServiceErrorType error_code,
+                                   const char* hostname, const sockaddr* address, uint32_t ttl,
+                                   void* context);
+
+void pump_service_ref(int /*fd*/, unsigned ev, void* data) {
+    DNSServiceRef* ref = reinterpret_cast<DNSServiceRef*>(data);
+
+    if (ev & FDE_READ) DNSServiceProcessResult(*ref);
+}
+
+class AsyncServiceRef {
+  public:
+    bool Initialized() const { return initialized_; }
+
+    void DestroyServiceRef() {
+        if (!initialized_) {
+            return;
+        }
+
+        // Order matters here! Must destroy the fdevent first since it has a
+        // reference to |sdref_|.
+        fdevent_destroy(fde_);
+        D("DNSServiceRefDeallocate(sdref=%p)", sdref_);
+        DNSServiceRefDeallocate(sdref_);
+        initialized_ = false;
+    }
+
+    virtual ~AsyncServiceRef() { DestroyServiceRef(); }
+
+  protected:
+    DNSServiceRef sdref_;
+
+    void Initialize() {
+        fde_ = fdevent_create(adb_DNSServiceRefSockFD(sdref_), pump_service_ref, &sdref_);
+        if (fde_ == nullptr) {
+            D("Unable to create fdevent");
+            return;
+        }
+        fdevent_set(fde_, FDE_READ);
+        initialized_ = true;
+    }
+
+  private:
+    bool initialized_ = false;
+    fdevent* fde_;
+};
+
+class ResolvedService : public AsyncServiceRef {
+  public:
+    virtual ~ResolvedService() = default;
+
+    ResolvedService(const std::string& service_name, const std::string& reg_type,
+                    uint32_t interface_index, const std::string& host_target, uint16_t port,
+                    int version)
+        : service_name_(service_name),
+          reg_type_(reg_type),
+          host_target_(host_target),
+          port_(port),
+          sa_family_(0),
+          service_version_(version) {
+        /* TODO: We should be able to get IPv6 support by adding
+         * kDNSServiceProtocol_IPv6 to the flags below. However, when we do
+         * this, we get served link-local addresses that are usually useless to
+         * connect to. What's more, we seem to /only/ get those and nothing else.
+         * If we want IPv6 in the future we'll have to figure out why.
+         */
+        DNSServiceErrorType ret = DNSServiceGetAddrInfo(
+                &sdref_, 0, interface_index, kDNSServiceProtocol_IPv4, host_target_.c_str(),
+                register_service_ip, reinterpret_cast<void*>(this));
+
+        if (ret != kDNSServiceErr_NoError) {
+            D("Got %d from DNSServiceGetAddrInfo.", ret);
+        } else {
+            D("DNSServiceGetAddrInfo(sdref=%p, host_target=%s)", sdref_, host_target_.c_str());
+            Initialize();
+        }
+
+        D("Client version: %d Service version: %d\n", clientVersion_, service_version_);
+    }
+
+    bool ConnectSecureWifiDevice() {
+        if (!adb_wifi_is_known_host(service_name_)) {
+            LOG(INFO) << "service_name=" << service_name_ << " not in keystore";
+            return false;
+        }
+
+        std::string response;
+        connect_device(
+                android::base::StringPrintf("%s.%s", service_name_.c_str(), reg_type_.c_str()),
+                &response);
+        D("Secure connect to %s regtype %s (%s:%hu) : %s", service_name_.c_str(), reg_type_.c_str(),
+          ip_addr_.c_str(), port_, response.c_str());
+        return true;
+    }
+
+    bool RegisterIpAddress(const sockaddr* address) {
+        sa_family_ = address->sa_family;
+
+        const void* ip_addr_data;
+        if (sa_family_ == AF_INET) {
+            ip_addr_data = &reinterpret_cast<const sockaddr_in*>(address)->sin_addr;
+            addr_format_ = "%s:%hu";
+        } else if (sa_family_ == AF_INET6) {
+            ip_addr_data = &reinterpret_cast<const sockaddr_in6*>(address)->sin6_addr;
+            addr_format_ = "[%s]:%hu";
+        } else {  // Should be impossible
+            D("mDNS resolved non-IP address.");
+            return false;
+        }
+
+        // Winsock version requires the const cast mingw defines inet_ntop differently from msvc.
+        char 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 false;
+        }
+        ip_addr_ = ip_addr;
+
+        return true;
+    }
+
+    static void AddToServiceRegistry(std::unique_ptr<ResolvedService> service) {
+        // Add to the service registry before trying to auto-connect, since socket_spec_connect will
+        // check these registries for the ip address when connecting via mdns instance name.
+        auto service_index = service->service_index();
+        if (!service_index) {
+            return;
+        }
+
+        ServiceRegistry* services = nullptr;
+        switch (*service_index) {
+            case kADBTransportServiceRefIndex:
+                services = sAdbTransportServices;
+                break;
+            case kADBSecurePairingServiceRefIndex:
+                services = sAdbSecurePairingServices;
+                break;
+            case kADBSecureConnectServiceRefIndex:
+                services = sAdbSecureConnectServices;
+                break;
+            default:
+                LOG(WARNING) << "No registry available for reg_type=[" << service->reg_type()
+                             << "]";
+                return;
+        }
+
+        services->push_back(std::move(service));
+        const auto& s = services->back();
+
+        auto reg_type = s->reg_type();
+        auto service_name = s->service_name();
+        // Remove any services with the same instance name, as it may be a stale registration.
+        RemoveDNSService(reg_type, service_name);
+
+        auto ip_addr = s->ip_address();
+        auto port = s->port();
+        if (adb_DNSServiceShouldAutoConnect(reg_type, service_name)) {
+            std::string response;
+            D("Attempting to connect service_name=[%s], regtype=[%s] ip_addr=(%s:%hu)",
+              service_name.c_str(), reg_type.c_str(), ip_addr.c_str(), port);
+
+            if (*service_index == kADBSecureConnectServiceRefIndex) {
+                s->ConnectSecureWifiDevice();
+            } else {
+                connect_device(android::base::StringPrintf("%s.%s", service_name.c_str(),
+                                                           reg_type.c_str()),
+                               &response);
+                D("Connect to %s regtype %s (%s:%hu) : %s", service_name.c_str(), reg_type.c_str(),
+                  ip_addr.c_str(), port, response.c_str());
+            }
+        } else {
+            D("Not immediately connecting to service_name=[%s], regtype=[%s] ip_addr=(%s:%hu)",
+              service_name.c_str(), reg_type.c_str(), ip_addr.c_str(), port);
+        }
+    }
+
+    std::optional<int> service_index() const {
+        return adb_DNSServiceIndexByName(reg_type_.c_str());
+    }
+
+    const std::string& host_target() const { return host_target_; }
+
+    const std::string& service_name() const { return service_name_; }
+
+    const std::string& reg_type() const { return reg_type_; }
+
+    const std::string& ip_address() const { return ip_addr_; }
+
+    uint16_t port() const { return port_; }
+
+    using ServiceRegistry = std::vector<std::unique_ptr<ResolvedService>>;
+
+    // unencrypted tcp connections
+    static ServiceRegistry* sAdbTransportServices;
+
+    static ServiceRegistry* sAdbSecurePairingServices;
+    static ServiceRegistry* sAdbSecureConnectServices;
+
+    static void InitAdbServiceRegistries();
+
+    static void ForEachService(const ServiceRegistry& services, const std::string& hostname,
+                               adb_secure_foreach_service_callback cb);
+
+    static bool ConnectByServiceName(const ServiceRegistry& services,
+                                     const std::string& service_name);
+
+    static void RemoveDNSService(const std::string& reg_type, const std::string& service_name);
+
+  private:
+    int clientVersion_ = ADB_SECURE_CLIENT_VERSION;
+    std::string addr_format_;
+    std::string service_name_;
+    std::string reg_type_;
+    std::string host_target_;
+    const uint16_t port_;
+    int sa_family_;
+    std::string ip_addr_;
+    int service_version_;
+};
+
+// static
+ResolvedService::ServiceRegistry* ResolvedService::sAdbTransportServices = NULL;
+
+// static
+ResolvedService::ServiceRegistry* ResolvedService::sAdbSecurePairingServices = NULL;
+
+// static
+ResolvedService::ServiceRegistry* ResolvedService::sAdbSecureConnectServices = NULL;
+
+// static
+void ResolvedService::InitAdbServiceRegistries() {
+    if (!sAdbTransportServices) {
+        sAdbTransportServices = new ServiceRegistry;
+    }
+    if (!sAdbSecurePairingServices) {
+        sAdbSecurePairingServices = new ServiceRegistry;
+    }
+    if (!sAdbSecureConnectServices) {
+        sAdbSecureConnectServices = new ServiceRegistry;
+    }
+}
+
+// static
+void ResolvedService::ForEachService(const ServiceRegistry& services,
+                                     const std::string& wanted_service_name,
+                                     adb_secure_foreach_service_callback cb) {
+    InitAdbServiceRegistries();
+
+    for (const auto& service : services) {
+        auto service_name = service->service_name();
+        auto reg_type = service->reg_type();
+        auto ip = service->ip_address();
+        auto port = service->port();
+
+        if (wanted_service_name.empty()) {
+            cb(service_name.c_str(), reg_type.c_str(), ip.c_str(), port);
+        } else if (service_name == wanted_service_name) {
+            cb(service_name.c_str(), reg_type.c_str(), ip.c_str(), port);
+        }
+    }
+}
+
+// static
+bool ResolvedService::ConnectByServiceName(const ServiceRegistry& services,
+                                           const std::string& service_name) {
+    InitAdbServiceRegistries();
+    for (const auto& service : services) {
+        auto wanted_name = service->service_name();
+        if (wanted_name == service_name) {
+            D("Got service_name match [%s]", wanted_name.c_str());
+            return service->ConnectSecureWifiDevice();
+        }
+    }
+    D("No registered service_names matched [%s]", service_name.c_str());
+    return false;
+}
+
+// static
+void ResolvedService::RemoveDNSService(const std::string& reg_type,
+                                       const std::string& service_name) {
+    D("%s: reg_type=[%s] service_name=[%s]", __func__, reg_type.c_str(), service_name.c_str());
+    auto index = adb_DNSServiceIndexByName(reg_type);
+    if (!index) {
+        return;
+    }
+    ServiceRegistry* services;
+    switch (*index) {
+        case kADBTransportServiceRefIndex:
+            services = sAdbTransportServices;
+            break;
+        case kADBSecurePairingServiceRefIndex:
+            services = sAdbSecurePairingServices;
+            break;
+        case kADBSecureConnectServiceRefIndex:
+            services = sAdbSecureConnectServices;
+            break;
+        default:
+            return;
+    }
+
+    if (services->empty()) {
+        return;
+    }
+
+    services->erase(std::remove_if(services->begin(), services->end(),
+                                   [&service_name](std::unique_ptr<ResolvedService>& service) {
+                                       return (service_name == service->service_name());
+                                   }),
+                    services->end());
+}
+
+void DNSSD_API register_service_ip(DNSServiceRef sdref, DNSServiceFlags flags,
+                                   uint32_t /*interface_index*/, DNSServiceErrorType error_code,
+                                   const char* hostname, const sockaddr* address, uint32_t ttl,
+                                   void* context) {
+    D("%s: sdref=%p flags=0x%08x error_code=%u ttl=%u", __func__, sdref, flags, error_code, ttl);
+    std::unique_ptr<ResolvedService> data(static_cast<ResolvedService*>(context));
+    // Only resolve the address once. If the address or port changes, we'll just get another
+    // registration.
+    data->DestroyServiceRef();
+
+    if (error_code != kDNSServiceErr_NoError) {
+        D("Got error while looking up ip_addr [%u]", error_code);
+        return;
+    }
+
+    if (flags & kDNSServiceFlagsAdd) {
+        if (data->RegisterIpAddress(address)) {
+            D("Resolved IP address for [%s]. Adding to service registry.", hostname);
+            ResolvedService::AddToServiceRegistry(std::move(data));
+        }
+    }
+}
+
+void DNSSD_API register_resolved_mdns_service(DNSServiceRef sdref, DNSServiceFlags flags,
+                                              uint32_t interface_index,
+                                              DNSServiceErrorType error_code, const char* fullname,
+                                              const char* host_target, uint16_t port,
+                                              uint16_t txt_len, const unsigned char* txt_record,
+                                              void* context);
+
+class DiscoveredService : public AsyncServiceRef {
+  public:
+    DiscoveredService(uint32_t interface_index, const char* service_name, const char* regtype,
+                      const char* domain)
+        : service_name_(service_name), reg_type_(regtype) {
+        DNSServiceErrorType ret =
+                DNSServiceResolve(&sdref_, 0, interface_index, service_name, regtype, domain,
+                                  register_resolved_mdns_service, reinterpret_cast<void*>(this));
+
+        D("DNSServiceResolve for "
+          "interface_index %u "
+          "service_name %s "
+          "regtype %s "
+          "domain %s "
+          ": %d",
+          interface_index, service_name, regtype, domain, ret);
+
+        if (ret == kDNSServiceErr_NoError) {
+            Initialize();
+        }
+    }
+
+    const std::string& service_name() { return service_name_; }
+
+    const std::string& reg_type() { return reg_type_; }
+
+  private:
+    std::string service_name_;
+    std::string reg_type_;
+};
+
+// Returns the version the device wanted to advertise,
+// or -1 if parsing fails.
+int ParseVersionFromTxtRecord(uint16_t txt_len, const unsigned char* txt_record) {
+    if (!txt_len) return -1;
+    if (!txt_record) 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> record_str(txt_len + 1, '\0');
+    char* str = record_str.data();
+
+    memcpy(str, txt_record + 1 /* skip the length byte */, txt_len);
+
+    // Check if it's the version key
+    static const char* version_key = "v=";
+    size_t version_key_len = strlen(version_key);
+
+    if (strncmp(version_key, str, version_key_len)) return -1;
+
+    auto value_start = str + version_key_len;
+
+    long parsed_number = strtol(value_start, 0, 10);
+
+    // No valid conversion. Also, 0
+    // is not a valid version.
+    if (!parsed_number) return -1;
+
+    // Outside bounds of int.
+    if (parsed_number < INT_MIN || parsed_number > INT_MAX) return -1;
+
+    // Possibly valid version
+    return static_cast<int>(parsed_number);
+}
+
+void DNSSD_API register_resolved_mdns_service(DNSServiceRef sdref, DNSServiceFlags flags,
+                                              uint32_t interface_index,
+                                              DNSServiceErrorType error_code, const char* fullname,
+                                              const char* host_target, uint16_t port,
+                                              uint16_t txt_len, const unsigned char* txt_record,
+                                              void* context) {
+    D("Resolved a service.");
+    std::unique_ptr<DiscoveredService> discovered(reinterpret_cast<DiscoveredService*>(context));
+
+    if (error_code != kDNSServiceErr_NoError) {
+        D("Got error %d resolving service.", error_code);
+        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 service_version = ParseVersionFromTxtRecord(txt_len, txt_record);
+
+    auto resolved = new ResolvedService(discovered->service_name(), discovered->reg_type(),
+                                        interface_index, host_target, ntohs(port), service_version);
+
+    if (!resolved->Initialized()) {
+        D("Unable to init resolved service");
+        delete resolved;
+    }
+
+    if (flags) { /* Only ever equals MoreComing or 0 */
+        D("releasing discovered service");
+        discovered.release();
+    }
+}
+
+void DNSSD_API on_service_browsed(DNSServiceRef sdref, DNSServiceFlags flags,
+                                  uint32_t interface_index, DNSServiceErrorType error_code,
+                                  const char* service_name, const char* regtype, const char* domain,
+                                  void* /*context*/) {
+    if (error_code != kDNSServiceErr_NoError) {
+        D("Got error %d during mDNS browse.", error_code);
+        DNSServiceRefDeallocate(sdref);
+        auto service_index = adb_DNSServiceIndexByName(regtype);
+        if (service_index) {
+            fdevent_destroy(g_service_ref_fdes[*service_index]);
+        }
+        return;
+    }
+
+    if (flags & kDNSServiceFlagsAdd) {
+        D("%s: Discover found new service_name=[%s] regtype=[%s] domain=[%s]", __func__,
+          service_name, regtype, domain);
+        auto discovered = new DiscoveredService(interface_index, service_name, regtype, domain);
+        if (!discovered->Initialized()) {
+            delete discovered;
+        }
+    } else {
+        D("%s: Discover lost service_name=[%s] regtype=[%s] domain=[%s]", __func__, service_name,
+          regtype, domain);
+        ResolvedService::RemoveDNSService(regtype, service_name);
+    }
+}
+
+void init_mdns_transport_discovery_thread(void) {
+    int error_codes[kNumADBDNSServices];
+    for (int i = 0; i < kNumADBDNSServices; ++i) {
+        error_codes[i] = DNSServiceBrowse(&g_service_refs[i], 0, 0, kADBDNSServices[i], nullptr,
+                                          on_service_browsed, nullptr);
+
+        if (error_codes[i] != kDNSServiceErr_NoError) {
+            D("Got %d browsing for mDNS service %s.", error_codes[i], kADBDNSServices[i]);
+        } else {
+            fdevent_run_on_main_thread([i]() {
+                g_service_ref_fdes[i] = fdevent_create(adb_DNSServiceRefSockFD(g_service_refs[i]),
+                                                       pump_service_ref, &g_service_refs[i]);
+                fdevent_set(g_service_ref_fdes[i], FDE_READ);
+            });
+        }
+    }
+}
+
+namespace MdnsResponder {
+
+bool adb_secure_connect_by_service_name(const std::string& instance_name) {
+    return ResolvedService::ConnectByServiceName(*ResolvedService::sAdbSecureConnectServices,
+                                                 instance_name);
+}
+
+std::string mdns_check() {
+    uint32_t daemon_version;
+    uint32_t sz = sizeof(daemon_version);
+
+    auto dnserr = DNSServiceGetProperty(kDNSServiceProperty_DaemonVersion, &daemon_version, &sz);
+    if (dnserr != kDNSServiceErr_NoError) {
+        return "ERROR: mdns daemon unavailable";
+    }
+
+    return android::base::StringPrintf("mdns daemon version [%u]", daemon_version);
+}
+
+std::string mdns_list_discovered_services() {
+    std::string result;
+    auto cb = [&](const std::string& service_name, const std::string& reg_type,
+                  const std::string& ip_addr, uint16_t port) {
+        result += android::base::StringPrintf("%s\t%s\t%s:%u\n", service_name.c_str(),
+                                              reg_type.c_str(), ip_addr.c_str(), port);
+    };
+
+    ResolvedService::ForEachService(*ResolvedService::sAdbTransportServices, "", cb);
+    ResolvedService::ForEachService(*ResolvedService::sAdbSecureConnectServices, "", cb);
+    ResolvedService::ForEachService(*ResolvedService::sAdbSecurePairingServices, "", cb);
+    return result;
+}
+
+std::optional<MdnsInfo> mdns_get_connect_service_info(const std::string& name) {
+    CHECK(!name.empty());
+
+    // only adb server creates these registries
+    if (!ResolvedService::sAdbTransportServices && !ResolvedService::sAdbSecureConnectServices) {
+        return std::nullopt;
+    }
+    CHECK(ResolvedService::sAdbTransportServices);
+    CHECK(ResolvedService::sAdbSecureConnectServices);
+
+    auto mdns_instance = mdns::mdns_parse_instance_name(name);
+    if (!mdns_instance.has_value()) {
+        D("Failed to parse mDNS name [%s]", name.c_str());
+        return std::nullopt;
+    }
+
+    std::optional<MdnsInfo> info;
+    auto cb = [&](const std::string& service_name, const std::string& reg_type,
+                  const std::string& ip_addr,
+                  uint16_t port) { info.emplace(service_name, reg_type, ip_addr, port); };
+
+    std::string reg_type;
+    if (!mdns_instance->service_name.empty()) {
+        reg_type = android::base::StringPrintf("%s.%s", mdns_instance->service_name.c_str(),
+                                               mdns_instance->transport_type.c_str());
+        auto index = adb_DNSServiceIndexByName(reg_type);
+        if (!index) {
+            return std::nullopt;
+        }
+        switch (*index) {
+            case kADBTransportServiceRefIndex:
+                ResolvedService::ForEachService(*ResolvedService::sAdbTransportServices,
+                                                mdns_instance->instance_name, cb);
+                break;
+            case kADBSecureConnectServiceRefIndex:
+                ResolvedService::ForEachService(*ResolvedService::sAdbSecureConnectServices,
+                                                mdns_instance->instance_name, cb);
+                break;
+            default:
+                D("Unknown reg_type [%s]", reg_type.c_str());
+                return std::nullopt;
+        }
+        return info;
+    }
+
+    for (const auto& service :
+         {ResolvedService::sAdbTransportServices, ResolvedService::sAdbSecureConnectServices}) {
+        ResolvedService::ForEachService(*service, name, cb);
+        if (info.has_value()) {
+            return info;
+        }
+    }
+
+    return std::nullopt;
+}
+
+std::optional<MdnsInfo> mdns_get_pairing_service_info(const std::string& name) {
+    CHECK(!name.empty());
+
+    auto mdns_instance = mdns::mdns_parse_instance_name(name);
+    if (!mdns_instance.has_value()) {
+        D("Failed to parse mDNS pairing name [%s]", name.c_str());
+        return std::nullopt;
+    }
+
+    std::optional<MdnsInfo> info;
+    auto cb = [&](const std::string& service_name, const std::string& reg_type,
+                  const std::string& ip_addr,
+                  uint16_t port) { info.emplace(service_name, reg_type, ip_addr, port); };
+
+    // Verify it's a pairing service if user explicitly inputs it.
+    if (!mdns_instance->service_name.empty()) {
+        auto reg_type = android::base::StringPrintf("%s.%s", mdns_instance->service_name.c_str(),
+                                                    mdns_instance->transport_type.c_str());
+        auto index = adb_DNSServiceIndexByName(reg_type);
+        if (!index) {
+            return std::nullopt;
+        }
+        switch (*index) {
+            case kADBSecurePairingServiceRefIndex:
+                break;
+            default:
+                D("Not an adb pairing reg_type [%s]", reg_type.c_str());
+                return std::nullopt;
+        }
+    }
+
+    ResolvedService::ForEachService(*ResolvedService::sAdbSecurePairingServices, name, cb);
+    return info;
+}
+
+void mdns_cleanup() {}
+
+}  // namespace MdnsResponder
+}  // namespace
+
+AdbMdnsResponderFuncs StartMdnsResponderDiscovery() {
+    ResolvedService::InitAdbServiceRegistries();
+    std::thread(init_mdns_transport_discovery_thread).detach();
+    AdbMdnsResponderFuncs f = {
+            .mdns_check = MdnsResponder::mdns_check,
+            .mdns_list_discovered_services = MdnsResponder::mdns_list_discovered_services,
+            .mdns_get_connect_service_info = MdnsResponder::mdns_get_connect_service_info,
+            .mdns_get_pairing_service_info = MdnsResponder::mdns_get_pairing_service_info,
+            .mdns_cleanup = MdnsResponder::mdns_cleanup,
+            .adb_secure_connect_by_service_name = MdnsResponder::adb_secure_connect_by_service_name,
+    };
+    return f;
+}
diff --git a/client/openscreen/mdns_service_info.cpp b/client/openscreen/mdns_service_info.cpp
new file mode 100644
index 0000000..73d0651
--- /dev/null
+++ b/client/openscreen/mdns_service_info.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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 "client/openscreen/mdns_service_info.h"
+
+#include "adb_mdns.h"
+
+using namespace openscreen;
+
+namespace mdns {
+
+ErrorOr<ServiceInfo> DnsSdInstanceEndpointToServiceInfo(
+        const discovery::DnsSdInstanceEndpoint& endpoint) {
+    ServiceInfo service_info;
+    // Check if |endpoint| is a known adb service name
+    for (int i = 0; i < kNumADBDNSServices; ++i) {
+        if (endpoint.service_id() == kADBDNSServices[i]) {
+            service_info.service_name = endpoint.service_id();
+            service_info.instance_name = endpoint.instance_id();
+            break;
+        }
+        if (i == kNumADBDNSServices - 1) {
+            LOG(ERROR) << "Got unknown service name [" << endpoint.service_id() << "]";
+            return Error::Code::kParameterInvalid;
+        }
+    }
+
+    service_info.port = endpoint.port();
+    for (const IPAddress& address : endpoint.addresses()) {
+        if (!service_info.v4_address && address.IsV4()) {
+            service_info.v4_address = address;
+        } else if (!service_info.v6_address && address.IsV6()) {
+            service_info.v6_address = address;
+        }
+    }
+    CHECK(service_info.v4_address || service_info.v6_address);
+    return service_info;
+}
+
+}  // namespace mdns
diff --git a/client/openscreen/mdns_service_info.h b/client/openscreen/mdns_service_info.h
new file mode 100644
index 0000000..501b20c
--- /dev/null
+++ b/client/openscreen/mdns_service_info.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <discovery/dnssd/public/dns_sd_instance_endpoint.h>
+#include <platform/base/ip_address.h>
+
+#include "client/mdns_utils.h"
+
+namespace mdns {
+
+struct ServiceInfo {
+    std::string instance_name;
+    std::string service_name;
+    openscreen::IPAddress v4_address;
+    openscreen::IPAddress v6_address;
+    uint16_t port;
+};  // ServiceInfo
+
+openscreen::ErrorOr<ServiceInfo> DnsSdInstanceEndpointToServiceInfo(
+        const openscreen::discovery::DnsSdInstanceEndpoint& endpoint);
+
+}  // namespace mdns
diff --git a/client/openscreen/mdns_service_watcher.cpp b/client/openscreen/mdns_service_watcher.cpp
new file mode 100644
index 0000000..2791ff7
--- /dev/null
+++ b/client/openscreen/mdns_service_watcher.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "client/openscreen/mdns_service_watcher.h"
+
+#include "client/openscreen/mdns_service_info.h"
+
+using namespace openscreen;
+
+namespace mdns {
+
+ServiceReceiver::ServiceReceiver(
+        discovery::DnsSdService* service, std::string_view service_name,
+        openscreen::discovery::DnsSdServiceWatcher<ServiceInfo>::ServicesUpdatedCallback cb)
+    : discovery::DnsSdServiceWatcher<ServiceInfo>(
+              service, service_name.data(), DnsSdInstanceEndpointToServiceInfo, std::move(cb)) {
+    LOG(VERBOSE) << "Initializing ServiceReceiver service=" << service_name;
+}
+}  // namespace mdns
diff --git a/client/openscreen/mdns_service_watcher.h b/client/openscreen/mdns_service_watcher.h
new file mode 100644
index 0000000..efea260
--- /dev/null
+++ b/client/openscreen/mdns_service_watcher.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string_view>
+
+#include "client/openscreen/mdns_service_info.h"
+
+#include <discovery/public/dns_sd_service_watcher.h>
+
+namespace mdns {
+
+class ServiceReceiver : public ::openscreen::discovery::DnsSdServiceWatcher<ServiceInfo> {
+  public:
+    explicit ServiceReceiver(
+            openscreen::discovery::DnsSdService* service, std::string_view service_name,
+            openscreen::discovery::DnsSdServiceWatcher<ServiceInfo>::ServicesUpdatedCallback cb);
+
+    const std::string& service_name() const { return service_name_; }
+
+  private:
+    std::string service_name_;
+};  // ServiceReceiver
+
+}  // namespace mdns
diff --git a/client/openscreen/platform/logging.cpp b/client/openscreen/platform/logging.cpp
new file mode 100644
index 0000000..9611e49
--- /dev/null
+++ b/client/openscreen/platform/logging.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <platform/api/logging.h>
+
+#include <android-base/logging.h>
+
+namespace openscreen {
+
+bool IsLoggingOn(LogLevel level, const char* file) {
+    return true;
+}
+
+void LogWithLevel(LogLevel level, const char* file, int line, std::stringstream message) {
+    switch (level) {
+        case LogLevel::kInfo:
+            LOG(INFO) << message.str();
+            break;
+        case LogLevel::kWarning:
+            LOG(WARNING) << message.str();
+            break;
+        case LogLevel::kError:
+            LOG(ERROR) << message.str();
+            break;
+        case LogLevel::kFatal:
+            LOG(FATAL) << message.str();
+            break;
+        default:
+            LOG(VERBOSE) << message.str();
+            break;
+    }
+}
+
+[[noreturn]] void Break() {
+    std::abort();
+}
+
+}  // namespace openscreen
diff --git a/client/openscreen/platform/task_runner.cpp b/client/openscreen/platform/task_runner.cpp
new file mode 100644
index 0000000..167a1c9
--- /dev/null
+++ b/client/openscreen/platform/task_runner.cpp
@@ -0,0 +1,120 @@
+/*
+ * 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 "client/openscreen/platform/task_runner.h"
+
+#include <chrono>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/threads.h>
+#include <platform/api/time.h>
+
+#include "fdevent/fdevent.h"
+
+using android::base::ScopedLockAssertion;
+using namespace openscreen;
+
+namespace mdns {
+
+AdbOspTaskRunner::AdbOspTaskRunner() {
+    check_main_thread();
+    thread_id_ = android::base::GetThreadId();
+    task_handler_ = std::thread([this]() { TaskExecutorWorker(); });
+}
+
+AdbOspTaskRunner::~AdbOspTaskRunner() {
+    if (task_handler_.joinable()) {
+        terminate_loop_ = true;
+        cv_.notify_one();
+        task_handler_.join();
+    }
+}
+
+void AdbOspTaskRunner::PostPackagedTask(Task task) {
+    PostPackagedTaskWithDelay(std::move(task), openscreen::Clock::duration::zero());
+}
+
+void AdbOspTaskRunner::PostPackagedTaskWithDelay(Task task, Clock::duration delay) {
+    auto now = std::chrono::steady_clock::now();
+    {
+        std::lock_guard<std::mutex> lock(mutex_);
+        tasks_.emplace(now + delay, std::move(task));
+    }
+    cv_.notify_one();
+}
+
+bool AdbOspTaskRunner::IsRunningOnTaskRunner() {
+    return (thread_id_ == android::base::GetThreadId());
+}
+
+void AdbOspTaskRunner::TaskExecutorWorker() {
+    for (;;) {
+        {
+            // Wait until there's a task available.
+            std::unique_lock<std::mutex> lock(mutex_);
+            ScopedLockAssertion assume_locked(mutex_);
+            while (!terminate_loop_ && tasks_.empty()) {
+                cv_.wait(lock);
+            }
+            if (terminate_loop_) {
+                return;
+            }
+
+            // Wait until the task with the closest time point is ready to run.
+            auto timepoint = tasks_.begin()->first;
+            while (timepoint > std::chrono::steady_clock::now()) {
+                cv_.wait_until(lock, timepoint);
+                // It's possible that another task with an earlier time was added
+                // while waiting for |timepoint|.
+                timepoint = tasks_.begin()->first;
+
+                if (terminate_loop_) {
+                    return;
+                }
+            }
+        }
+
+        // Execute all tasks whose time points have passed.
+        std::vector<Task> running_tasks;
+        {
+            std::lock_guard<std::mutex> lock(mutex_);
+
+            while (!tasks_.empty()) {
+                auto task_with_delay = tasks_.begin();
+                if (task_with_delay->first > std::chrono::steady_clock::now()) {
+                    break;
+                } else {
+                    running_tasks.emplace_back(std::move(task_with_delay->second));
+                    tasks_.erase(task_with_delay);
+                }
+            }
+        }
+
+        CHECK(!running_tasks.empty());
+        std::packaged_task<int()> waitable_task([&] {
+            for (Task& task : running_tasks) {
+                task();
+            }
+            return 0;
+        });
+
+        fdevent_run_on_main_thread([&]() { waitable_task(); });
+
+        waitable_task.get_future().wait();
+    }
+}
+}  // namespace mdns
diff --git a/client/openscreen/platform/task_runner.h b/client/openscreen/platform/task_runner.h
new file mode 100644
index 0000000..814d250
--- /dev/null
+++ b/client/openscreen/platform/task_runner.h
@@ -0,0 +1,69 @@
+/*
+ * 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 <platform/api/task_runner.h>
+
+#include <atomic>
+#include <condition_variable>
+#include <map>
+#include <mutex>
+#include <optional>
+#include <thread>
+
+#include <android-base/thread_annotations.h>
+
+namespace mdns {
+
+// Openscreen API surface that allows for posting tasks. The underlying
+// implementation may be single or multi-threaded, and all complication should
+// be handled by the implementation class. The implementation must guarantee:
+// (1) Tasks shall not overlap in time/CPU.
+// (2) Tasks shall run sequentially, e.g. posting task A then B implies
+//     that A shall run before B.
+// (3) If task A is posted before task B, then any mutation in A happens-before
+//     B runs (even if A and B run on different threads).
+//
+// Adb implementation: The PostPackagedTask* APIs are thread-safe.
+// Another thread will handle dequeuing each item and calling it on the fdevent thread.
+// Thus, the task runner thread is the fdevent thread, and IsRunningOnTaskRunner() shall always
+// return true if calling from within the running Task.
+class AdbOspTaskRunner final : public openscreen::TaskRunner {
+  public:
+    using Task = openscreen::TaskRunner::Task;
+
+    // Must be called on the fdevent thread.
+    explicit AdbOspTaskRunner();
+    ~AdbOspTaskRunner() final;
+    void PostPackagedTask(Task task) final;
+    void PostPackagedTaskWithDelay(Task task, openscreen::Clock::duration delay) final;
+    bool IsRunningOnTaskRunner() final;
+
+    // The task executor thread.
+    void TaskExecutorWorker();
+
+  private:
+    uint64_t thread_id_;
+    std::mutex mutex_;
+    std::multimap<std::chrono::time_point<std::chrono::steady_clock>, Task> tasks_
+            GUARDED_BY(mutex_);
+    std::atomic<bool> terminate_loop_ = false;
+    std::condition_variable cv_;
+    std::thread task_handler_;
+};
+
+}  // namespace mdns
diff --git a/client/openscreen/platform/udp_socket.cpp b/client/openscreen/platform/udp_socket.cpp
new file mode 100644
index 0000000..3252078
--- /dev/null
+++ b/client/openscreen/platform/udp_socket.cpp
@@ -0,0 +1,664 @@
+/*
+ * 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 <platform/api/udp_socket.h>
+
+#include <sstream>
+#include <string>
+
+#include <android-base/logging.h>
+#include <discovery/mdns/public/mdns_constants.h>
+
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+#include "fdevent/fdevent.h"
+#include "sysdeps.h"
+
+//#define DEBUG_UDP
+
+constexpr bool IsPowerOf2(uint32_t x) {
+    return (x > 0) && ((x & (x - 1)) == 0);
+}
+
+static_assert(IsPowerOf2(alignof(adb_cmsghdr)), "std::align requires power-of-2 alignment");
+
+// For windows, winuser.h defines a SendMessage macro that causes compilation issues.
+#ifdef _WIN32
+#ifdef SendMessage
+#undef SendMessage
+#endif
+#endif
+namespace openscreen {
+
+namespace {
+
+#ifdef _WIN32
+using IPv4NetworkInterfaceIndex = decltype(ip_mreq().imr_interface.s_addr);
+#else
+using IPv4NetworkInterfaceIndex = decltype(ip_mreqn().imr_ifindex);
+#endif
+using IPv6NetworkInterfaceIndex = decltype(ipv6_mreq().ipv6mr_interface);
+
+// Examine |posix_errno| to determine whether the specific cause of a failure
+// was transient or hard, and return the appropriate error response.
+Error ChooseError(decltype(errno) posix_errno, Error::Code hard_error_code) {
+    if (posix_errno == EAGAIN || posix_errno == EWOULDBLOCK || posix_errno == ENOBUFS) {
+        return Error(Error::Code::kAgain, strerror(errno));
+    }
+    return Error(hard_error_code, strerror(errno));
+}
+
+IPAddress GetIPAddressFromSockAddr(const sockaddr_in& sa) {
+    static_assert(IPAddress::kV4Size == sizeof(sa.sin_addr.s_addr), "IPv4 address size mismatch.");
+    return IPAddress(IPAddress::Version::kV4,
+                     reinterpret_cast<const uint8_t*>(&sa.sin_addr.s_addr));
+}
+
+IPAddress GetIPAddressFromPktInfo(const in_pktinfo& pktinfo) {
+    static_assert(IPAddress::kV4Size == sizeof(pktinfo.ipi_addr), "IPv4 address size mismatch.");
+    return IPAddress(IPAddress::Version::kV4, reinterpret_cast<const uint8_t*>(&pktinfo.ipi_addr));
+}
+
+uint16_t GetPortFromFromSockAddr(const sockaddr_in& sa) {
+    return ntohs(sa.sin_port);
+}
+
+IPAddress GetIPAddressFromSockAddr(const sockaddr_in6& sa) {
+    return IPAddress(IPAddress::Version::kV6, sa.sin6_addr.s6_addr);
+}
+
+IPAddress GetIPAddressFromPktInfo(const in6_pktinfo& pktinfo) {
+    return IPAddress(IPAddress::Version::kV6, pktinfo.ipi6_addr.s6_addr);
+}
+
+uint16_t GetPortFromFromSockAddr(const sockaddr_in6& sa) {
+    return ntohs(sa.sin6_port);
+}
+
+template <class PktInfoType>
+bool IsPacketInfo(adb_cmsghdr* cmh);
+
+template <>
+bool IsPacketInfo<in_pktinfo>(adb_cmsghdr* cmh) {
+    return cmh->cmsg_level == IPPROTO_IP && cmh->cmsg_type == IP_PKTINFO;
+}
+
+template <>
+bool IsPacketInfo<in6_pktinfo>(adb_cmsghdr* cmh) {
+    return cmh->cmsg_level == IPPROTO_IPV6 && cmh->cmsg_type == IPV6_PKTINFO;
+}
+
+template <class SockAddrType, class PktInfoType>
+Error ReceiveMessageInternal(borrowed_fd fd, UdpPacket* packet) {
+    SockAddrType sa;
+    adb_iovec iov;
+    iov.iov_len = packet->size();
+    iov.iov_base = packet->data();
+    alignas(alignof(adb_cmsghdr)) uint8_t control_buffer[1024];
+    adb_msghdr msg;
+    msg.msg_name = &sa;
+    msg.msg_namelen = sizeof(sa);
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 1;
+    msg.msg_control = control_buffer;
+    msg.msg_controllen = sizeof(control_buffer);
+    msg.msg_flags = 0;
+
+    ssize_t bytes_received = adb_recvmsg(fd, &msg, 0);
+    if (bytes_received == -1) {
+        return ChooseError(errno, Error::Code::kSocketReadFailure);
+    }
+
+    CHECK_EQ(static_cast<size_t>(bytes_received), packet->size());
+
+    IPEndpoint source_endpoint = {.address = GetIPAddressFromSockAddr(sa),
+                                  .port = GetPortFromFromSockAddr(sa)};
+    packet->set_source(std::move(source_endpoint));
+
+    // For multicast sockets, the packet's original destination address may be
+    // the host address (since we called bind()) but it may also be a
+    // multicast address.  This may be relevant for handling multicast data;
+    // specifically, mDNSResponder requires this information to work properly.
+
+    socklen_t sa_len = sizeof(sa);
+    if (((msg.msg_flags & MSG_CTRUNC) != 0) ||
+        (adb_getsockname(fd, reinterpret_cast<sockaddr*>(&sa), &sa_len) == -1)) {
+        return Error::Code::kNone;
+    }
+    for (adb_cmsghdr* cmh = adb_CMSG_FIRSTHDR(&msg); cmh; cmh = adb_CMSG_NXTHDR(&msg, cmh)) {
+        if (IsPacketInfo<PktInfoType>(cmh)) {
+            PktInfoType* pktinfo = reinterpret_cast<PktInfoType*>(adb_CMSG_DATA(cmh));
+            IPEndpoint destination_endpoint = {.address = GetIPAddressFromPktInfo(*pktinfo),
+                                               .port = GetPortFromFromSockAddr(sa)};
+            packet->set_destination(destination_endpoint);
+            break;
+        }
+    }
+    return Error::Code::kNone;
+}
+// An open UDP socket for sending/receiving datagrams to/from either specific
+// endpoints or over IP multicast.
+//
+// Usage: The socket is created and opened by calling the Create() method. This
+// returns a unique pointer that auto-closes/destroys the socket when it goes
+// out-of-scope.
+class AdbUdpSocket : public UdpSocket {
+  public:
+    explicit AdbUdpSocket(UdpSocket::Client* client, const IPEndpoint& local_endpoint, unique_fd fd)
+        : client_(client), local_endpoint_(local_endpoint), fd_(std::move(fd)) {
+        CHECK(client_);
+        CHECK(local_endpoint_.address.IsV4() || local_endpoint_.address.IsV6());
+        fde_ = fdevent_create(fd_.get(), OnFdeventResult, this);
+        if (!fde_) {
+            LOG(FATAL) << "Unable to create fdevent";
+            return;
+        }
+        fdevent_set(fde_, FDE_READ);
+        LOG(INFO) << __func__ << " fd=" << fd_.get();
+    }
+
+    ~AdbUdpSocket() override {
+        if (fde_) {
+            fdevent_destroy(fde_);
+        }
+    }
+
+    // Returns true if |socket| belongs to the IPv4/IPv6 address family.
+    bool IsIPv4() const override { return local_endpoint_.address.IsV4(); }
+    bool IsIPv6() const override { return local_endpoint_.address.IsV6(); }
+
+    // Returns the current local endpoint's address and port. Initially, this will
+    // be the same as the value that was passed into Create(). However, it can
+    // later change after certain operations, such as Bind(), are executed.
+    IPEndpoint GetLocalEndpoint() const override {
+        if (local_endpoint_.port == 0) {
+            // Note: If the getsockname() call fails, just assume that's because the
+            // socket isn't bound yet. In this case, leave the original value in-place.
+            switch (local_endpoint_.address.version()) {
+                case UdpSocket::Version::kV4: {
+                    struct sockaddr_in address;
+                    socklen_t address_len = sizeof(address);
+                    if (adb_getsockname(fd_, reinterpret_cast<struct sockaddr*>(&address),
+                                        &address_len) == 0) {
+                        CHECK_EQ(address.sin_family, AF_INET);
+                        local_endpoint_.address =
+                                IPAddress(IPAddress::Version::kV4,
+                                          reinterpret_cast<uint8_t*>(&address.sin_addr.s_addr));
+                        local_endpoint_.port = ntohs(address.sin_port);
+                    }
+                    break;
+                }
+
+                case UdpSocket::Version::kV6: {
+                    struct sockaddr_in6 address;
+                    socklen_t address_len = sizeof(address);
+                    if (adb_getsockname(fd_, reinterpret_cast<struct sockaddr*>(&address),
+                                        &address_len) == 0) {
+                        CHECK_EQ(address.sin6_family, AF_INET6);
+                        local_endpoint_.address =
+                                IPAddress(IPAddress::Version::kV6,
+                                          reinterpret_cast<uint8_t*>(&address.sin6_addr));
+                        local_endpoint_.port = ntohs(address.sin6_port);
+                    }
+                    break;
+                }
+            }
+        }
+
+        return local_endpoint_;
+    }
+
+    // Binds to the address specified in the constructor. If the local endpoint's
+    // address is zero, the operating system will bind to all interfaces. If the
+    // local endpoint's port is zero, the operating system will automatically find
+    // a free local port and bind to it. Future calls to GetLocalEndpoint() will
+    // reflect the resolved port.
+    //
+    // TODO: openscreen does some chromium compat thing where it calls Bind() before
+    // SetMulticastOutboundInterface(), because chromium alreadys sets IP_MULTICAST_IF internally
+    // before calling Bind(). So we currently wait for the SetMulticastOutboundInterface() call from
+    // the osp-discovery code before actually binding. Note that this means AdbUdpSocket is not for
+    // the general use-case of udp sockets.
+    void Bind() override {
+        if (mdns_ifindex_) {
+            // TODO: move MdnsBind() code back into here once osp-discovery calls Bind() after
+            // SetMulticastOutboundInterface().
+            LOG(FATAL) << "osp-discovery called Bind() after SetMulticastOutboundInterface()!";
+        } else {
+            // mdns impl will only call SetMulticastOutboundInterface and JoinMulticastGroup after
+            // bind is successful.
+            client_->OnBound(this);
+        }
+    }
+
+    static void SetIPV4MulticastProperties(std::optional<IPAddress> local_ipv4,
+                                           std::optional<IPAddress> multiaddr_ipv4,
+                                           ip_mreq* result) {
+        CHECK(result);
+        static_assert(sizeof(result->imr_multiaddr) == 4u, "IPv4 address requires exactly 4 bytes");
+        static_assert(sizeof(result->imr_interface) == 4u, "IPv4 address requires exactly 4 bytes");
+
+        *result = {};
+        if (local_ipv4) {
+            local_ipv4->CopyToV4(reinterpret_cast<uint8_t*>(&result->imr_interface.s_addr));
+        }
+
+        if (multiaddr_ipv4) {
+            multiaddr_ipv4->CopyToV4(reinterpret_cast<uint8_t*>(&result->imr_multiaddr));
+        }
+    }
+
+    // Sets the device to use for outgoing multicast packets on the socket.
+    void SetMulticastOutboundInterface(NetworkInterfaceIndex ifindex) override {
+        if (!fd_.ok()) {
+            OnError(Error::Code::kSocketClosedFailure);
+            return;
+        }
+
+        // TODO: remove once osp-discovery calls Bind() after SetMulticastOutboundInterface().
+        *mdns_ifindex_ = ifindex;
+
+        LOG(INFO) << "SetMulticastOutboundInterface for index=" << ifindex;
+        switch (local_endpoint_.address.version()) {
+            case UdpSocket::Version::kV4: {
+                struct ip_mreq multicast_properties = {};
+                SetIPV4MulticastProperties(local_endpoint_.address, std::nullopt,
+                                           &multicast_properties);
+#ifdef DEBUG_UDP
+                struct in_addr default_addr;
+                unsigned int buf_size = sizeof(default_addr);
+                if (getsockopt(fd_.get(), IPPROTO_IP, IP_MULTICAST_IF, &default_addr, &buf_size) !=
+                    -1) {
+                    const auto default_ip =
+                            IPAddress(IPAddress::Version::kV4,
+                                      reinterpret_cast<const uint8_t*>(&default_addr.s_addr));
+                    LOG(INFO) << "BEFORE IP_MULTICAST_IF: default multicast addr=" << default_ip;
+                }
+#endif  // DEBUG_UDP
+                if (adb_setsockopt(fd_, IPPROTO_IP, IP_MULTICAST_IF, &multicast_properties,
+                                   sizeof(multicast_properties)) == -1) {
+                    OnError(Error::Code::kSocketOptionSettingFailure);
+                    PLOG(ERROR) << "adb_setsockopt() failed";
+                    return;
+                }
+#ifdef DEBUG_UDP
+#ifndef _WIN32
+                buf_size = sizeof(default_addr);
+                if (getsockopt(fd_.get(), IPPROTO_IP, IP_MULTICAST_IF, &default_addr, &buf_size) !=
+                    -1) {
+                    const auto default_ip =
+                            IPAddress(IPAddress::Version::kV4,
+                                      reinterpret_cast<const uint8_t*>(&default_addr.s_addr));
+                    LOG(INFO) << "AFTER IP_MULTICAST_IF: default multicast addr=" << default_ip;
+                }
+#endif  // !_WIN32
+#endif  // DEBUG_UDP
+                break;
+            }
+            case UdpSocket::Version::kV6: {
+                const auto index = static_cast<IPv6NetworkInterfaceIndex>(ifindex);
+                if (adb_setsockopt(fd_, IPPROTO_IPV6, IPV6_MULTICAST_IF, &index, sizeof(index)) ==
+                    -1) {
+                    OnError(Error::Code::kSocketOptionSettingFailure);
+                    PLOG(ERROR) << "adb_setsockopt() failed";
+                    return;
+                }
+                break;
+            }
+        }
+
+        // TODO: remove once osp-discovery calls Bind() after SetMulticastOutboundInterface().
+        MdnsBind(ifindex);
+    }
+
+    // Joins to the multicast group at the given address, using the specified
+    // interface.
+    void JoinMulticastGroup(const IPAddress& address, NetworkInterfaceIndex ifindex) override {
+        if (!fd_.ok()) {
+            OnError(Error::Code::kSocketClosedFailure);
+            return;
+        }
+
+        switch (local_endpoint_.address.version()) {
+            case UdpSocket::Version::kV4: {
+                // Passed as data to setsockopt().  1 means return IP_PKTINFO control data
+                // in recvmsg() calls.
+                const int enable_pktinfo = 1;
+                if (adb_setsockopt(fd_, IPPROTO_IP, IP_PKTINFO, &enable_pktinfo,
+                                   sizeof(enable_pktinfo)) == -1) {
+                    OnError(Error::Code::kSocketOptionSettingFailure);
+                    LOG(ERROR) << "adb_setsockopt failed";
+                    return;
+                }
+                struct ip_mreq multicast_properties;
+                SetIPV4MulticastProperties(local_endpoint_.address, address, &multicast_properties);
+                if (adb_setsockopt(fd_, IPPROTO_IP, IP_ADD_MEMBERSHIP, &multicast_properties,
+                                   sizeof(multicast_properties)) == -1) {
+                    OnError(Error::Code::kSocketOptionSettingFailure);
+                    LOG(ERROR) << "adb_setsockopt failed";
+                    return;
+                }
+                return;
+            }
+
+            case UdpSocket::Version::kV6: {
+                // Passed as data to setsockopt().  1 means return IPV6_PKTINFO control
+                // data in recvmsg() calls.
+                const int enable_pktinfo = 1;
+#ifdef _WIN32
+                if (adb_setsockopt(fd_, IPPROTO_IPV6, IPV6_PKTINFO, &enable_pktinfo,
+                                   sizeof(enable_pktinfo)) == -1) {
+#else
+                if (adb_setsockopt(fd_, IPPROTO_IPV6, IPV6_RECVPKTINFO, &enable_pktinfo,
+                                   sizeof(enable_pktinfo)) == -1) {
+#endif
+                    OnError(Error::Code::kSocketOptionSettingFailure);
+                    LOG(ERROR) << "adb_setsockopt failed";
+                    return;
+                }
+                struct ipv6_mreq multicast_properties = {
+                        .ipv6mr_multiaddr = {},
+                        .ipv6mr_interface = static_cast<IPv6NetworkInterfaceIndex>(ifindex),
+                };
+                static_assert(sizeof(multicast_properties.ipv6mr_multiaddr) == 16u,
+                              "IPv6 address requires exactly 16 bytes");
+                address.CopyToV6(
+                        reinterpret_cast<uint8_t*>(&multicast_properties.ipv6mr_multiaddr));
+                // Portability note: All platforms support IPV6_JOIN_GROUP, which is
+                // synonymous with IPV6_ADD_MEMBERSHIP.
+                if (adb_setsockopt(fd_, IPPROTO_IPV6, IPV6_JOIN_GROUP, &multicast_properties,
+                                   sizeof(multicast_properties)) == -1) {
+                    OnError(Error::Code::kSocketOptionSettingFailure);
+                    LOG(ERROR) << "adb_setsockopt failed";
+                    return;
+                }
+                return;
+            }
+        }
+    }
+
+    // Sends a message. If the message is not sent, Client::OnSendError() will be
+    // called to indicate this. Error::Code::kAgain indicates the operation would
+    // block, which can be expected during normal operation.
+    virtual void SendMessage(const void* data, size_t length, const IPEndpoint& dest) override {
+        if (!fd_.ok()) {
+            client_->OnSendError(this, Error::Code::kSocketClosedFailure);
+            return;
+        }
+
+        LOG(INFO) << "SendMessage ip=" << dest.ToString();
+        adb_iovec iov;
+        iov.iov_len = length;
+        iov.iov_base = const_cast<void*>(data);
+
+        adb_msghdr msg;
+        msg.msg_iov = &iov;
+        msg.msg_iovlen = 1;
+        msg.msg_control = nullptr;
+        msg.msg_controllen = 0;
+        msg.msg_flags = 0;
+
+        ssize_t num_bytes_sent = -2;
+        switch (local_endpoint_.address.version()) {
+            case UdpSocket::Version::kV4: {
+                struct sockaddr_in sa = {};
+                sa.sin_family = AF_INET;
+                sa.sin_port = htons(dest.port);
+                dest.address.CopyToV4(reinterpret_cast<uint8_t*>(&sa.sin_addr.s_addr));
+                msg.msg_name = &sa;
+                msg.msg_namelen = sizeof(sa);
+                num_bytes_sent = adb_sendmsg(fd_, &msg, 0);
+                break;
+            }
+
+            case UdpSocket::Version::kV6: {
+                struct sockaddr_in6 sa = {};
+                sa.sin6_family = AF_INET6;
+                sa.sin6_flowinfo = 0;
+                sa.sin6_scope_id = 0;
+                sa.sin6_port = htons(dest.port);
+                dest.address.CopyToV6(reinterpret_cast<uint8_t*>(&sa.sin6_addr.s6_addr));
+                msg.msg_name = &sa;
+                msg.msg_namelen = sizeof(sa);
+                num_bytes_sent = adb_sendmsg(fd_, &msg, 0);
+                break;
+            }
+        }
+
+        if (num_bytes_sent == -1) {
+            client_->OnSendError(this, ChooseError(errno, Error::Code::kSocketSendFailure));
+            return;
+        }
+
+        // Validity-check: UDP datagram sendmsg() is all or nothing.
+        CHECK_EQ(static_cast<size_t>(num_bytes_sent), length);
+    }
+
+    // Sets the DSCP value to use for all messages sent from this socket.
+    void SetDscp(DscpMode state) override {
+#ifdef _WIN32
+        // TODO: this method doesn't seem to be used anywhere in the openscreen code, so
+        // ignoring implementation for now.
+        // Windows 10 seems to ignore setsockopt IP_TOS, so need to use Win APIs instead.
+        LOG(FATAL) << "NOT IMPLEMENTED";
+#else   // !_WIN32
+        if (!fd_.ok()) {
+            OnError(Error::Code::kSocketClosedFailure);
+            return;
+        }
+
+        constexpr auto kSettingLevel = IPPROTO_IP;
+        uint8_t code_array[1] = {static_cast<uint8_t>(state)};
+        auto code = adb_setsockopt(fd_, kSettingLevel, IP_TOS, code_array, sizeof(uint8_t));
+
+        if (code == EBADF || code == ENOTSOCK || code == EFAULT) {
+            OnError(Error::Code::kSocketOptionSettingFailure);
+            LOG(WARNING) << "BAD SOCKET PROVIDED. CODE: " << code;
+            return;
+        } else if (code == EINVAL) {
+            OnError(Error::Code::kSocketOptionSettingFailure);
+            LOG(WARNING) << "INVALID DSCP INFO PROVIDED";
+            return;
+        } else if (code == ENOPROTOOPT) {
+            OnError(Error::Code::kSocketOptionSettingFailure);
+            LOG(WARNING) << "INVALID DSCP SETTING LEVEL PROVIDED: " << kSettingLevel;
+            return;
+        }
+#endif  // _WIN32
+    }
+
+  private:
+    // TODO: Move back into public Bind() call once osp-discovery code calls Bind() after
+    // SetMulticastOutboundInterface().
+    void MdnsBind(NetworkInterfaceIndex ifindex) {
+        if (!fd_.ok()) {
+            OnError(Error::Code::kSocketClosedFailure);
+            LOG(ERROR) << "Bind() failed. Socket is closed.";
+            return;
+        }
+
+        // This is effectively a boolean passed to setsockopt() to allow a future
+        // bind() on the same socket to succeed, even if the address is already in
+        // use. This is pretty much universally the desired behavior.
+        const int reuse_addr = 1;
+        if (adb_setsockopt(fd_, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr)) == -1) {
+            OnError(Error::Code::kSocketOptionSettingFailure);
+            LOG(WARNING) << "Failed to set SO_REUSEADDR";
+            return;
+        }
+
+        switch (local_endpoint_.address.version()) {
+            case UdpSocket::Version::kV4: {
+                struct sockaddr_in address = {};
+                address.sin_family = AF_INET;
+                address.sin_port = htons(local_endpoint_.port);
+                // MUST bind to ADDR_ANY to send and receive multicast messages.
+                address.sin_addr.s_addr = INADDR_ANY;
+                if (adb_bind(fd_, reinterpret_cast<struct sockaddr*>(&address), sizeof(address)) ==
+                    -1) {
+                    OnError(Error::Code::kSocketBindFailure);
+                    PLOG(ERROR) << "adb_bind failed";
+                    return;
+                }
+
+                // Get the resolved address/port
+                sockaddr_in sa;
+                socklen_t sa_len = sizeof(sa);
+                if (adb_getsockname(fd_, reinterpret_cast<sockaddr*>(&sa), &sa_len) != -1) {
+                    local_endpoint_.address = GetIPAddressFromSockAddr(sa);
+                    local_endpoint_.port = GetPortFromFromSockAddr(sa);
+                    LOG(INFO) << "bind endpoint=" << local_endpoint_;
+                }
+                return;
+            }
+            case UdpSocket::Version::kV6: {
+                struct sockaddr_in6 address = {};
+                address.sin6_family = AF_INET6;
+                address.sin6_flowinfo = 0;
+                address.sin6_port = htons(local_endpoint_.port);
+                // MUST bind to ADDR_ANY and scope_id unset to send and receive multicast messages.
+                address.sin6_addr = in6addr_any;
+                address.sin6_scope_id = 0;
+                if (adb_bind(fd_, reinterpret_cast<struct sockaddr*>(&address), sizeof(address)) ==
+                    -1) {
+                    OnError(Error::Code::kSocketBindFailure);
+                    PLOG(ERROR) << "adb_bind failed";
+                    return;
+                }
+
+                // Get the resolved address/port
+                sockaddr_in6 sa;
+                socklen_t sa_len = sizeof(sa);
+                if (adb_getsockname(fd_, reinterpret_cast<sockaddr*>(&sa), &sa_len) != -1) {
+                    local_endpoint_.address = GetIPAddressFromSockAddr(sa);
+                    local_endpoint_.port = GetPortFromFromSockAddr(sa);
+                    LOG(INFO) << "bind endpoint=" << local_endpoint_
+                              << " scope_id=" << sa.sin6_scope_id;
+                }
+                return;
+            }
+            default:
+                LOG(FATAL) << "Invalid domain";
+                break;
+        }
+    }
+
+    // Called by fdevent handler when data is available.
+    void ReceiveMessage() {
+        if (!fd_.ok()) {
+            client_->OnRead(this, Error::Code::kSocketClosedFailure);
+            return;
+        }
+
+        const auto bytes_available = network_peek(fd_);
+        if (!bytes_available.has_value()) {
+            client_->OnRead(this, ChooseError(errno, Error::Code::kSocketReadFailure));
+            return;
+        }
+
+        UdpPacket packet(*bytes_available);
+        packet.set_socket(this);
+        Error result = Error::Code::kUnknownError;
+        switch (local_endpoint_.address.version()) {
+            case UdpSocket::Version::kV4: {
+                result = ReceiveMessageInternal<sockaddr_in, in_pktinfo>(fd_, &packet);
+                break;
+            }
+            case UdpSocket::Version::kV6: {
+                result = ReceiveMessageInternal<sockaddr_in6, in6_pktinfo>(fd_, &packet);
+                break;
+            }
+            default: {
+                LOG(FATAL) << "Invalid domain";
+                break;
+            }
+        }
+
+        client_->OnRead(this, result.ok() ? ErrorOr<UdpPacket>(std::move(packet))
+                                          : ErrorOr<UdpPacket>(std::move(result)));
+    }
+
+    void OnError(Error::Code error_code) {
+        // Close the socket unless the error code represents a transient condition.
+        if (error_code != Error::Code::kNone && error_code != Error::Code::kAgain) {
+            if (fde_) {
+                fdevent_destroy(fde_);
+                fde_ = nullptr;
+            }
+        }
+
+        std::stringstream stream;
+        stream << "endpoint: " << local_endpoint_;
+        client_->OnError(this, Error(error_code, stream.str()));
+    }
+
+    static void OnFdeventResult(int fd, unsigned ev, void* opaque) {
+        AdbUdpSocket* s = reinterpret_cast<AdbUdpSocket*>(opaque);
+        if (ev & FDE_READ) {
+            s->ReceiveMessage();
+        }
+    }
+
+    Client* const client_;
+    mutable IPEndpoint local_endpoint_;
+    unique_fd fd_;
+    fdevent* fde_ = nullptr;
+    std::optional<NetworkInterfaceIndex> mdns_ifindex_;
+};
+
+}  // namespace
+
+// Implementation of openscreen's platform APIs for udp_socket.h
+// static
+ErrorOr<std::unique_ptr<UdpSocket>> UdpSocket::Create(TaskRunner* task_runner,
+                                                      UdpSocket::Client* client,
+                                                      const IPEndpoint& local_endpoint) {
+    // |task_runner| is not used in adb's udp implementation because everything is going through the
+    // fdevent thread when we register the fd.
+    std::string err;
+    std::stringstream ip_addr_ss;
+    ip_addr_ss << local_endpoint.address;
+
+    int domain;
+    switch (local_endpoint.address.version()) {
+        case Version::kV4:
+            domain = AF_INET;
+            break;
+        case Version::kV6:
+            domain = AF_INET6;
+            break;
+        default:
+            LOG(FATAL) << "Invalid domain";
+            return Error::Code::kInitializationFailure;
+    }
+
+    unique_fd fd(adb_socket(domain, SOCK_DGRAM, 0));
+    if (!fd.ok()) {
+        PLOG(ERROR) << "Failed to create udp socket";
+        return Error::Code::kInitializationFailure;
+    }
+
+    if (!set_file_block_mode(fd, false)) {
+        PLOG(ERROR) << "Failed to set non-block mode on fd";
+        return Error::Code::kInitializationFailure;
+    }
+
+    LOG(INFO) << "UDP socket created for " << local_endpoint;
+    std::unique_ptr<UdpSocket> udp_socket(new AdbUdpSocket(client, local_endpoint, std::move(fd)));
+    return udp_socket;
+}
+
+}  // namespace openscreen
diff --git a/client/transport_mdns.cpp b/client/transport_mdns.cpp
index a0fc9ca..843aca8 100644
--- a/client/transport_mdns.cpp
+++ b/client/transport_mdns.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -31,7 +31,14 @@
 
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#include <dns_sd.h>
+
+#include <discovery/common/config.h>
+#include <discovery/common/reporting_client.h>
+#include <discovery/public/dns_sd_service_factory.h>
+#include <platform/api/network_interface.h>
+#include <platform/api/serial_delete_ptr.h>
+#include <platform/base/error.h>
+#include <platform/base/interface_info.h>
 
 #include "adb_client.h"
 #include "adb_mdns.h"
@@ -39,653 +46,275 @@
 #include "adb_utils.h"
 #include "adb_wifi.h"
 #include "client/mdns_utils.h"
+#include "client/openscreen/mdns_service_watcher.h"
+#include "client/openscreen/platform/task_runner.h"
 #include "fdevent/fdevent.h"
 #include "sysdeps.h"
 
-static DNSServiceRef service_refs[kNumADBDNSServices];
-static fdevent* service_ref_fdes[kNumADBDNSServices];
-static auto& g_autoconn_whitelist = *new std::unordered_set<int>();
+namespace {
 
-static int adb_DNSServiceIndexByName(std::string_view regType) {
-    for (int i = 0; i < kNumADBDNSServices; ++i) {
-        if (!strncmp(regType.data(), kADBDNSServices[i], strlen(kADBDNSServices[i]))) {
-            return i;
-        }
-    }
-    return -1;
-}
+using namespace mdns;
+using namespace openscreen;
+using ServicesUpdatedState = mdns::ServiceReceiver::ServicesUpdatedState;
 
-static void config_auto_connect_services() {
-    // ADB_MDNS_AUTO_CONNECT is a comma-delimited list of mdns services
-    // that are allowed to auto-connect. By default, only allow "adb-tls-connect"
-    // to auto-connect, since this is filtered down to auto-connect only to paired
-    // devices.
-    g_autoconn_whitelist.insert(kADBSecureConnectServiceRefIndex);
-    const char* srvs = getenv("ADB_MDNS_AUTO_CONNECT");
-    if (!srvs) {
-        return;
+struct DiscoveryState;
+DiscoveryState* g_state = nullptr;
+// TODO: remove once openscreen has bonjour client APIs.
+bool g_using_bonjour = false;
+AdbMdnsResponderFuncs g_adb_mdnsresponder_funcs;
+
+class DiscoveryReportingClient : public discovery::ReportingClient {
+  public:
+    void OnFatalError(Error error) override {
+        // The multicast port 5353 may fail to bind because of another process already binding
+        // to it (bonjour). So let's fallback to bonjour client APIs.
+        // TODO: Remove this once openscreen implements the bonjour client APIs.
+        LOG(ERROR) << "Encountered fatal discovery error: " << error;
+        got_fatal_ = true;
     }
 
-    if (strcmp(srvs, "0") == 0) {
-        D("Disabling all auto-connecting");
-        g_autoconn_whitelist.clear();
-        return;
+    void OnRecoverableError(Error error) override {
+        LOG(ERROR) << "Encountered recoverable discovery error: " << error;
     }
 
-    if (strcmp(srvs, "1") == 0) {
-        D("Allow all auto-connecting");
-        g_autoconn_whitelist.insert(kADBTransportServiceRefIndex);
-        return;
-    }
+    bool GotFatalError() const { return got_fatal_; }
 
-    // Selectively choose which services to allow auto-connect.
-    // E.g. ADB_MDNS_AUTO_CONNECT=adb,adb-tls-connect would allow
-    // _adb._tcp and _adb-tls-connnect._tcp services to auto-connect.
-    auto srvs_list = android::base::Split(srvs, ",");
-    std::unordered_set<int> new_whitelist;
-    for (const auto& item : srvs_list) {
-        auto full_srv = android::base::StringPrintf("_%s._tcp", item.data());
-        int idx = adb_DNSServiceIndexByName(full_srv);
-        if (idx >= 0) {
-            new_whitelist.insert(idx);
-        }
-    }
+  private:
+    std::atomic<bool> got_fatal_{false};
+};
 
-    if (!new_whitelist.empty()) {
-        g_autoconn_whitelist = std::move(new_whitelist);
+struct DiscoveryState {
+    SerialDeletePtr<discovery::DnsSdService> service;
+    std::unique_ptr<DiscoveryReportingClient> reporting_client;
+    std::unique_ptr<AdbOspTaskRunner> task_runner;
+    std::vector<std::unique_ptr<ServiceReceiver>> receivers;
+    InterfaceInfo interface_info;
+};
+
+// Callback provided to service receiver for updates.
+void OnServiceReceiverResult(std::vector<std::reference_wrapper<const ServiceInfo>> infos,
+                             std::reference_wrapper<const ServiceInfo> info,
+                             ServicesUpdatedState state) {
+    LOG(INFO) << "Endpoint state=" << static_cast<int>(state)
+              << " instance_name=" << info.get().instance_name
+              << " service_name=" << info.get().service_name << " addr=" << info.get().v4_address
+              << " addrv6=" << info.get().v6_address << " total_serv=" << infos.size();
+
+    switch (state) {
+        case ServicesUpdatedState::EndpointCreated:
+        case ServicesUpdatedState::EndpointUpdated:
+            if (adb_DNSServiceShouldAutoConnect(info.get().service_name,
+                                                info.get().instance_name) &&
+                info.get().v4_address) {
+                auto index = adb_DNSServiceIndexByName(info.get().service_name);
+                if (!index) {
+                    return;
+                }
+
+                // Don't try to auto-connect if not in the keystore.
+                if (*index == kADBSecureConnectServiceRefIndex &&
+                    !adb_wifi_is_known_host(info.get().instance_name)) {
+                    LOG(INFO) << "instance_name=" << info.get().instance_name << " not in keystore";
+                    return;
+                }
+                std::string response;
+                LOG(INFO) << "Attempting to auto-connect to instance=" << info.get().instance_name
+                          << " service=" << info.get().service_name << " addr4=%s"
+                          << info.get().v4_address << ":" << info.get().port;
+                connect_device(
+                        android::base::StringPrintf("%s.%s", info.get().instance_name.c_str(),
+                                                    info.get().service_name.c_str()),
+                        &response);
+            }
+            break;
+        default:
+            break;
     }
 }
 
-static bool adb_DNSServiceShouldAutoConnect(const char* regType, const char* serviceName) {
-    // Try to auto-connect to any "_adb" or "_adb-tls-connect" services excluding emulator services.
-    int index = adb_DNSServiceIndexByName(regType);
-    if (index != kADBTransportServiceRefIndex && index != kADBSecureConnectServiceRefIndex) {
+std::optional<discovery::Config> GetConfigForAllInterfaces() {
+    auto interface_infos = GetNetworkInterfaces();
+
+    discovery::Config config;
+    for (const auto interface : interface_infos) {
+        discovery::Config::NetworkInfo::AddressFamilies supported_address_families =
+                discovery::Config::NetworkInfo::kNoAddressFamily;
+        if (interface.GetIpAddressV4()) {
+            supported_address_families |= discovery::Config::NetworkInfo::kUseIpV4;
+        }
+        if (interface.GetIpAddressV6()) {
+            supported_address_families |= discovery::Config::NetworkInfo::kUseIpV6;
+        }
+        if (supported_address_families == discovery::Config::NetworkInfo::kNoAddressFamily) {
+            LOG(INFO) << "Interface [" << interface << "] doesn't support ipv4 or ipv6."
+                      << " Ignoring interface.";
+            continue;
+        }
+        config.network_info.push_back({interface, supported_address_families});
+        LOG(VERBOSE) << "Listening on interface [" << interface << "]";
+    }
+
+    if (config.network_info.empty()) {
+        LOG(INFO) << "No available network interfaces for mDNS discovery";
+        return std::nullopt;
+    }
+
+    return config;
+}
+
+void StartDiscovery() {
+    CHECK(!g_state);
+    g_state = new DiscoveryState();
+    g_state->task_runner = std::make_unique<AdbOspTaskRunner>();
+    g_state->reporting_client = std::make_unique<DiscoveryReportingClient>();
+
+    g_state->task_runner->PostTask([]() {
+        auto config = GetConfigForAllInterfaces();
+        if (!config) {
+            return;
+        }
+
+        g_state->service = discovery::CreateDnsSdService(g_state->task_runner.get(),
+                                                         g_state->reporting_client.get(), *config);
+        // Register a receiver for each service type
+        for (int i = 0; i < kNumADBDNSServices; ++i) {
+            auto receiver = std::make_unique<ServiceReceiver>(
+                    g_state->service.get(), kADBDNSServices[i], OnServiceReceiverResult);
+            receiver->StartDiscovery();
+            g_state->receivers.push_back(std::move(receiver));
+
+            if (g_state->reporting_client->GotFatalError()) {
+                for (auto& r : g_state->receivers) {
+                    if (r->is_running()) {
+                        r->StopDiscovery();
+                    }
+                }
+                g_using_bonjour = true;
+                break;
+            }
+        }
+
+        if (g_using_bonjour) {
+            LOG(INFO) << "Fallback to MdnsResponder client for discovery";
+            g_adb_mdnsresponder_funcs = StartMdnsResponderDiscovery();
+        }
+    });
+}
+
+void ForEachService(const std::unique_ptr<ServiceReceiver>& receiver,
+                    std::string_view wanted_instance_name, adb_secure_foreach_service_callback cb) {
+    if (!receiver->is_running()) {
+        return;
+    }
+    auto services = receiver->GetServices();
+    for (const auto& s : services) {
+        if (wanted_instance_name.empty() || s.get().instance_name == wanted_instance_name) {
+            std::stringstream ss;
+            ss << s.get().v4_address;
+            cb(s.get().instance_name.c_str(), s.get().service_name.c_str(), ss.str().c_str(),
+               s.get().port);
+        }
+    }
+}
+
+bool ConnectAdbSecureDevice(const MdnsInfo& info) {
+    if (!adb_wifi_is_known_host(info.service_name)) {
+        LOG(INFO) << "serviceName=" << info.service_name << " not in keystore";
         return false;
     }
-    if (g_autoconn_whitelist.find(index) == g_autoconn_whitelist.end()) {
-        D("Auto-connect for regType '%s' disabled", regType);
-        return false;
-    }
-    // Ignore adb-EMULATOR* service names, as it interferes with the
-    // emulator ports that are already connected.
-    if (android::base::StartsWith(serviceName, "adb-EMULATOR")) {
-        LOG(INFO) << "Ignoring emulator transport service [" << serviceName << "]";
-        return false;
-    }
+
+    std::string response;
+    connect_device(android::base::StringPrintf("%s.%s", info.service_name.c_str(),
+                                               info.service_type.c_str()),
+                   &response);
+    D("Secure connect to %s regtype %s (%s:%hu) : %s", info.service_name.c_str(),
+      info.service_type.c_str(), info.addr.c_str(), info.port, response.c_str());
     return true;
 }
 
-// Use adb_DNSServiceRefSockFD() instead of calling DNSServiceRefSockFD()
-// directly so that the socket is put through the appropriate compatibility
-// layers to work with the rest of ADB's internal APIs.
-static inline int adb_DNSServiceRefSockFD(DNSServiceRef ref) {
-    return adb_register_socket(DNSServiceRefSockFD(ref));
-}
-#define DNSServiceRefSockFD ___xxx_DNSServiceRefSockFD
+}  // namespace
 
-static void DNSSD_API register_service_ip(DNSServiceRef sdRef,
-                                          DNSServiceFlags flags,
-                                          uint32_t interfaceIndex,
-                                          DNSServiceErrorType errorCode,
-                                          const char* hostname,
-                                          const sockaddr* address,
-                                          uint32_t ttl,
-                                          void* context);
-
-static void pump_service_ref(int /*fd*/, unsigned ev, void* data) {
-    DNSServiceRef* ref = reinterpret_cast<DNSServiceRef*>(data);
-
-    if (ev & FDE_READ)
-        DNSServiceProcessResult(*ref);
-}
-
-class AsyncServiceRef {
-  public:
-    bool Initialized() {
-        return initialized_;
-    }
-
-    void DestroyServiceRef() {
-        if (!initialized_) {
-            return;
-        }
-
-        // Order matters here! Must destroy the fdevent first since it has a
-        // reference to |sdRef_|.
-        fdevent_destroy(fde_);
-        D("DNSServiceRefDeallocate(sdRef=%p)", sdRef_);
-        DNSServiceRefDeallocate(sdRef_);
-        initialized_ = false;
-    }
-
-    virtual ~AsyncServiceRef() { DestroyServiceRef(); }
-
-  protected:
-    DNSServiceRef sdRef_;
-
-    void Initialize() {
-        fde_ = fdevent_create(adb_DNSServiceRefSockFD(sdRef_), pump_service_ref, &sdRef_);
-        if (fde_ == nullptr) {
-            D("Unable to create fdevent");
-            return;
-        }
-        fdevent_set(fde_, FDE_READ);
-        initialized_ = true;
-    }
-
-  private:
-    bool initialized_ = false;
-    fdevent* fde_;
-};
-
-class ResolvedService : public AsyncServiceRef {
-  public:
-    virtual ~ResolvedService() = default;
-
-    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
-         * this, we get served link-local addresses that are usually useless to
-         * connect to. What's more, we seem to /only/ get those and nothing else.
-         * If we want IPv6 in the future we'll have to figure out why.
-         */
-        DNSServiceErrorType ret =
-            DNSServiceGetAddrInfo(
-                &sdRef_, 0, interfaceIndex,
-                kDNSServiceProtocol_IPv4, hosttarget,
-                register_service_ip, reinterpret_cast<void*>(this));
-
-        if (ret != kDNSServiceErr_NoError) {
-            D("Got %d from DNSServiceGetAddrInfo.", ret);
-        } else {
-            D("DNSServiceGetAddrInfo(sdRef=%p, hosttarget=%s)", sdRef_, hosttarget);
-            Initialize();
-        }
-
-        D("Client version: %d Service version: %d\n", clientVersion_, serviceVersion_);
-    }
-
-    bool ConnectSecureWifiDevice() {
-        if (!adb_wifi_is_known_host(serviceName_)) {
-            LOG(INFO) << "serviceName=" << serviceName_ << " not in keystore";
-            return false;
-        }
-
-        std::string response;
-        connect_device(android::base::StringPrintf("%s.%s", serviceName_.c_str(), regType_.c_str()),
-                       &response);
-        D("Secure connect to %s regtype %s (%s:%hu) : %s", serviceName_.c_str(), regType_.c_str(),
-          ip_addr_, port_, response.c_str());
-        return true;
-    }
-
-    bool AddToServiceRegistry(const sockaddr* address) {
-        sa_family_ = address->sa_family;
-
-        if (sa_family_ == AF_INET) {
-            ip_addr_data_ = &reinterpret_cast<const sockaddr_in*>(address)->sin_addr;
-            addr_format_ = "%s:%hu";
-        } else if (sa_family_ == AF_INET6) {
-            ip_addr_data_ = &reinterpret_cast<const sockaddr_in6*>(address)->sin6_addr;
-            addr_format_ = "[%s]:%hu";
-        } else {  // Should be impossible
-            D("mDNS resolved non-IP address.");
-            return false;
-        }
-
-        // Winsock version requires the const cast Because Microsoft.
-        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 false;
-        }
-
-        // Remove any services with the same instance name, as it may be a stale registration.
-        removeDNSService(regType_.c_str(), serviceName_.c_str());
-
-        // Add to the service registry before trying to auto-connect, since socket_spec_connect will
-        // check these registries for the ip address when connecting via mdns instance name.
-        int adbSecureServiceType = serviceIndex();
-        ServiceRegistry* services = nullptr;
-        switch (adbSecureServiceType) {
-            case kADBTransportServiceRefIndex:
-                services = sAdbTransportServices;
-                break;
-            case kADBSecurePairingServiceRefIndex:
-                services = sAdbSecurePairingServices;
-                break;
-            case kADBSecureConnectServiceRefIndex:
-                services = sAdbSecureConnectServices;
-                break;
-            default:
-                LOG(WARNING) << "No registry available for reg_type=[" << regType_ << "]";
-                return false;
-        }
-
-        services->push_back(std::unique_ptr<ResolvedService>(this));
-
-        if (adb_DNSServiceShouldAutoConnect(regType_.c_str(), serviceName_.c_str())) {
-            std::string response;
-            D("Attempting to connect serviceName=[%s], regtype=[%s] ipaddr=(%s:%hu)",
-              serviceName_.c_str(), regType_.c_str(), ip_addr_, port_);
-            int index = adb_DNSServiceIndexByName(regType_.c_str());
-            if (index == kADBSecureConnectServiceRefIndex) {
-                ConnectSecureWifiDevice();
-            } else {
-                connect_device(android::base::StringPrintf("%s.%s", serviceName_.c_str(),
-                                                           regType_.c_str()),
-                               &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_);
-        }
-
-        return true;
-    }
-
-    int serviceIndex() const { return adb_DNSServiceIndexByName(regType_.c_str()); }
-
-    std::string hostTarget() const { return hosttarget_; }
-
-    std::string serviceName() const { return serviceName_; }
-
-    std::string regType() const { return regType_; }
-
-    std::string ipAddress() const { return ip_addr_; }
-
-    uint16_t port() const { return port_; }
-
-    using ServiceRegistry = std::vector<std::unique_ptr<ResolvedService>>;
-
-    // unencrypted tcp connections
-    static ServiceRegistry* sAdbTransportServices;
-
-    static ServiceRegistry* sAdbSecurePairingServices;
-    static ServiceRegistry* sAdbSecureConnectServices;
-
-    static void initAdbServiceRegistries();
-
-    static void forEachService(const ServiceRegistry& services, std::string_view hostname,
-                               adb_secure_foreach_service_callback cb);
-
-    static bool connectByServiceName(const ServiceRegistry& services,
-                                     const std::string& service_name);
-
-    static void removeDNSService(const char* regType, const char* serviceName);
-
-  private:
-    int clientVersion_ = ADB_SECURE_CLIENT_VERSION;
-    std::string addr_format_;
-    std::string serviceName_;
-    std::string regType_;
-    std::string hosttarget_;
-    const uint16_t port_;
-    int sa_family_;
-    const void* ip_addr_data_;
-    char ip_addr_[INET6_ADDRSTRLEN];
-    int serviceVersion_;
-};
-
-// static
-ResolvedService::ServiceRegistry* ResolvedService::sAdbTransportServices = NULL;
-
-// static
-ResolvedService::ServiceRegistry* ResolvedService::sAdbSecurePairingServices = NULL;
-
-// static
-ResolvedService::ServiceRegistry* ResolvedService::sAdbSecureConnectServices = NULL;
-
-// static
-void ResolvedService::initAdbServiceRegistries() {
-    if (!sAdbTransportServices) {
-        sAdbTransportServices = new ServiceRegistry;
-    }
-    if (!sAdbSecurePairingServices) {
-        sAdbSecurePairingServices = new ServiceRegistry;
-    }
-    if (!sAdbSecureConnectServices) {
-        sAdbSecureConnectServices = new ServiceRegistry;
-    }
-}
-
-// static
-void ResolvedService::forEachService(const ServiceRegistry& services,
-                                     std::string_view wanted_service_name,
-                                     adb_secure_foreach_service_callback cb) {
-    initAdbServiceRegistries();
-
-    for (const auto& service : services) {
-        auto service_name = service->serviceName();
-        auto reg_type = service->regType();
-        auto ip = service->ipAddress();
-        auto port = service->port();
-
-        if (wanted_service_name.empty()) {
-            cb(service_name.c_str(), reg_type.c_str(), ip.c_str(), port);
-        } else if (service_name == wanted_service_name) {
-            cb(service_name.c_str(), reg_type.c_str(), ip.c_str(), port);
-        }
-    }
-}
-
-// static
-bool ResolvedService::connectByServiceName(const ServiceRegistry& services,
-                                           const std::string& service_name) {
-    initAdbServiceRegistries();
-    for (const auto& service : services) {
-        if (service_name == service->serviceName()) {
-            D("Got service_name match [%s]", service->serviceName().c_str());
-            return service->ConnectSecureWifiDevice();
-        }
-    }
-    D("No registered serviceNames matched [%s]", service_name.c_str());
-    return false;
-}
-
-// static
-void ResolvedService::removeDNSService(const char* regType, const char* serviceName) {
-    D("%s: regType=[%s] serviceName=[%s]", __func__, regType, serviceName);
-    int index = adb_DNSServiceIndexByName(regType);
-    ServiceRegistry* services;
-    switch (index) {
-        case kADBTransportServiceRefIndex:
-            services = sAdbTransportServices;
-            break;
-        case kADBSecurePairingServiceRefIndex:
-            services = sAdbSecurePairingServices;
-            break;
-        case kADBSecureConnectServiceRefIndex:
-            services = sAdbSecureConnectServices;
-            break;
-        default:
-            return;
-    }
-
-    if (services->empty()) {
-        return;
-    }
-
-    std::string sName(serviceName);
-    services->erase(std::remove_if(services->begin(), services->end(),
-                                   [&sName](std::unique_ptr<ResolvedService>& service) {
-                                       return (sName == service->serviceName());
-                                   }),
-                    services->end());
-}
-
-void adb_secure_foreach_pairing_service(const char* service_name,
-                                        adb_secure_foreach_service_callback cb) {
-    ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices, service_name, cb);
-}
-
-void adb_secure_foreach_connect_service(const char* service_name,
-                                        adb_secure_foreach_service_callback cb) {
-    ResolvedService::forEachService(*ResolvedService::sAdbSecureConnectServices, service_name, cb);
-}
-
-bool adb_secure_connect_by_service_name(const char* service_name) {
-    return ResolvedService::connectByServiceName(*ResolvedService::sAdbSecureConnectServices,
-                                                 service_name);
-}
-
-static void DNSSD_API register_service_ip(DNSServiceRef sdRef, DNSServiceFlags flags,
-                                          uint32_t /*interfaceIndex*/,
-                                          DNSServiceErrorType errorCode, const char* hostname,
-                                          const sockaddr* address, uint32_t ttl, void* context) {
-    D("%s: sdRef=%p flags=0x%08x errorCode=%u ttl=%u", __func__, sdRef, flags, errorCode, ttl);
-    std::unique_ptr<ResolvedService> data(
-        reinterpret_cast<ResolvedService*>(context));
-    // Only resolve the address once. If the address or port changes, we'll just get another
-    // registration.
-    data->DestroyServiceRef();
-
-    if (errorCode != kDNSServiceErr_NoError) {
-        D("Got error while looking up ipaddr [%u]", errorCode);
-        return;
-    }
-
-    if (flags & kDNSServiceFlagsAdd) {
-        D("Resolved IP address for [%s]. Adding to service registry.", hostname);
-        auto* ptr = data.release();
-        if (!ptr->AddToServiceRegistry(address)) {
-            data.reset(ptr);
-        }
-    }
-}
-
-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);
-
-class DiscoveredService : public AsyncServiceRef {
-  public:
-    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));
-
-        D("DNSServiceResolve for "
-          "interfaceIndex %u "
-          "serviceName %s "
-          "regtype %s "
-          "domain %s "
-          ": %d",
-          interfaceIndex, serviceName, regtype, domain, ret);
-
-        if (ret == kDNSServiceErr_NoError) {
-            Initialize();
-        }
-    }
-
-    const char* ServiceName() {
-        return serviceName_.c_str();
-    }
-
-    const char* RegType() { return regType_.c_str(); }
-
-  private:
-    std::string serviceName_;
-    std::string regType_;
-};
-
-// 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));
-
-    if (errorCode != kDNSServiceErr_NoError) {
-        D("Got error %d resolving service.", errorCode);
-        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(), discovered->RegType(),
-                                        interfaceIndex, hosttarget, ntohs(port), serviceVersion);
-
-    if (! resolved->Initialized()) {
-        D("Unable to init resolved service");
-        delete resolved;
-    }
-
-    if (flags) { /* Only ever equals MoreComing or 0 */
-        D("releasing discovered service");
-        discovered.release();
-    }
-}
-
-static void DNSSD_API on_service_browsed(DNSServiceRef sdRef, DNSServiceFlags flags,
-                                         uint32_t interfaceIndex, DNSServiceErrorType errorCode,
-                                         const char* serviceName, const char* regtype,
-                                         const char* domain, void* /*context*/) {
-    if (errorCode != kDNSServiceErr_NoError) {
-        D("Got error %d during mDNS browse.", errorCode);
-        DNSServiceRefDeallocate(sdRef);
-        int serviceIndex = adb_DNSServiceIndexByName(regtype);
-        if (serviceIndex != -1) {
-            fdevent_destroy(service_ref_fdes[serviceIndex]);
-        }
-        return;
-    }
-
-    if (flags & kDNSServiceFlagsAdd) {
-        D("%s: Discover found new serviceName=[%s] regtype=[%s] domain=[%s]", __func__, serviceName,
-          regtype, domain);
-        auto discovered = new DiscoveredService(interfaceIndex, serviceName, regtype, domain);
-        if (!discovered->Initialized()) {
-            delete discovered;
-        }
-    } else {
-        D("%s: Discover lost serviceName=[%s] regtype=[%s] domain=[%s]", __func__, serviceName,
-          regtype, domain);
-        ResolvedService::removeDNSService(regtype, serviceName);
-    }
-}
-
-void init_mdns_transport_discovery_thread(void) {
-    config_auto_connect_services();
-    std::string res;
-    std::for_each(g_autoconn_whitelist.begin(), g_autoconn_whitelist.end(), [&](const int& i) {
-        res += kADBDNSServices[i];
-        res += ",";
-    });
-    D("mdns auto-connect whitelist: [%s]", res.data());
-
-    int errorCodes[kNumADBDNSServices];
-    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);
-            });
-        }
+/////////////////////////////////////////////////////////////////////////////////
+void mdns_cleanup() {
+    if (g_using_bonjour) {
+        return g_adb_mdnsresponder_funcs.mdns_cleanup();
     }
 }
 
 void init_mdns_transport_discovery(void) {
-    ResolvedService::initAdbServiceRegistries();
-    std::thread(init_mdns_transport_discovery_thread).detach();
+    // TODO(joshuaduong): Use openscreen discovery by default for all platforms.
+    const char* mdns_osp = getenv("ADB_MDNS_OPENSCREEN");
+    if (mdns_osp && strcmp(mdns_osp, "1") == 0) {
+        LOG(INFO) << "Openscreen mdns discovery enabled";
+        StartDiscovery();
+    } else {
+        // Original behavior is to use Bonjour client.
+        g_using_bonjour = true;
+        g_adb_mdnsresponder_funcs = StartMdnsResponderDiscovery();
+    }
+}
+
+bool adb_secure_connect_by_service_name(const std::string& instance_name) {
+    if (g_using_bonjour) {
+        return g_adb_mdnsresponder_funcs.adb_secure_connect_by_service_name(instance_name);
+    }
+
+    if (!g_state || g_state->receivers.empty()) {
+        LOG(INFO) << "Mdns not enabled";
+        return false;
+    }
+
+    std::optional<MdnsInfo> info;
+    auto cb = [&](const std::string& instance_name, const std::string& service_name,
+                  const std::string& ip_addr,
+                  uint16_t port) { info.emplace(instance_name, service_name, ip_addr, port); };
+    ForEachService(g_state->receivers[kADBSecureConnectServiceRefIndex], instance_name, cb);
+    if (info.has_value()) {
+        return ConnectAdbSecureDevice(*info);
+    }
+    return false;
 }
 
 std::string mdns_check() {
-    uint32_t daemon_version;
-    uint32_t sz = sizeof(daemon_version);
-
-    auto dnserr = DNSServiceGetProperty(kDNSServiceProperty_DaemonVersion, &daemon_version, &sz);
-    std::string result = "ERROR: mdns daemon unavailable";
-    if (dnserr != kDNSServiceErr_NoError) {
-        return result;
+    if (g_using_bonjour) {
+        return g_adb_mdnsresponder_funcs.mdns_check();
     }
 
-    result = android::base::StringPrintf("mdns daemon version [%u]", daemon_version);
-    return result;
+    return "mdns daemon version [Openscreen discovery 0.0.0]";
 }
 
 std::string mdns_list_discovered_services() {
+    if (g_using_bonjour) {
+        return g_adb_mdnsresponder_funcs.mdns_list_discovered_services();
+    }
+
+    if (!g_state || g_state->receivers.empty()) {
+        return "";
+    }
+
     std::string result;
-    auto cb = [&](const char* service_name, const char* reg_type, const char* ip_addr,
-                  uint16_t port) {
-        result += android::base::StringPrintf("%s\t%s\t%s:%u\n", service_name, reg_type, ip_addr,
-                                              port);
+    auto cb = [&](const std::string& instance_name, const std::string& service_name,
+                  const std::string& ip_addr, uint16_t port) {
+        result += android::base::StringPrintf("%s\t%s\t%s:%u\n", instance_name.data(),
+                                              service_name.data(), ip_addr.data(), port);
     };
 
-    ResolvedService::forEachService(*ResolvedService::sAdbTransportServices, "", cb);
-    ResolvedService::forEachService(*ResolvedService::sAdbSecureConnectServices, "", cb);
-    ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices, "", cb);
+    for (const auto& receiver : g_state->receivers) {
+        ForEachService(receiver, "", cb);
+    }
     return result;
 }
 
-std::optional<MdnsInfo> mdns_get_connect_service_info(std::string_view name) {
+std::optional<MdnsInfo> mdns_get_connect_service_info(const std::string& name) {
     CHECK(!name.empty());
 
-    // only adb server creates these registries
-    if (!ResolvedService::sAdbTransportServices && !ResolvedService::sAdbSecureConnectServices) {
+    if (g_using_bonjour) {
+        return g_adb_mdnsresponder_funcs.mdns_get_connect_service_info(name);
+    }
+
+    if (!g_state || g_state->receivers.empty()) {
         return std::nullopt;
     }
-    CHECK(ResolvedService::sAdbTransportServices);
-    CHECK(ResolvedService::sAdbSecureConnectServices);
 
     auto mdns_instance = mdns::mdns_parse_instance_name(name);
     if (!mdns_instance.has_value()) {
@@ -694,68 +323,83 @@
     }
 
     std::optional<MdnsInfo> info;
-    auto cb = [&](const char* service_name, const char* reg_type, const char* ip_addr,
-                  uint16_t port) { info.emplace(service_name, reg_type, ip_addr, port); };
+    auto cb = [&](const std::string& instance_name, const std::string& service_name,
+                  const std::string& ip_addr,
+                  uint16_t port) { info.emplace(instance_name, service_name, ip_addr, port); };
 
     std::string reg_type;
+    // Service name was provided.
     if (!mdns_instance->service_name.empty()) {
         reg_type = android::base::StringPrintf("%s.%s", mdns_instance->service_name.data(),
                                                mdns_instance->transport_type.data());
-        int index = adb_DNSServiceIndexByName(reg_type);
-        switch (index) {
+        const auto index = adb_DNSServiceIndexByName(reg_type);
+        if (!index) {
+            return std::nullopt;
+        }
+        switch (*index) {
             case kADBTransportServiceRefIndex:
-                ResolvedService::forEachService(*ResolvedService::sAdbTransportServices,
-                                                mdns_instance->instance_name, cb);
-                break;
             case kADBSecureConnectServiceRefIndex:
-                ResolvedService::forEachService(*ResolvedService::sAdbSecureConnectServices,
-                                                mdns_instance->instance_name, cb);
+                ForEachService(g_state->receivers[*index], mdns_instance->instance_name, cb);
                 break;
             default:
-                D("Unknown reg_type [%s]", reg_type.data());
+                D("Not a connectable service name [%s]", reg_type.data());
                 return std::nullopt;
         }
         return info;
     }
 
-    for (const auto& service :
-         {ResolvedService::sAdbTransportServices, ResolvedService::sAdbSecureConnectServices}) {
-        ResolvedService::forEachService(*service, name, cb);
-        if (info.has_value()) {
-            return info;
-        }
+    // No mdns service name provided. Just search for the instance name in all adb connect services.
+    // Prefer the secured connect service over the other.
+    ForEachService(g_state->receivers[kADBSecureConnectServiceRefIndex], name, cb);
+    if (!info.has_value()) {
+        ForEachService(g_state->receivers[kADBTransportServiceRefIndex], name, cb);
     }
 
-    return std::nullopt;
+    return info;
 }
 
-std::optional<MdnsInfo> mdns_get_pairing_service_info(std::string_view name) {
+std::optional<MdnsInfo> mdns_get_pairing_service_info(const std::string& name) {
     CHECK(!name.empty());
 
+    if (g_using_bonjour) {
+        return g_adb_mdnsresponder_funcs.mdns_get_pairing_service_info(name);
+    }
+
+    if (!g_state || g_state->receivers.empty()) {
+        return std::nullopt;
+    }
+
     auto mdns_instance = mdns::mdns_parse_instance_name(name);
     if (!mdns_instance.has_value()) {
-        D("Failed to parse mDNS pairing name [%s]", name.data());
+        D("Failed to parse mDNS name [%s]", name.data());
         return std::nullopt;
     }
 
     std::optional<MdnsInfo> info;
-    auto cb = [&](const char* service_name, const char* reg_type, const char* ip_addr,
-                  uint16_t port) { info.emplace(service_name, reg_type, ip_addr, port); };
+    auto cb = [&](const std::string& instance_name, const std::string& service_name,
+                  const std::string& ip_addr,
+                  uint16_t port) { info.emplace(instance_name, service_name, ip_addr, port); };
 
+    std::string reg_type;
     // Verify it's a pairing service if user explicitly inputs it.
     if (!mdns_instance->service_name.empty()) {
-        auto reg_type = android::base::StringPrintf("%s.%s", mdns_instance->service_name.data(),
-                                                    mdns_instance->transport_type.data());
-        int index = adb_DNSServiceIndexByName(reg_type);
-        switch (index) {
+        reg_type = android::base::StringPrintf("%s.%s", mdns_instance->service_name.data(),
+                                               mdns_instance->transport_type.data());
+        const auto index = adb_DNSServiceIndexByName(reg_type);
+        if (!index) {
+            return std::nullopt;
+        }
+        switch (*index) {
             case kADBSecurePairingServiceRefIndex:
                 break;
             default:
                 D("Not an adb pairing reg_type [%s]", reg_type.data());
                 return std::nullopt;
         }
+        return info;
     }
 
-    ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices, name, cb);
+    ForEachService(g_state->receivers[kADBSecurePairingServiceRefIndex], name, cb);
+
     return info;
 }
diff --git a/socket_spec.cpp b/socket_spec.cpp
index 5cad70d..359e238 100644
--- a/socket_spec.cpp
+++ b/socket_spec.cpp
@@ -29,8 +29,8 @@
 #include <cutils/sockets.h>
 
 #include "adb.h"
+#include "adb_mdns.h"
 #include "adb_utils.h"
-#include "adb_wifi.h"
 #include "sysdeps.h"
 
 using namespace std::string_literals;
@@ -197,7 +197,7 @@
         } else {
 #if ADB_HOST
             // Check if the address is an mdns service we can connect to.
-            if (auto mdns_info = mdns_get_connect_service_info(address.substr(4));
+            if (auto mdns_info = mdns_get_connect_service_info(std::string(address.substr(4)));
                 mdns_info != std::nullopt) {
                 fd->reset(network_connect(mdns_info->addr, mdns_info->port, SOCK_STREAM, 0, error));
                 if (fd->get() != -1) {
diff --git a/sysdeps.h b/sysdeps.h
index 7326ab1..5ba85b4 100644
--- a/sysdeps.h
+++ b/sysdeps.h
@@ -26,6 +26,7 @@
 
 #include <errno.h>
 
+#include <optional>
 #include <string>
 #include <string_view>
 #include <vector>
@@ -60,6 +61,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <io.h>
+#include <mswsock.h>
 #include <process.h>
 #include <stdint.h>
 #include <sys/stat.h>
@@ -184,11 +186,15 @@
 int network_connect(const std::string& host, int port, int type, int timeout,
                     std::string* error);
 
+std::optional<ssize_t> network_peek(borrowed_fd fd);
+
 extern int adb_socket_accept(borrowed_fd serverfd, struct sockaddr* addr, socklen_t* addrlen);
 
 #undef   accept
 #define  accept  ___xxx_accept
 
+int adb_getsockname(borrowed_fd fd, struct sockaddr* sockaddr, socklen_t* addrlen);
+
 // Returns the local port number of a bound socket, or -1 on failure.
 int adb_socket_get_local_port(borrowed_fd fd);
 
@@ -198,8 +204,34 @@
 #undef   setsockopt
 #define  setsockopt  ___xxx_setsockopt
 
+// Wrapper around socket() call. On Windows, ADB has an indirection layer for file descriptors.
+extern int adb_socket(int domain, int type, int protocol);
+
+// Wrapper around bind() call, as Windows has indirection layer.
+extern int adb_bind(borrowed_fd fd, const sockaddr* addr, int namelen);
+
 extern int adb_socketpair(int sv[2]);
 
+// Posix compatibility layer for msghdr
+struct adb_msghdr {
+    void* msg_name;
+    socklen_t msg_namelen;
+    struct adb_iovec* msg_iov;
+    size_t msg_iovlen;
+    void* msg_control;
+    size_t msg_controllen;
+    int msg_flags;
+};
+
+ssize_t adb_sendmsg(borrowed_fd fd, const adb_msghdr* msg, int flags);
+ssize_t adb_recvmsg(borrowed_fd fd, adb_msghdr* msg, int flags);
+
+using adb_cmsghdr = WSACMSGHDR;
+
+extern adb_cmsghdr* adb_CMSG_FIRSTHDR(adb_msghdr* msgh);
+extern adb_cmsghdr* adb_CMSG_NXTHDR(adb_msghdr* msgh, adb_cmsghdr* cmsg);
+extern unsigned char* adb_CMSG_DATA(adb_cmsghdr* cmsg);
+
 struct adb_pollfd {
     int fd;
     short events;
@@ -576,6 +608,11 @@
 
 int network_connect(const std::string& host, int port, int type, int timeout, std::string* error);
 
+inline std::optional<ssize_t> network_peek(borrowed_fd fd) {
+    ssize_t ret = recv(fd.get(), nullptr, 0, MSG_PEEK | MSG_TRUNC);
+    return ret == -1 ? std::nullopt : std::make_optional(ret);
+}
+
 static inline int adb_socket_accept(borrowed_fd serverfd, struct sockaddr* addr,
                                     socklen_t* addrlen) {
     int fd;
@@ -589,6 +626,10 @@
 #undef accept
 #define accept ___xxx_accept
 
+inline int adb_getsockname(borrowed_fd fd, struct sockaddr* sockaddr, socklen_t* addrlen) {
+    return getsockname(fd.get(), sockaddr, addrlen);
+}
+
 inline int adb_socket_get_local_port(borrowed_fd fd) {
     return socket_get_local_port(fd.get());
 }
@@ -627,6 +668,14 @@
 #undef setsockopt
 #define setsockopt ___xxx_setsockopt
 
+static inline int adb_socket(int domain, int type, int protocol) {
+    return socket(domain, type, protocol);
+}
+
+static inline int adb_bind(borrowed_fd fd, const sockaddr* addr, int namelen) {
+    return bind(fd.get(), addr, namelen);
+}
+
 static inline int unix_socketpair(int d, int type, int protocol, int sv[2]) {
     return socketpair(d, type, protocol, sv);
 }
@@ -645,6 +694,29 @@
 #undef socketpair
 #define socketpair ___xxx_socketpair
 
+typedef struct msghdr adb_msghdr;
+inline ssize_t adb_sendmsg(borrowed_fd fd, const adb_msghdr* msg, int flags) {
+    return sendmsg(fd.get(), msg, flags);
+}
+
+inline ssize_t adb_recvmsg(borrowed_fd fd, adb_msghdr* msg, int flags) {
+    return recvmsg(fd.get(), msg, flags);
+}
+
+using adb_cmsghdr = cmsghdr;
+
+inline adb_cmsghdr* adb_CMSG_FIRSTHDR(adb_msghdr* msgh) {
+    return CMSG_FIRSTHDR(msgh);
+}
+
+inline adb_cmsghdr* adb_CMSG_NXTHDR(adb_msghdr* msgh, adb_cmsghdr* cmsg) {
+    return CMSG_NXTHDR(msgh, cmsg);
+}
+
+inline unsigned char* adb_CMSG_DATA(adb_cmsghdr* cmsg) {
+    return CMSG_DATA(cmsg);
+}
+
 typedef struct pollfd adb_pollfd;
 static inline int adb_poll(adb_pollfd* fds, size_t nfds, int timeout) {
     return TEMP_FAILURE_RETRY(poll(fds, nfds, timeout));
diff --git a/sysdeps_win32.cpp b/sysdeps_win32.cpp
index 217a6b7..a43a3fc 100644
--- a/sysdeps_win32.cpp
+++ b/sysdeps_win32.cpp
@@ -802,6 +802,200 @@
     }
 }
 
+int adb_socket(int domain, int type, int protocol) {
+    SOCKET s;
+
+    unique_fh f(_fh_alloc(&_fh_socket_class));
+    if (!f) {
+        return -1;
+    }
+
+    s = socket(domain, type, GetSocketProtocolFromSocketType(type));
+    if (s == INVALID_SOCKET) {
+        const DWORD err = WSAGetLastError();
+        const auto error = android::base::StringPrintf(
+                "cannot create socket: %s", android::base::SystemErrorCodeToString(err).c_str());
+        D("%s", error.c_str());
+        _socket_set_errno(err);
+        return -1;
+    }
+    f->fh_socket = s;
+
+    const int fd = _fh_to_int(f.get());
+    f.release();
+    return fd;
+}
+
+int adb_bind(borrowed_fd fd, const sockaddr* addr, socklen_t addrlen) {
+    FH fh = _fh_from_int(fd, __func__);
+
+    if (!fh || fh->clazz != &_fh_socket_class) {
+        D("adb_bind: invalid fd %d", fd.get());
+        errno = EBADF;
+        return -1;
+    }
+
+    if (bind(fh->fh_socket, addr, addrlen) == SOCKET_ERROR) {
+        const DWORD err = WSAGetLastError();
+        LOG(ERROR) << "adb_bind: bind on fd " << fd.get()
+                   << " failed: " + android::base::SystemErrorCodeToString(err);
+        _socket_set_errno(err);
+        return -1;
+    }
+
+    return 0;
+}
+
+static void to_WSAMSG(const struct adb_msghdr* msg, WSAMSG* wmsg) {
+    WSABUF* msgbuf = reinterpret_cast<WSABUF*>(msg->msg_iov);
+
+    memset(wmsg, 0, sizeof(decltype(*wmsg)));
+    wmsg->name = (struct sockaddr*)msg->msg_name;
+    char ipaddr[1024];
+    switch (wmsg->name->sa_family) {
+        case AF_INET: {
+            auto* sin = reinterpret_cast<struct sockaddr_in*>(wmsg->name);
+            inet_ntop(sin->sin_family, &sin->sin_addr, ipaddr, 1024);
+            break;
+        }
+        case AF_INET6: {
+            auto* sin = reinterpret_cast<struct sockaddr_in6*>(wmsg->name);
+            inet_ntop(sin->sin6_family, &sin->sin6_addr, ipaddr, 1024);
+            break;
+        }
+        default:
+            // Address may be unset when receiving messages, which is fine.
+            break;
+    }
+    wmsg->namelen = msg->msg_namelen;
+    wmsg->lpBuffers = msgbuf;
+    wmsg->dwBufferCount = msg->msg_iovlen;
+    wmsg->Control.len = msg->msg_controllen;
+    wmsg->Control.buf = (char*)msg->msg_control;
+    wmsg->dwFlags = msg->msg_flags;
+}
+
+ssize_t adb_sendmsg(borrowed_fd fd, const struct adb_msghdr* msg, int flags) {
+    FH fh = _fh_from_int(fd, __func__);
+
+    if (!fh || fh->clazz != &_fh_socket_class) {
+        D("adb_sendmsg: invalid fd %d", fd.get());
+        errno = EBADF;
+        return -1;
+    }
+
+    WSAMSG wmsg;
+    to_WSAMSG(msg, &wmsg);
+
+    DWORD num_bytes = 0;
+
+    // TODO: WSASendMsg doesn't work when setting the source address to INADDR_ANY. Posix sendmsg()
+    // works though. Need to figure out what to do when we get a wildcard address.
+    auto ret = WSASendMsg(fh->fh_socket, &wmsg, 0, &num_bytes, NULL, NULL);
+    if (ret == SOCKET_ERROR) {
+        const DWORD err = WSAGetLastError();
+        LOG(ERROR) << "WSASendMsg() failed " << android::base::SystemErrorCodeToString(err);
+        _socket_set_errno(err);
+        return -1;
+    }
+
+    return num_bytes;
+}
+
+// WSARecvMsg() function pointer must be obtained at runtime.
+static LPFN_WSARECVMSG GetWSARecvMsgFunc(borrowed_fd fd) {
+    FH fh = _fh_from_int(fd, __func__);
+
+    if (!fh || fh->clazz != &_fh_socket_class) {
+        D("%s(%d) failed: invalid fd", __func__, fd.get());
+        errno = EBADF;
+        return nullptr;
+    }
+
+    LPFN_WSARECVMSG func = nullptr;
+    GUID guid = WSAID_WSARECVMSG;
+    DWORD bytes_returned = 0;
+
+    if (WSAIoctl(fh->fh_socket, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), &func,
+                 sizeof(func), &bytes_returned, nullptr, nullptr) != 0) {
+        const DWORD err = WSAGetLastError();
+        D("%s(%d) failed: %s", __func__, fd.get(),
+          android::base::SystemErrorCodeToString(err).c_str());
+        _socket_set_errno(err);
+        return nullptr;
+    }
+
+    return func;
+}
+
+ssize_t adb_recvmsg(borrowed_fd fd, struct adb_msghdr* msg, int flags) {
+    FH fh = _fh_from_int(fd, __func__);
+
+    if (!fh || fh->clazz != &_fh_socket_class) {
+        D("adb_recvmsg: invalid fd %d", fd.get());
+        errno = EBADF;
+        return -1;
+    }
+
+    auto WSARecvMsgFunc = GetWSARecvMsgFunc(fd);
+    if (!WSARecvMsgFunc) {
+        errno = ENOSYS;
+        return -1;
+    }
+
+    WSAMSG wmsg;
+    to_WSAMSG(msg, &wmsg);
+
+    DWORD num_bytes = 0;
+    CHECK_EQ(wmsg.dwBufferCount, 1U);
+    char* orig = wmsg.lpBuffers[0].buf;
+    auto orig_len = wmsg.lpBuffers[0].len;
+    auto bytes_remaining = orig_len;
+    auto orig_flags = wmsg.dwFlags;
+    while (bytes_remaining > 0) {
+        const auto ret = WSARecvMsgFunc(fh->fh_socket, &wmsg, &num_bytes, NULL, NULL);
+        if (ret == SOCKET_ERROR) {
+            const DWORD err = WSAGetLastError();
+            LOG(ERROR) << "WSARecvMsg() failed " << android::base::SystemErrorCodeToString(err);
+            _socket_set_errno(err);
+            return -1;
+        }
+
+        bytes_remaining -= num_bytes;
+
+        if (bytes_remaining > 0) {
+            wmsg.lpBuffers[0].buf = orig + (orig_len - bytes_remaining);
+            wmsg.lpBuffers[0].len = bytes_remaining;
+            // WSARecvMsg will change dwFlags, which will make subsequent calls to WSARecvMsg fail
+            // with invalid operation error.
+            wmsg.dwFlags = orig_flags;
+        }
+    }
+
+    wmsg.lpBuffers[0].buf = orig;
+    wmsg.lpBuffers[0].len = orig_len;
+
+    return orig_len;
+}
+
+adb_cmsghdr* adb_CMSG_FIRSTHDR(adb_msghdr* msgh) {
+    WSAMSG wmsg;
+    to_WSAMSG(msgh, &wmsg);
+
+    return WSA_CMSG_FIRSTHDR(&wmsg);
+}
+
+adb_cmsghdr* adb_CMSG_NXTHDR(adb_msghdr* msgh, adb_cmsghdr* cmsg) {
+    WSAMSG wmsg;
+    to_WSAMSG(msgh, &wmsg);
+
+    return WSA_CMSG_NXTHDR(&wmsg, cmsg);
+}
+
+unsigned char* adb_CMSG_DATA(adb_cmsghdr* cmsg) {
+    return WSA_CMSG_DATA(cmsg);
+}
+
 int network_loopback_client(int port, int type, std::string* error) {
     struct sockaddr_in addr;
     SOCKET s;
@@ -1004,6 +1198,26 @@
     return fd;
 }
 
+std::optional<ssize_t> network_peek(borrowed_fd fd) {
+    FH fh = _fh_from_int(fd, __func__);
+
+    if (!fh || fh->clazz != &_fh_socket_class) {
+        D("network_peek: invalid fd %d", fd.get());
+        errno = EBADF;
+        return std::nullopt;
+    }
+
+    unsigned long sz_bytes = -1;
+    if (ioctlsocket(fh->fh_socket, FIONREAD, &sz_bytes) != 0) {
+        const DWORD err = WSAGetLastError();
+        LOG(ERROR) << "ioctlsocket() failed " << android::base::SystemErrorCodeToString(err);
+        _socket_set_errno(err);
+        return std::nullopt;
+    }
+
+    return sz_bytes;
+}
+
 int adb_register_socket(SOCKET s) {
     FH f = _fh_alloc(&_fh_socket_class);
     f->fh_socket = s;
@@ -1068,7 +1282,7 @@
     return result;
 }
 
-static int adb_getsockname(borrowed_fd fd, struct sockaddr* sockaddr, socklen_t* optlen) {
+int adb_getsockname(borrowed_fd fd, struct sockaddr* sockaddr, socklen_t* optlen) {
     FH fh = _fh_from_int(fd, __func__);
 
     if (!fh || fh->clazz != &_fh_socket_class) {
diff --git a/transport.cpp b/transport.cpp
index 93b4618..e2e0498 100644
--- a/transport.cpp
+++ b/transport.cpp
@@ -87,6 +87,8 @@
 const char* const kFeatureSendRecv2LZ4 = "sendrecv_v2_lz4";
 const char* const kFeatureSendRecv2Zstd = "sendrecv_v2_zstd";
 const char* const kFeatureSendRecv2DryRunSend = "sendrecv_v2_dry_run_send";
+// TODO(joshuaduong): Bump to v2 when openscreen discovery is enabled by default
+const char* const kFeatureOpenscreenMdns = "openscreen_mdns";
 
 namespace {
 
@@ -1192,6 +1194,7 @@
                 kFeatureSendRecv2LZ4,
                 kFeatureSendRecv2Zstd,
                 kFeatureSendRecv2DryRunSend,
+                kFeatureOpenscreenMdns,
                 // Increment ADB_SERVER_VERSION when adding a feature that adbd needs
                 // to know about. Otherwise, the client can be stuck running an old
                 // version of the server even after upgrading their copy of adb.