Merge changes from topic "adb-mdns-cmdline"
* changes: [adb client] Add "mdns services" command. [adb client] Add "adb mdns check" command.
This commit is contained in:
commit
8cab9b6b24
6 changed files with 201 additions and 9 deletions
23
adb/adb.cpp
23
adb/adb.cpp
|
@ -1076,6 +1076,25 @@ void adb_set_reject_kill_server(bool value) {
|
|||
g_reject_kill_server = value;
|
||||
}
|
||||
|
||||
static bool handle_mdns_request(std::string_view service, int reply_fd) {
|
||||
if (!android::base::ConsumePrefix(&service, "mdns:")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (service == "check") {
|
||||
std::string check = mdns_check();
|
||||
SendOkay(reply_fd, check);
|
||||
return true;
|
||||
}
|
||||
if (service == "services") {
|
||||
std::string services_list = mdns_list_discovered_services();
|
||||
SendOkay(reply_fd, services_list);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
HostRequestResult handle_host_request(std::string_view service, TransportType type,
|
||||
const char* serial, TransportId transport_id, int reply_fd,
|
||||
asocket* s) {
|
||||
|
@ -1320,6 +1339,10 @@ HostRequestResult handle_host_request(std::string_view service, TransportType ty
|
|||
return HostRequestResult::Handled;
|
||||
}
|
||||
|
||||
if (handle_mdns_request(service, reply_fd)) {
|
||||
return HostRequestResult::Handled;
|
||||
}
|
||||
|
||||
return HostRequestResult::Unhandled;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,9 @@ void adb_wifi_pair_device(const std::string& host, const std::string& password,
|
|||
std::string& response);
|
||||
bool adb_wifi_is_known_host(const std::string& host);
|
||||
|
||||
std::string mdns_check();
|
||||
std::string mdns_list_discovered_services();
|
||||
|
||||
#else // !ADB_HOST
|
||||
|
||||
struct AdbdAuthContext;
|
||||
|
|
|
@ -90,8 +90,9 @@ extern const char* _Nullable * _Nullable __adb_envp;
|
|||
|
||||
// ADB Secure DNS service interface. Used to query what ADB Secure DNS services have been
|
||||
// resolved, and to run some kind of callback for each one.
|
||||
using adb_secure_foreach_service_callback = std::function<void(
|
||||
const char* _Nonnull service_name, const char* _Nonnull ip_address, uint16_t port)>;
|
||||
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
|
||||
|
|
|
@ -127,6 +127,8 @@ static void help() {
|
|||
" localfilesystem:<unix domain socket name>\n"
|
||||
" reverse --remove REMOTE remove specific reverse socket connection\n"
|
||||
" reverse --remove-all remove all reverse socket connections from device\n"
|
||||
" mdns check check if mdns discovery is available\n"
|
||||
" mdns services list all discovered services\n"
|
||||
"\n"
|
||||
"file transfer:\n"
|
||||
" push [--sync] [-z ALGORITHM] [-Z] LOCAL... REMOTE\n"
|
||||
|
@ -1910,6 +1912,29 @@ int adb_commandline(int argc, const char** argv) {
|
|||
|
||||
ReadOrderlyShutdown(fd);
|
||||
return 0;
|
||||
} else if (!strcmp(argv[0], "mdns")) {
|
||||
--argc;
|
||||
if (argc < 1) error_exit("mdns requires an argument");
|
||||
++argv;
|
||||
|
||||
std::string error;
|
||||
if (!adb_check_server_version(&error)) {
|
||||
error_exit("failed to check server version: %s", error.c_str());
|
||||
}
|
||||
|
||||
std::string query = "host:mdns:";
|
||||
if (!strcmp(argv[0], "check")) {
|
||||
if (argc != 1) error_exit("mdns %s doesn't take any arguments", argv[0]);
|
||||
query += "check";
|
||||
} else if (!strcmp(argv[0], "services")) {
|
||||
if (argc != 1) error_exit("mdns %s doesn't take any arguments", argv[0]);
|
||||
query += "services";
|
||||
printf("List of discovered mdns services\n");
|
||||
} else {
|
||||
error_exit("unknown mdns command [%s]", argv[0]);
|
||||
}
|
||||
|
||||
return adb_query_command(query);
|
||||
}
|
||||
/* do_sync_*() commands */
|
||||
else if (!strcmp(argv[0], "ls")) {
|
||||
|
|
|
@ -216,6 +216,9 @@ class ResolvedService : public AsyncServiceRef {
|
|||
|
||||
int adbSecureServiceType = serviceIndex();
|
||||
switch (adbSecureServiceType) {
|
||||
case kADBTransportServiceRefIndex:
|
||||
sAdbTransportServices->push_back(this);
|
||||
break;
|
||||
case kADBSecurePairingServiceRefIndex:
|
||||
sAdbSecurePairingServices->push_back(this);
|
||||
break;
|
||||
|
@ -233,16 +236,21 @@ class ResolvedService : public AsyncServiceRef {
|
|||
|
||||
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<ResolvedService*>;
|
||||
|
||||
// unencrypted tcp connections
|
||||
static ServiceRegistry* sAdbTransportServices;
|
||||
|
||||
static ServiceRegistry* sAdbSecurePairingServices;
|
||||
static ServiceRegistry* sAdbSecureConnectServices;
|
||||
|
||||
static void initAdbSecure();
|
||||
static void initAdbServiceRegistries();
|
||||
|
||||
static void forEachService(const ServiceRegistry& services, const std::string& hostname,
|
||||
adb_secure_foreach_service_callback cb);
|
||||
|
@ -263,6 +271,9 @@ class ResolvedService : public AsyncServiceRef {
|
|||
int serviceVersion_;
|
||||
};
|
||||
|
||||
// static
|
||||
std::vector<ResolvedService*>* ResolvedService::sAdbTransportServices = NULL;
|
||||
|
||||
// static
|
||||
std::vector<ResolvedService*>* ResolvedService::sAdbSecurePairingServices = NULL;
|
||||
|
||||
|
@ -270,7 +281,10 @@ std::vector<ResolvedService*>* ResolvedService::sAdbSecurePairingServices = NULL
|
|||
std::vector<ResolvedService*>* ResolvedService::sAdbSecureConnectServices = NULL;
|
||||
|
||||
// static
|
||||
void ResolvedService::initAdbSecure() {
|
||||
void ResolvedService::initAdbServiceRegistries() {
|
||||
if (!sAdbTransportServices) {
|
||||
sAdbTransportServices = new ServiceRegistry;
|
||||
}
|
||||
if (!sAdbSecurePairingServices) {
|
||||
sAdbSecurePairingServices = new ServiceRegistry;
|
||||
}
|
||||
|
@ -283,17 +297,18 @@ void ResolvedService::initAdbSecure() {
|
|||
void ResolvedService::forEachService(const ServiceRegistry& services,
|
||||
const std::string& wanted_service_name,
|
||||
adb_secure_foreach_service_callback cb) {
|
||||
initAdbSecure();
|
||||
initAdbServiceRegistries();
|
||||
|
||||
for (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 == "") {
|
||||
cb(service_name.c_str(), ip.c_str(), port);
|
||||
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(), ip.c_str(), port);
|
||||
cb(service_name.c_str(), reg_type.c_str(), ip.c_str(), port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -301,7 +316,7 @@ void ResolvedService::forEachService(const ServiceRegistry& services,
|
|||
// static
|
||||
bool ResolvedService::connectByServiceName(const ServiceRegistry& services,
|
||||
const std::string& service_name) {
|
||||
initAdbSecure();
|
||||
initAdbServiceRegistries();
|
||||
for (auto service : services) {
|
||||
if (service_name == service->serviceName()) {
|
||||
D("Got service_name match [%s]", service->serviceName().c_str());
|
||||
|
@ -398,6 +413,9 @@ static void adb_RemoveDNSService(const char* regType, const char* serviceName) {
|
|||
int index = adb_DNSServiceIndexByName(regType);
|
||||
ResolvedService::ServiceRegistry* services;
|
||||
switch (index) {
|
||||
case kADBTransportServiceRefIndex:
|
||||
services = ResolvedService::sAdbTransportServices;
|
||||
break;
|
||||
case kADBSecurePairingServiceRefIndex:
|
||||
services = ResolvedService::sAdbSecurePairingServices;
|
||||
break;
|
||||
|
@ -542,6 +560,34 @@ void init_mdns_transport_discovery_thread(void) {
|
|||
}
|
||||
|
||||
void init_mdns_transport_discovery(void) {
|
||||
ResolvedService::initAdbSecure();
|
||||
ResolvedService::initAdbServiceRegistries();
|
||||
std::thread(init_mdns_transport_discovery_thread).detach();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
result = android::base::StringPrintf("mdns daemon version [%u]", daemon_version);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string mdns_list_discovered_services() {
|
||||
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);
|
||||
};
|
||||
|
||||
ResolvedService::forEachService(*ResolvedService::sAdbTransportServices, "", cb);
|
||||
ResolvedService::forEachService(*ResolvedService::sAdbSecureConnectServices, "", cb);
|
||||
ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices, "", cb);
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -32,6 +32,8 @@ import threading
|
|||
import time
|
||||
import unittest
|
||||
import warnings
|
||||
from importlib import util
|
||||
from parameterized import parameterized_class
|
||||
|
||||
def find_open_port():
|
||||
# Find an open port.
|
||||
|
@ -576,6 +578,98 @@ class PowerTest(unittest.TestCase):
|
|||
# If the power event was detected, the adb shell command should be broken very quickly.
|
||||
self.assertLess(end - start, 2)
|
||||
|
||||
"""Use 'adb mdns check' to see if mdns discovery is available."""
|
||||
def is_adb_mdns_available():
|
||||
with adb_server() as server_port:
|
||||
output = subprocess.check_output(["adb", "-P", str(server_port),
|
||||
"mdns", "check"]).strip()
|
||||
return output.startswith(b"mdns daemon version")
|
||||
|
||||
"""Check if we have zeroconf python library installed"""
|
||||
def is_zeroconf_installed():
|
||||
zeroconf_spec = util.find_spec("zeroconf")
|
||||
return zeroconf_spec is not None
|
||||
|
||||
@contextlib.contextmanager
|
||||
def zeroconf_context(ipversion):
|
||||
from zeroconf import Zeroconf
|
||||
"""Context manager for a zeroconf instance
|
||||
|
||||
This creates a zeroconf instance and returns it.
|
||||
"""
|
||||
|
||||
try:
|
||||
zeroconf = Zeroconf(ip_version=ipversion)
|
||||
yield zeroconf
|
||||
finally:
|
||||
zeroconf.close()
|
||||
|
||||
@contextlib.contextmanager
|
||||
def zeroconf_register_service(zeroconf_ctx, info):
|
||||
"""Context manager for a zeroconf service
|
||||
|
||||
Registers a service and unregisters it on cleanup. Returns the ServiceInfo
|
||||
supplied.
|
||||
"""
|
||||
|
||||
try:
|
||||
zeroconf_ctx.register_service(info)
|
||||
yield info
|
||||
finally:
|
||||
zeroconf_ctx.unregister_service(info)
|
||||
|
||||
"""Should match the service names listed in adb_mdns.h"""
|
||||
@parameterized_class(('service_name',), [
|
||||
("adb",),
|
||||
("adb-tls-connect",),
|
||||
("adb-tls-pairing",),
|
||||
])
|
||||
@unittest.skipIf(not is_adb_mdns_available(), "mdns feature not available")
|
||||
class MdnsTest(unittest.TestCase):
|
||||
"""Tests for adb mdns."""
|
||||
|
||||
@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"""
|
||||
with zeroconf_context(IPVersion.V4Only) as zc:
|
||||
serv_instance = "my_fake_test_service"
|
||||
serv_type = "_" + self.service_name + "._tcp."
|
||||
serv_ipaddr = socket.inet_aton("1.2.3.4")
|
||||
serv_port = 12345
|
||||
service_info = ServiceInfo(
|
||||
serv_type + "local.",
|
||||
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(0.25)
|
||||
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)))
|
||||
|
||||
"""Give adb some time to unregister the service"""
|
||||
print("Unregistering mdns service...")
|
||||
time.sleep(0.25)
|
||||
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)))
|
||||
|
||||
def main():
|
||||
"""Main entrypoint."""
|
||||
|
|
Loading…
Reference in a new issue