Merge changes from topic "adb-mdns"

* changes:
  adb pair: allow passing password as command-line argument.
  Change adb auto-connect to use mdns instance name.
  'adb pair' by mdns instance name.
  'adb connect' by mDNS service name.
  Add mDNS service instance name parser.
diff --git a/adb/Android.bp b/adb/Android.bp
index 432770c..9db151d 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -218,6 +218,7 @@
         "client/usb_dispatch.cpp",
         "client/transport_local.cpp",
         "client/transport_mdns.cpp",
+        "client/mdns_utils.cpp",
         "client/transport_usb.cpp",
         "client/pairing/pairing_client.cpp",
     ],
@@ -264,7 +265,10 @@
 cc_test_host {
     name: "adb_test",
     defaults: ["adb_defaults"],
-    srcs: libadb_test_srcs,
+    srcs: libadb_test_srcs + [
+        "client/mdns_utils_test.cpp",
+    ],
+
     static_libs: [
         "libadb_crypto_static",
         "libadb_host",
diff --git a/adb/adb_wifi.h b/adb/adb_wifi.h
index 3a6b0b1..42f414b 100644
--- a/adb/adb_wifi.h
+++ b/adb/adb_wifi.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <optional>
 #include <string>
 
 #include "adb.h"
@@ -30,6 +31,19 @@
 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/adb/client/adb_wifi.cpp b/adb/client/adb_wifi.cpp
index fa71028..61a9a48 100644
--- a/adb/client/adb_wifi.cpp
+++ b/adb/client/adb_wifi.cpp
@@ -179,17 +179,21 @@
 
 void adb_wifi_pair_device(const std::string& host, const std::string& password,
                           std::string& response) {
-    // Check the address for a valid address and port.
-    std::string parsed_host;
-    std::string err;
-    int port = -1;
-    if (!android::base::ParseNetAddress(host, &parsed_host, &port, nullptr, &err)) {
-        response = "Failed to parse address for pairing: " + err;
-        return;
-    }
-    if (port <= 0 || port > 65535) {
-        response = "Invalid port while parsing address [" + host + "]";
-        return;
+    auto mdns_info = mdns_get_pairing_service_info(host);
+
+    if (!mdns_info.has_value()) {
+        // Check the address for a valid address and port.
+        std::string parsed_host;
+        std::string err;
+        int port = -1;
+        if (!android::base::ParseNetAddress(host, &parsed_host, &port, nullptr, &err)) {
+            response = "Failed to parse address for pairing: " + err;
+            return;
+        }
+        if (port <= 0 || port > 65535) {
+            response = "Invalid port while parsing address [" + host + "]";
+            return;
+        }
     }
 
     auto priv_key = adb_auth_get_user_privkey();
@@ -220,7 +224,11 @@
 
     PairingResultWaiter waiter;
     std::unique_lock<std::mutex> lock(waiter.mutex_);
-    if (!client->Start(host, waiter.OnResult, &waiter)) {
+    if (!client->Start(mdns_info.has_value()
+                               ? android::base::StringPrintf("%s:%d", mdns_info->addr.c_str(),
+                                                             mdns_info->port)
+                               : host,
+                       waiter.OnResult, &waiter)) {
         response = "Failed: Unable to start pairing client.";
         return;
     }
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index efb6c2f..eaa32e5 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -104,7 +104,8 @@
         " connect HOST[:PORT]      connect to a device via TCP/IP [default port=5555]\n"
         " disconnect [HOST[:PORT]]\n"
         "     disconnect from given TCP/IP device [default port=5555], or all\n"
-        " pair HOST[:PORT]         pair with a device for secure TCP/IP communication\n"
+        " pair HOST[:PORT] [PAIRING CODE]\n"
+        "     pair with a device for secure TCP/IP communication\n"
         " forward --list           list all forward socket connections\n"
         " forward [--no-rebind] LOCAL REMOTE\n"
         "     forward socket connection using:\n"
@@ -1735,13 +1736,17 @@
     } else if (!strcmp(argv[0], "abb")) {
         return adb_abb(argc, argv);
     } else if (!strcmp(argv[0], "pair")) {
-        if (argc != 2) error_exit("usage: adb pair <host>[:<port>]");
+        if (argc < 2 || argc > 3) error_exit("usage: adb pair HOST[:PORT] [PAIRING CODE]");
 
         std::string password;
-        printf("Enter pairing code: ");
-        fflush(stdout);
-        if (!std::getline(std::cin, password) || password.empty()) {
-            error_exit("No pairing code provided");
+        if (argc == 2) {
+            printf("Enter pairing code: ");
+            fflush(stdout);
+            if (!std::getline(std::cin, password) || password.empty()) {
+                error_exit("No pairing code provided");
+            }
+        } else {
+            password = argv[2];
         }
         std::string query =
                 android::base::StringPrintf("host:pair:%s:%s", password.c_str(), argv[1]);
diff --git a/adb/client/mdns_utils.cpp b/adb/client/mdns_utils.cpp
new file mode 100644
index 0000000..8666b18
--- /dev/null
+++ b/adb/client/mdns_utils.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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/mdns_utils.h"
+
+#include <android-base/strings.h>
+
+namespace mdns {
+
+// <Instance>.<Service>.<Domain>
+std::optional<MdnsInstance> mdns_parse_instance_name(std::string_view name) {
+    CHECK(!name.empty());
+
+    // Return the whole name if it doesn't fall under <Instance>.<Service>.<Domain> or
+    // <Instance>.<Service>
+    bool has_local_suffix = false;
+    // Strip the local suffix, if any
+    {
+        std::string local_suffix = ".local";
+        local_suffix += android::base::EndsWith(name, ".") ? "." : "";
+
+        if (android::base::ConsumeSuffix(&name, local_suffix)) {
+            if (name.empty()) {
+                return std::nullopt;
+            }
+            has_local_suffix = true;
+        }
+    }
+
+    std::string transport;
+    // Strip the transport suffix, if any
+    {
+        std::string add_dot = (!has_local_suffix && android::base::EndsWith(name, ".")) ? "." : "";
+        std::array<std::string, 2> transport_suffixes{"._tcp", "._udp"};
+
+        for (const auto& t : transport_suffixes) {
+            if (android::base::ConsumeSuffix(&name, t + add_dot)) {
+                if (name.empty()) {
+                    return std::nullopt;
+                }
+                transport = t.substr(1);
+                break;
+            }
+        }
+
+        if (has_local_suffix && transport.empty()) {
+            return std::nullopt;
+        }
+    }
+
+    if (!has_local_suffix && transport.empty()) {
+        return std::make_optional<MdnsInstance>(name, "", "");
+    }
+
+    // Split the service name from the instance name
+    auto pos = name.rfind(".");
+    if (pos == 0 || pos == std::string::npos || pos == name.size() - 1) {
+        return std::nullopt;
+    }
+
+    return std::make_optional<MdnsInstance>(name.substr(0, pos), name.substr(pos + 1), transport);
+}
+
+}  // namespace mdns
diff --git a/adb/client/mdns_utils.h b/adb/client/mdns_utils.h
new file mode 100644
index 0000000..40d095d
--- /dev/null
+++ b/adb/client/mdns_utils.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <optional>
+#include <string_view>
+
+#include "adb_wifi.h"
+
+namespace mdns {
+
+struct MdnsInstance {
+    std::string instance_name;   // "my name"
+    std::string service_name;    // "_adb-tls-connect"
+    std::string transport_type;  // either "_tcp" or "_udp"
+
+    MdnsInstance(std::string_view inst, std::string_view serv, std::string_view trans)
+        : instance_name(inst), service_name(serv), transport_type(trans) {}
+};
+
+// This parser is based on https://tools.ietf.org/html/rfc6763#section-4.1 for
+// structured service instance names, where the whole name is in the format
+// <Instance>.<Service>.<Domain>.
+//
+// In our case, we ignore <Domain> portion of the name, which
+// we always assume to be ".local", or link-local mDNS.
+//
+// The string can be in one of the following forms:
+//   - <Instance>.<Service>.<Domain>.?
+//     - e.g. "instance._service._tcp.local" (or "...local.")
+//   - <Instance>.<Service>.? (must contain either "_tcp" or "_udp" at the end)
+//     - e.g. "instance._service._tcp" (or "..._tcp.)
+//   - <Instance> (can contain dots '.')
+//     - e.g. "myname", "name.", "my.name."
+//
+// Returns an MdnsInstance with the appropriate fields filled in (instance name is never empty),
+// otherwise returns std::nullopt.
+std::optional<MdnsInstance> mdns_parse_instance_name(std::string_view name);
+
+}  // namespace mdns
diff --git a/adb/client/mdns_utils_test.cpp b/adb/client/mdns_utils_test.cpp
new file mode 100644
index 0000000..ec71529
--- /dev/null
+++ b/adb/client/mdns_utils_test.cpp
@@ -0,0 +1,173 @@
+/*
+ * 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/mdns_utils.h"
+
+#include <gtest/gtest.h>
+
+namespace mdns {
+
+TEST(mdns_utils, mdns_parse_instance_name) {
+    // Just the instance name
+    {
+        std::string str = ".";
+        auto res = mdns_parse_instance_name(str);
+        ASSERT_TRUE(res.has_value());
+        EXPECT_EQ(str, res->instance_name);
+        EXPECT_TRUE(res->service_name.empty());
+        EXPECT_TRUE(res->transport_type.empty());
+    }
+    {
+        std::string str = "my.name";
+        auto res = mdns_parse_instance_name(str);
+        ASSERT_TRUE(res.has_value());
+        EXPECT_EQ(str, res->instance_name);
+        EXPECT_TRUE(res->service_name.empty());
+        EXPECT_TRUE(res->transport_type.empty());
+    }
+    {
+        std::string str = "my.name.";
+        auto res = mdns_parse_instance_name(str);
+        ASSERT_TRUE(res.has_value());
+        EXPECT_EQ(str, res->instance_name);
+        EXPECT_TRUE(res->service_name.empty());
+        EXPECT_TRUE(res->transport_type.empty());
+    }
+
+    // With "_tcp", "_udp" transport type
+    for (const std::string_view transport : {"._tcp", "._udp"}) {
+        {
+            std::string str = android::base::StringPrintf("%s", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            EXPECT_FALSE(res.has_value());
+        }
+        {
+            std::string str = android::base::StringPrintf("%s.", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            EXPECT_FALSE(res.has_value());
+        }
+        {
+            std::string str = android::base::StringPrintf("service%s", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            EXPECT_FALSE(res.has_value());
+        }
+        {
+            std::string str = android::base::StringPrintf(".service%s", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            EXPECT_FALSE(res.has_value());
+        }
+        {
+            std::string str = android::base::StringPrintf("service.%s", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            EXPECT_FALSE(res.has_value());
+        }
+        {
+            std::string str = android::base::StringPrintf("my.service%s", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            ASSERT_TRUE(res.has_value());
+            EXPECT_EQ(res->instance_name, "my");
+            EXPECT_EQ(res->service_name, "service");
+            EXPECT_EQ(res->transport_type, transport.substr(1));
+        }
+        {
+            std::string str = android::base::StringPrintf("my.service%s.", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            ASSERT_TRUE(res.has_value());
+            EXPECT_EQ(res->instance_name, "my");
+            EXPECT_EQ(res->service_name, "service");
+            EXPECT_EQ(res->transport_type, transport.substr(1));
+        }
+        {
+            std::string str = android::base::StringPrintf("my..service%s", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            ASSERT_TRUE(res.has_value());
+            EXPECT_EQ(res->instance_name, "my.");
+            EXPECT_EQ(res->service_name, "service");
+            EXPECT_EQ(res->transport_type, transport.substr(1));
+        }
+        {
+            std::string str = android::base::StringPrintf("my.name.service%s.", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            ASSERT_TRUE(res.has_value());
+            EXPECT_EQ(res->instance_name, "my.name");
+            EXPECT_EQ(res->service_name, "service");
+            EXPECT_EQ(res->transport_type, transport.substr(1));
+        }
+        {
+            std::string str = android::base::StringPrintf("name.service.%s.", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            EXPECT_FALSE(res.has_value());
+        }
+
+        // With ".local" domain
+        {
+            std::string str = ".local";
+            auto res = mdns_parse_instance_name(str);
+            EXPECT_FALSE(res.has_value());
+        }
+        {
+            std::string str = ".local.";
+            auto res = mdns_parse_instance_name(str);
+            EXPECT_FALSE(res.has_value());
+        }
+        {
+            std::string str = "name.local";
+            auto res = mdns_parse_instance_name(str);
+            EXPECT_FALSE(res.has_value());
+        }
+        {
+            std::string str = android::base::StringPrintf("%s.local", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            EXPECT_FALSE(res.has_value());
+        }
+        {
+            std::string str = android::base::StringPrintf("service%s.local", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            EXPECT_FALSE(res.has_value());
+        }
+        {
+            std::string str = android::base::StringPrintf("name.service%s.local", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            ASSERT_TRUE(res.has_value());
+            EXPECT_EQ(res->instance_name, "name");
+            EXPECT_EQ(res->service_name, "service");
+            EXPECT_EQ(res->transport_type, transport.substr(1));
+        }
+        {
+            std::string str =
+                    android::base::StringPrintf("name.service%s.local.", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            ASSERT_TRUE(res.has_value());
+            EXPECT_EQ(res->instance_name, "name");
+            EXPECT_EQ(res->service_name, "service");
+            EXPECT_EQ(res->transport_type, transport.substr(1));
+        }
+        {
+            std::string str =
+                    android::base::StringPrintf("name.service%s..local.", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            EXPECT_FALSE(res.has_value());
+        }
+        {
+            std::string str =
+                    android::base::StringPrintf("name.service.%s.local.", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            EXPECT_FALSE(res.has_value());
+        }
+    }
+}
+
+}  // namespace mdns
diff --git a/adb/client/transport_mdns.cpp b/adb/client/transport_mdns.cpp
index c9993b7..9db2453 100644
--- a/adb/client/transport_mdns.cpp
+++ b/adb/client/transport_mdns.cpp
@@ -38,6 +38,7 @@
 #include "adb_trace.h"
 #include "adb_utils.h"
 #include "adb_wifi.h"
+#include "client/mdns_utils.h"
 #include "fdevent/fdevent.h"
 #include "sysdeps.h"
 
@@ -221,7 +222,7 @@
         }
 
         std::string response;
-        connect_device(android::base::StringPrintf(addr_format_.c_str(), ip_addr_, port_),
+        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());
@@ -248,26 +249,8 @@
             return false;
         }
 
-        // adb secure service needs to do something different from just
-        // connecting here.
-        if (adb_DNSServiceShouldAutoConnect(regType_.c_str(), serviceName_.c_str())) {
-            std::string response;
-            D("Attempting to serviceName=[%s], regtype=[%s] ipaddr=(%s:%hu)", serviceName_.c_str(),
-              regType_.c_str(), ip_addr_, port_);
-            int index = adb_DNSServiceIndexByName(regType_.c_str());
-            if (index == kADBSecureConnectServiceRefIndex) {
-                ConnectSecureWifiDevice();
-            } else {
-                connect_device(android::base::StringPrintf(addr_format_.c_str(), ip_addr_, port_),
-                               &response);
-                D("Connect to %s regtype %s (%s:%hu) : %s", serviceName_.c_str(), regType_.c_str(),
-                  ip_addr_, port_, response.c_str());
-            }
-        } else {
-            D("Not immediately connecting to serviceName=[%s], regtype=[%s] ipaddr=(%s:%hu)",
-              serviceName_.c_str(), regType_.c_str(), ip_addr_, port_);
-        }
-
+        // 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) {
@@ -294,6 +277,25 @@
         }
         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;
     }
 
@@ -319,7 +321,7 @@
 
     static void initAdbServiceRegistries();
 
-    static void forEachService(const ServiceRegistry& services, const std::string& hostname,
+    static void forEachService(const ServiceRegistry& services, std::string_view hostname,
                                adb_secure_foreach_service_callback cb);
 
     static bool connectByServiceName(const ServiceRegistry& services,
@@ -362,7 +364,7 @@
 
 // static
 void ResolvedService::forEachService(const ServiceRegistry& services,
-                                     const std::string& wanted_service_name,
+                                     std::string_view wanted_service_name,
                                      adb_secure_foreach_service_callback cb) {
     initAdbServiceRegistries();
 
@@ -372,7 +374,7 @@
         auto ip = service->ipAddress();
         auto port = service->port();
 
-        if (wanted_service_name == "") {
+        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);
@@ -396,14 +398,12 @@
 
 void adb_secure_foreach_pairing_service(const char* service_name,
                                         adb_secure_foreach_service_callback cb) {
-    ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices,
-                                    service_name ? service_name : "", cb);
+    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 ? service_name : "", cb);
+    ResolvedService::forEachService(*ResolvedService::sAdbSecureConnectServices, service_name, cb);
 }
 
 bool adb_secure_connect_by_service_name(const char* service_name) {
@@ -676,3 +676,79 @@
     ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices, "", cb);
     return result;
 }
+
+std::optional<MdnsInfo> mdns_get_connect_service_info(std::string_view name) {
+    CHECK(!name.empty());
+
+    auto mdns_instance = mdns::mdns_parse_instance_name(name);
+    if (!mdns_instance.has_value()) {
+        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); };
+
+    std::string reg_type;
+    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) {
+            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.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;
+        }
+    }
+
+    return std::nullopt;
+}
+
+std::optional<MdnsInfo> mdns_get_pairing_service_info(std::string_view 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.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); };
+
+    // 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) {
+            case kADBSecurePairingServiceRefIndex:
+                break;
+            default:
+                D("Not an adb pairing reg_type [%s]", reg_type.data());
+                return std::nullopt;
+        }
+    }
+
+    ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices, name, cb);
+    return info;
+}
diff --git a/adb/socket_spec.cpp b/adb/socket_spec.cpp
index b7b25fa..5cad70d 100644
--- a/adb/socket_spec.cpp
+++ b/adb/socket_spec.cpp
@@ -30,6 +30,7 @@
 
 #include "adb.h"
 #include "adb_utils.h"
+#include "adb_wifi.h"
 #include "sysdeps.h"
 
 using namespace std::string_literals;
@@ -195,7 +196,24 @@
             fd->reset(network_loopback_client(port_value, SOCK_STREAM, error));
         } else {
 #if ADB_HOST
-            fd->reset(network_connect(hostname, port_value, SOCK_STREAM, 0, error));
+            // Check if the address is an mdns service we can connect to.
+            if (auto mdns_info = mdns_get_connect_service_info(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) {
+                    // TODO(joshuaduong): We still show the ip address for the serial. Change it to
+                    // use the mdns instance name, so we can adjust to address changes on
+                    // reconnects.
+                    port_value = mdns_info->port;
+                    if (serial) {
+                        *serial = android::base::StringPrintf("%s.%s",
+                                                              mdns_info->service_name.c_str(),
+                                                              mdns_info->service_type.c_str());
+                    }
+                }
+            } else {
+                fd->reset(network_connect(hostname, port_value, SOCK_STREAM, 0, error));
+            }
 #else
             // Disallow arbitrary connections in adbd.
             *error = "adbd does not support arbitrary tcp connections";
diff --git a/adb/test_adb.py b/adb/test_adb.py
index 9912f11..4b99411 100755
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -25,6 +25,7 @@
 import random
 import select
 import socket
+import string
 import struct
 import subprocess
 import sys
@@ -628,21 +629,49 @@
 class MdnsTest(unittest.TestCase):
     """Tests for adb mdns."""
 
+    @staticmethod
+    def _mdns_services(port):
+        output = subprocess.check_output(["adb", "-P", str(port), "mdns", "services"])
+        return [x.split("\t") for x in output.decode("utf8").strip().splitlines()[1:]]
+
+    @staticmethod
+    def _devices(port):
+        output = subprocess.check_output(["adb", "-P", str(port), "devices"])
+        return [x.split("\t") for x in output.decode("utf8").strip().splitlines()[1:]]
+
+    @contextlib.contextmanager
+    def _adb_mdns_connect(self, server_port, mdns_instance, serial, should_connect):
+        """Context manager for an ADB connection.
+
+        This automatically disconnects when done with the connection.
+        """
+
+        output = subprocess.check_output(["adb", "-P", str(server_port), "connect", mdns_instance])
+        if should_connect:
+            self.assertEqual(output.strip(), "connected to {}".format(serial).encode("utf8"))
+        else:
+            self.assertTrue(output.startswith("failed to resolve host: '{}'"
+                .format(mdns_instance).encode("utf8")))
+
+        try:
+            yield
+        finally:
+            # Perform best-effort disconnection. Discard the output.
+            subprocess.Popen(["adb", "disconnect", serial],
+                             stdout=subprocess.PIPE,
+                             stderr=subprocess.PIPE).communicate()
+
+
     @unittest.skipIf(not is_zeroconf_installed(), "zeroconf library not installed")
     def test_mdns_services_register_unregister(self):
         """Ensure that `adb mdns services` correctly adds and removes a service
         """
         from zeroconf import IPVersion, ServiceInfo
- 
-        def _mdns_services(port):
-            output = subprocess.check_output(["adb", "-P", str(port), "mdns", "services"])
-            return [x.split("\t") for x in output.decode("utf8").strip().splitlines()[1:]]
 
         with adb_server() as server_port:
             output = subprocess.check_output(["adb", "-P", str(server_port),
                                               "mdns", "services"]).strip()
             self.assertTrue(output.startswith(b"List of discovered mdns services"))
-            print(f"services={_mdns_services(server_port)}")
 
             """TODO(joshuaduong): Add ipv6 tests once we have it working in adb"""
             """Register/Unregister a service"""
@@ -656,20 +685,52 @@
                         name=serv_instance + "." + serv_type + "local.",
                         addresses=[serv_ipaddr],
                         port=serv_port)
-                print(f"Registering {serv_instance}.{serv_type} ...")
                 with zeroconf_register_service(zc, service_info) as info:
                     """Give adb some time to register the service"""
                     time.sleep(1)
-                    print(f"services={_mdns_services(server_port)}")
                     self.assertTrue(any((serv_instance in line and serv_type in line)
-                        for line in _mdns_services(server_port)))
+                        for line in MdnsTest._mdns_services(server_port)))
 
                 """Give adb some time to unregister the service"""
-                print("Unregistering mdns service...")
                 time.sleep(1)
-                print(f"services={_mdns_services(server_port)}")
                 self.assertFalse(any((serv_instance in line and serv_type in line)
-                    for line in _mdns_services(server_port)))
+                    for line in MdnsTest._mdns_services(server_port)))
+
+    @unittest.skipIf(not is_zeroconf_installed(), "zeroconf library not installed")
+    def test_mdns_connect(self):
+        """Ensure that `adb connect` by mdns instance name works (for non-pairing services)
+        """
+        from zeroconf import IPVersion, ServiceInfo
+
+        with adb_server() as server_port:
+            with zeroconf_context(IPVersion.V4Only) as zc:
+                serv_instance = "fakeadbd-" + ''.join(
+                        random.choice(string.ascii_letters) for i in range(4))
+                serv_type = "_" + self.service_name + "._tcp."
+                serv_ipaddr = socket.inet_aton("127.0.0.1")
+                should_connect = self.service_name != "adb-tls-pairing"
+                with fake_adbd() as (port, _):
+                    service_info = ServiceInfo(
+                            serv_type + "local.",
+                            name=serv_instance + "." + serv_type + "local.",
+                            addresses=[serv_ipaddr],
+                            port=port)
+                    with zeroconf_register_service(zc, service_info) as info:
+                        """Give adb some time to register the service"""
+                        time.sleep(1)
+                        self.assertTrue(any((serv_instance in line and serv_type in line)
+                            for line in MdnsTest._mdns_services(server_port)))
+                        full_name = '.'.join([serv_instance, serv_type])
+                        with self._adb_mdns_connect(server_port, serv_instance, full_name,
+                                should_connect):
+                            if should_connect:
+                                self.assertEqual(MdnsTest._devices(server_port),
+                                        [[full_name, "device"]])
+
+                    """Give adb some time to unregister the service"""
+                    time.sleep(1)
+                    self.assertFalse(any((serv_instance in line and serv_type in line)
+                        for line in MdnsTest._mdns_services(server_port)))
 
 def main():
     """Main entrypoint."""