Merge "adb: only submit USB writes on the worker thread."
diff --git a/Android.bp b/Android.bp
index 432770c..9db151d 100644
--- a/Android.bp
+++ b/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_wifi.h b/adb_wifi.h
index 3a6b0b1..42f414b 100644
--- a/adb_wifi.h
+++ b/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/client/adb_client.cpp b/client/adb_client.cpp
index 31c938c..a308732 100644
--- a/client/adb_client.cpp
+++ b/client/adb_client.cpp
@@ -417,17 +417,19 @@
}
const std::optional<FeatureSet>& adb_get_feature_set(std::string* error) {
- static std::string cached_error [[clang::no_destroy]];
- static const std::optional<FeatureSet> features
- [[clang::no_destroy]] ([]() -> std::optional<FeatureSet> {
- std::string result;
- if (adb_query(format_host_command("features"), &result, &cached_error)) {
- return StringToFeatureSet(result);
- }
- return std::nullopt;
- }());
- if (!features && error) {
- *error = cached_error;
+ static std::mutex feature_mutex [[clang::no_destroy]];
+ static std::optional<FeatureSet> features [[clang::no_destroy]] GUARDED_BY(feature_mutex);
+ std::lock_guard<std::mutex> lock(feature_mutex);
+ if (!features) {
+ std::string result;
+ std::string err;
+ if (adb_query(format_host_command("features"), &result, &err)) {
+ features = StringToFeatureSet(result);
+ } else {
+ if (error) {
+ *error = err;
+ }
+ }
}
return features;
}
diff --git a/client/adb_install.cpp b/client/adb_install.cpp
index d66d400..d6f536e 100644
--- a/client/adb_install.cpp
+++ b/client/adb_install.cpp
@@ -301,7 +301,7 @@
}
template <class TimePoint>
-static int msBetween(TimePoint start, TimePoint end) {
+static int ms_between(TimePoint start, TimePoint end) {
return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
}
@@ -349,7 +349,7 @@
}
const auto end = clock::now();
- printf("Install command complete in %d ms\n", msBetween(start, end));
+ printf("Install command complete in %d ms\n", ms_between(start, end));
if (wait) {
(*server_process).wait();
@@ -358,9 +358,9 @@
return 0;
}
-static std::pair<InstallMode, std::optional<InstallMode>> calculateInstallMode(
- InstallMode modeFromArgs, bool fastdeploy, CmdlineOption incrementalRequest) {
- if (incrementalRequest == CmdlineOption::Enable) {
+static std::pair<InstallMode, std::optional<InstallMode>> calculate_install_mode(
+ InstallMode modeFromArgs, bool fastdeploy, CmdlineOption incremental_request) {
+ if (incremental_request == CmdlineOption::Enable) {
if (fastdeploy) {
error_exit(
"--incremental and --fast-deploy options are incompatible. "
@@ -369,30 +369,30 @@
}
if (modeFromArgs != INSTALL_DEFAULT) {
- if (incrementalRequest == CmdlineOption::Enable) {
+ if (incremental_request == CmdlineOption::Enable) {
error_exit("--incremental is not compatible with other installation modes");
}
return {modeFromArgs, std::nullopt};
}
- if (incrementalRequest != CmdlineOption::Disable && !is_abb_exec_supported()) {
- if (incrementalRequest == CmdlineOption::None) {
- incrementalRequest = CmdlineOption::Disable;
+ if (incremental_request != CmdlineOption::Disable && !is_abb_exec_supported()) {
+ if (incremental_request == CmdlineOption::None) {
+ incremental_request = CmdlineOption::Disable;
} else {
error_exit("Device doesn't support incremental installations");
}
}
- if (incrementalRequest == CmdlineOption::None) {
+ if (incremental_request == CmdlineOption::None) {
// check if the host is ok with incremental by default
if (const char* incrementalFromEnv = getenv("ADB_INSTALL_DEFAULT_INCREMENTAL")) {
using namespace android::base;
auto val = ParseBool(incrementalFromEnv);
if (val == ParseBoolResult::kFalse) {
- incrementalRequest = CmdlineOption::Disable;
+ incremental_request = CmdlineOption::Disable;
}
}
}
- if (incrementalRequest == CmdlineOption::None) {
+ if (incremental_request == CmdlineOption::None) {
// still ok: let's see if the device allows using incremental by default
// it starts feeling like we're looking for an excuse to not to use incremental...
std::string error;
@@ -408,17 +408,17 @@
using namespace android::base;
auto val = ParseBool(buf);
if (val == ParseBoolResult::kFalse) {
- incrementalRequest = CmdlineOption::Disable;
+ incremental_request = CmdlineOption::Disable;
}
}
}
- if (incrementalRequest == CmdlineOption::Enable) {
+ if (incremental_request == CmdlineOption::Enable) {
// explicitly requested - no fallback
return {INSTALL_INCREMENTAL, std::nullopt};
}
const auto bestMode = best_install_mode();
- if (incrementalRequest == CmdlineOption::None) {
+ if (incremental_request == CmdlineOption::None) {
// no opinion - use incremental, fallback to regular on a failure.
return {INSTALL_INCREMENTAL, bestMode};
}
@@ -426,57 +426,75 @@
return {bestMode, std::nullopt};
}
-int install_app(int argc, const char** argv) {
- std::vector<int> processedArgIndices;
- InstallMode installMode = INSTALL_DEFAULT;
- bool use_fastdeploy = false;
- bool is_reinstall = false;
- bool wait = false;
- auto incremental_request = CmdlineOption::None;
- FastDeploy_AgentUpdateStrategy agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
+static std::vector<const char*> parse_install_mode(std::vector<const char*> argv,
+ InstallMode* install_mode,
+ CmdlineOption* incremental_request,
+ bool* incremental_wait) {
+ *install_mode = INSTALL_DEFAULT;
+ *incremental_request = CmdlineOption::None;
+ *incremental_wait = false;
- for (int i = 1; i < argc; i++) {
- if (argv[i] == "--streaming"sv) {
- processedArgIndices.push_back(i);
- installMode = INSTALL_STREAM;
- } else if (argv[i] == "--no-streaming"sv) {
- processedArgIndices.push_back(i);
- installMode = INSTALL_PUSH;
- } else if (argv[i] == "-r"sv) {
- // Note that this argument is not added to processedArgIndices because it
- // must be passed through to pm
- is_reinstall = true;
- } else if (argv[i] == "--fastdeploy"sv) {
- processedArgIndices.push_back(i);
- use_fastdeploy = true;
- } else if (argv[i] == "--no-fastdeploy"sv) {
- processedArgIndices.push_back(i);
- use_fastdeploy = false;
- } else if (argv[i] == "--force-agent"sv) {
- processedArgIndices.push_back(i);
- agent_update_strategy = FastDeploy_AgentUpdateAlways;
- } else if (argv[i] == "--date-check-agent"sv) {
- processedArgIndices.push_back(i);
- agent_update_strategy = FastDeploy_AgentUpdateNewerTimeStamp;
- } else if (argv[i] == "--version-check-agent"sv) {
- processedArgIndices.push_back(i);
- agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
- } else if (strlen(argv[i]) >= "--incr"sv.size() && "--incremental"sv.starts_with(argv[i])) {
- processedArgIndices.push_back(i);
- incremental_request = CmdlineOption::Enable;
- } else if (strlen(argv[i]) >= "--no-incr"sv.size() &&
- "--no-incremental"sv.starts_with(argv[i])) {
- processedArgIndices.push_back(i);
- incremental_request = CmdlineOption::Disable;
- } else if (argv[i] == "--wait"sv) {
- processedArgIndices.push_back(i);
- wait = true;
+ std::vector<const char*> passthrough;
+ for (auto&& arg : argv) {
+ if (arg == "--streaming"sv) {
+ *install_mode = INSTALL_STREAM;
+ } else if (arg == "--no-streaming"sv) {
+ *install_mode = INSTALL_PUSH;
+ } else if (strlen(arg) >= "--incr"sv.size() && "--incremental"sv.starts_with(arg)) {
+ *incremental_request = CmdlineOption::Enable;
+ } else if (strlen(arg) >= "--no-incr"sv.size() && "--no-incremental"sv.starts_with(arg)) {
+ *incremental_request = CmdlineOption::Disable;
+ } else if (arg == "--wait"sv) {
+ *incremental_wait = true;
+ } else {
+ passthrough.push_back(arg);
}
}
+ return passthrough;
+}
- auto [primaryMode, fallbackMode] =
- calculateInstallMode(installMode, use_fastdeploy, incremental_request);
- if ((primaryMode == INSTALL_STREAM || fallbackMode.value_or(INSTALL_PUSH) == INSTALL_STREAM) &&
+static std::vector<const char*> parse_fast_deploy_mode(
+ std::vector<const char*> argv, bool* use_fastdeploy,
+ FastDeploy_AgentUpdateStrategy* agent_update_strategy) {
+ *use_fastdeploy = false;
+ *agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
+
+ std::vector<const char*> passthrough;
+ for (auto&& arg : argv) {
+ if (arg == "--fastdeploy"sv) {
+ *use_fastdeploy = true;
+ } else if (arg == "--no-fastdeploy"sv) {
+ *use_fastdeploy = false;
+ } else if (arg == "--force-agent"sv) {
+ *agent_update_strategy = FastDeploy_AgentUpdateAlways;
+ } else if (arg == "--date-check-agent"sv) {
+ *agent_update_strategy = FastDeploy_AgentUpdateNewerTimeStamp;
+ } else if (arg == "--version-check-agent"sv) {
+ *agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
+ } else {
+ passthrough.push_back(arg);
+ }
+ }
+ return passthrough;
+}
+
+int install_app(int argc, const char** argv) {
+ InstallMode install_mode = INSTALL_DEFAULT;
+ auto incremental_request = CmdlineOption::None;
+ bool incremental_wait = false;
+
+ bool use_fastdeploy = false;
+ FastDeploy_AgentUpdateStrategy agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
+
+ auto unused_argv = parse_install_mode({argv, argv + argc}, &install_mode, &incremental_request,
+ &incremental_wait);
+ auto passthrough_argv =
+ parse_fast_deploy_mode(std::move(unused_argv), &use_fastdeploy, &agent_update_strategy);
+
+ auto [primary_mode, fallback_mode] =
+ calculate_install_mode(install_mode, use_fastdeploy, incremental_request);
+ if ((primary_mode == INSTALL_STREAM ||
+ fallback_mode.value_or(INSTALL_PUSH) == INSTALL_STREAM) &&
best_install_mode() == INSTALL_PUSH) {
error_exit("Attempting to use streaming install on unsupported device");
}
@@ -490,19 +508,12 @@
}
fastdeploy_set_agent_update_strategy(agent_update_strategy);
- std::vector<const char*> passthrough_argv;
- for (int i = 0; i < argc; i++) {
- if (std::find(processedArgIndices.begin(), processedArgIndices.end(), i) ==
- processedArgIndices.end()) {
- passthrough_argv.push_back(argv[i]);
- }
- }
if (passthrough_argv.size() < 2) {
error_exit("install requires an apk argument");
}
- auto runInstallMode = [&](InstallMode installMode, bool silent) {
- switch (installMode) {
+ auto run_install_mode = [&](InstallMode install_mode, bool silent) {
+ switch (install_mode) {
case INSTALL_PUSH:
return install_app_legacy(passthrough_argv.size(), passthrough_argv.data(),
use_fastdeploy);
@@ -511,20 +522,20 @@
use_fastdeploy);
case INSTALL_INCREMENTAL:
return install_app_incremental(passthrough_argv.size(), passthrough_argv.data(),
- wait, silent);
+ incremental_wait, silent);
case INSTALL_DEFAULT:
default:
- return 1;
+ error_exit("invalid install mode");
}
};
- auto res = runInstallMode(primaryMode, fallbackMode.has_value());
- if (res && fallbackMode.value_or(primaryMode) != primaryMode) {
- res = runInstallMode(*fallbackMode, false);
+ auto res = run_install_mode(primary_mode, fallback_mode.has_value());
+ if (res && fallback_mode.value_or(primary_mode) != primary_mode) {
+ res = run_install_mode(*fallback_mode, false);
}
return res;
}
-int install_multiple_app(int argc, const char** argv) {
+static int install_multiple_app_streamed(int argc, const char** argv) {
// Find all APK arguments starting at end.
// All other arguments passed through verbatim.
int first_apk = -1;
@@ -550,7 +561,6 @@
if (first_apk == -1) error_exit("need APK file on command line");
const bool use_abb_exec = is_abb_exec_supported();
-
const std::string install_cmd =
use_abb_exec ? "package"
: best_install_mode() == INSTALL_PUSH ? "exec:pm" : "exec:cmd package";
@@ -673,6 +683,44 @@
return EXIT_SUCCESS;
}
+int install_multiple_app(int argc, const char** argv) {
+ InstallMode install_mode = INSTALL_DEFAULT;
+ auto incremental_request = CmdlineOption::None;
+ bool incremental_wait = false;
+ bool use_fastdeploy = false;
+
+ auto passthrough_argv = parse_install_mode({argv + 1, argv + argc}, &install_mode,
+ &incremental_request, &incremental_wait);
+
+ auto [primary_mode, fallback_mode] =
+ calculate_install_mode(install_mode, use_fastdeploy, incremental_request);
+ if ((primary_mode == INSTALL_STREAM ||
+ fallback_mode.value_or(INSTALL_PUSH) == INSTALL_STREAM) &&
+ best_install_mode() == INSTALL_PUSH) {
+ error_exit("Attempting to use streaming install on unsupported device");
+ }
+
+ auto run_install_mode = [&](InstallMode install_mode, bool silent) {
+ switch (install_mode) {
+ case INSTALL_PUSH:
+ case INSTALL_STREAM:
+ return install_multiple_app_streamed(passthrough_argv.size(),
+ passthrough_argv.data());
+ case INSTALL_INCREMENTAL:
+ return install_app_incremental(passthrough_argv.size(), passthrough_argv.data(),
+ incremental_wait, silent);
+ case INSTALL_DEFAULT:
+ default:
+ error_exit("invalid install mode");
+ }
+ };
+ auto res = run_install_mode(primary_mode, fallback_mode.has_value());
+ if (res && fallback_mode.value_or(primary_mode) != primary_mode) {
+ res = run_install_mode(*fallback_mode, false);
+ }
+ return res;
+}
+
int install_multi_package(int argc, const char** argv) {
// Find all APK arguments starting at end.
// All other arguments passed through verbatim.
@@ -697,23 +745,31 @@
fprintf(stderr, "adb: multi-package install is not supported on this device\n");
return EXIT_FAILURE;
}
- std::string install_cmd = "exec:cmd package";
- std::string multi_package_cmd =
- android::base::StringPrintf("%s install-create --multi-package", install_cmd.c_str());
+ const bool use_abb_exec = is_abb_exec_supported();
+ const std::string install_cmd = use_abb_exec ? "package" : "exec:cmd package";
+
+ std::vector<std::string> multi_package_cmd_args = {install_cmd, "install-create",
+ "--multi-package"};
+
+ multi_package_cmd_args.reserve(first_package + 4);
for (int i = 1; i < first_package; i++) {
- multi_package_cmd += " " + escape_arg(argv[i]);
+ if (use_abb_exec) {
+ multi_package_cmd_args.push_back(argv[i]);
+ } else {
+ multi_package_cmd_args.push_back(escape_arg(argv[i]));
+ }
}
if (apex_found) {
- multi_package_cmd += " --staged";
+ multi_package_cmd_args.emplace_back("--staged");
}
// Create multi-package install session
std::string error;
char buf[BUFSIZ];
{
- unique_fd fd(adb_connect(multi_package_cmd, &error));
+ unique_fd fd = send_command(multi_package_cmd_args, &error);
if (fd < 0) {
fprintf(stderr, "adb: connect error for create multi-package: %s\n", error.c_str());
return EXIT_FAILURE;
@@ -735,6 +791,7 @@
fputs(buf, stderr);
return EXIT_FAILURE;
}
+ const auto parent_session_id_str = std::to_string(parent_session_id);
fprintf(stdout, "Created parent session ID %d.\n", parent_session_id);
@@ -742,17 +799,30 @@
// Valid session, now create the individual sessions and stream the APKs
int success = EXIT_FAILURE;
- std::string individual_cmd =
- android::base::StringPrintf("%s install-create", install_cmd.c_str());
- std::string all_session_ids = "";
+ std::vector<std::string> individual_cmd_args = {install_cmd, "install-create"};
for (int i = 1; i < first_package; i++) {
- individual_cmd += " " + escape_arg(argv[i]);
+ if (use_abb_exec) {
+ individual_cmd_args.push_back(argv[i]);
+ } else {
+ individual_cmd_args.push_back(escape_arg(argv[i]));
+ }
}
if (apex_found) {
- individual_cmd += " --staged";
+ individual_cmd_args.emplace_back("--staged");
}
- std::string individual_apex_cmd = individual_cmd + " --apex";
- std::string cmd = "";
+
+ std::vector<std::string> individual_apex_cmd_args;
+ if (apex_found) {
+ individual_apex_cmd_args = individual_cmd_args;
+ individual_apex_cmd_args.emplace_back("--apex");
+ }
+
+ std::vector<std::string> add_session_cmd_args = {
+ install_cmd,
+ "install-add-session",
+ parent_session_id_str,
+ };
+
for (int i = first_package; i < argc; i++) {
const char* file = argv[i];
char buf[BUFSIZ];
@@ -760,9 +830,9 @@
unique_fd fd;
// Create individual install session
if (android::base::EndsWithIgnoreCase(file, ".apex")) {
- fd.reset(adb_connect(individual_apex_cmd, &error));
+ fd = send_command(individual_apex_cmd_args, &error);
} else {
- fd.reset(adb_connect(individual_cmd, &error));
+ fd = send_command(individual_cmd_args, &error);
}
if (fd < 0) {
fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
@@ -785,6 +855,7 @@
fputs(buf, stderr);
goto finalize_multi_package_session;
}
+ const auto session_id_str = std::to_string(session_id);
fprintf(stdout, "Created child session ID %d.\n", session_id);
session_ids.push_back(session_id);
@@ -799,10 +870,15 @@
goto finalize_multi_package_session;
}
- std::string cmd = android::base::StringPrintf(
- "%s install-write -S %" PRIu64 " %d %d_%s -", install_cmd.c_str(),
- static_cast<uint64_t>(sb.st_size), session_id, i,
- android::base::Basename(split).c_str());
+ std::vector<std::string> cmd_args = {
+ install_cmd,
+ "install-write",
+ "-S",
+ std::to_string(sb.st_size),
+ session_id_str,
+ android::base::StringPrintf("%d_%s", i, android::base::Basename(file).c_str()),
+ "-",
+ };
unique_fd local_fd(adb_open(split.c_str(), O_RDONLY | O_CLOEXEC));
if (local_fd < 0) {
@@ -811,7 +887,7 @@
}
std::string error;
- unique_fd remote_fd(adb_connect(cmd, &error));
+ unique_fd remote_fd = send_command(cmd_args, &error);
if (remote_fd < 0) {
fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
goto finalize_multi_package_session;
@@ -830,13 +906,11 @@
goto finalize_multi_package_session;
}
}
- all_session_ids += android::base::StringPrintf(" %d", session_id);
+ add_session_cmd_args.push_back(std::to_string(session_id));
}
- cmd = android::base::StringPrintf("%s install-add-session %d%s", install_cmd.c_str(),
- parent_session_id, all_session_ids.c_str());
{
- unique_fd fd(adb_connect(cmd, &error));
+ unique_fd fd = send_command(add_session_cmd_args, &error);
if (fd < 0) {
fprintf(stderr, "adb: connect error for install-add-session: %s\n", error.c_str());
goto finalize_multi_package_session;
@@ -845,7 +919,8 @@
}
if (strncmp("Success", buf, 7)) {
- fprintf(stderr, "adb: failed to link sessions (%s)\n", cmd.c_str());
+ fprintf(stderr, "adb: failed to link sessions (%s)\n",
+ android::base::Join(add_session_cmd_args, " ").c_str());
fputs(buf, stderr);
goto finalize_multi_package_session;
}
@@ -855,11 +930,14 @@
finalize_multi_package_session:
// Commit session if we streamed everything okay; otherwise abandon
- std::string service =
- android::base::StringPrintf("%s install-%s %d", install_cmd.c_str(),
- success == 0 ? "commit" : "abandon", parent_session_id);
+ std::vector<std::string> service_args = {
+ install_cmd,
+ success == 0 ? "install-commit" : "install-abandon",
+ parent_session_id_str,
+ };
+
{
- unique_fd fd(adb_connect(service, &error));
+ unique_fd fd = send_command(service_args, &error);
if (fd < 0) {
fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
return EXIT_FAILURE;
@@ -880,10 +958,13 @@
session_ids.push_back(parent_session_id);
// try to abandon all remaining sessions
for (std::size_t i = 0; i < session_ids.size(); i++) {
- service = android::base::StringPrintf("%s install-abandon %d", install_cmd.c_str(),
- session_ids[i]);
+ std::vector<std::string> service_args = {
+ install_cmd,
+ "install-abandon",
+ std::to_string(session_ids[i]),
+ };
fprintf(stderr, "Attempting to abandon session ID %d\n", session_ids[i]);
- unique_fd fd(adb_connect(service, &error));
+ unique_fd fd = send_command(service_args, &error);
if (fd < 0) {
fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
continue;
diff --git a/client/adb_wifi.cpp b/client/adb_wifi.cpp
index fa71028..61a9a48 100644
--- a/client/adb_wifi.cpp
+++ b/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/client/commandline.cpp b/client/commandline.cpp
index efb6c2f..eaa32e5 100644
--- a/client/commandline.cpp
+++ b/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/client/mdns_utils.cpp b/client/mdns_utils.cpp
new file mode 100644
index 0000000..8666b18
--- /dev/null
+++ b/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/client/mdns_utils.h b/client/mdns_utils.h
new file mode 100644
index 0000000..40d095d
--- /dev/null
+++ b/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/client/mdns_utils_test.cpp b/client/mdns_utils_test.cpp
new file mode 100644
index 0000000..ec71529
--- /dev/null
+++ b/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/client/transport_mdns.cpp b/client/transport_mdns.cpp
index c9993b7..9db2453 100644
--- a/client/transport_mdns.cpp
+++ b/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/daemon/shell_service.cpp b/daemon/shell_service.cpp
index fbfae1e..dbca4ad 100644
--- a/daemon/shell_service.cpp
+++ b/daemon/shell_service.cpp
@@ -646,15 +646,21 @@
}
// After handling all of the events we've received, check to see if any fds have died.
- if (stdinout_pfd.revents & (POLLHUP | POLLRDHUP | POLLERR | POLLNVAL)) {
+ auto poll_finished = [](int events) {
+ // Don't return failure until we've read out all of the fd's incoming data.
+ return (events & POLLIN) == 0 &&
+ (events & (POLLHUP | POLLRDHUP | POLLERR | POLLNVAL)) != 0;
+ };
+
+ if (poll_finished(stdinout_pfd.revents)) {
return &stdinout_sfd_;
}
- if (stderr_pfd.revents & (POLLHUP | POLLRDHUP | POLLERR | POLLNVAL)) {
+ if (poll_finished(stderr_pfd.revents)) {
return &stderr_sfd_;
}
- if (protocol_pfd.revents & (POLLHUP | POLLRDHUP | POLLERR | POLLNVAL)) {
+ if (poll_finished(protocol_pfd.revents)) {
return &protocol_sfd_;
}
} // while (!dead_sfd)
diff --git a/socket_spec.cpp b/socket_spec.cpp
index b7b25fa..5cad70d 100644
--- a/socket_spec.cpp
+++ b/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/test_adb.py b/test_adb.py
index 9912f11..4b99411 100755
--- a/test_adb.py
+++ b/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."""
diff --git a/test_device.py b/test_device.py
index f5e4cbb..9f1f403 100755
--- a/test_device.py
+++ b/test_device.py
@@ -82,10 +82,13 @@
class AbbTest(DeviceTest):
def test_smoke(self):
- result = subprocess.run(['adb', 'abb'], capture_output=True)
- self.assertEqual(1, result.returncode)
- expected_output = b"cmd: No service specified; use -l to list all services\n"
- self.assertEqual(expected_output, result.stderr)
+ abb = subprocess.run(['adb', 'abb'], capture_output=True)
+ cmd = subprocess.run(['adb', 'shell', 'cmd'], capture_output=True)
+
+ # abb squashes all failures to 1.
+ self.assertEqual(abb.returncode == 0, cmd.returncode == 0)
+ self.assertEqual(abb.stdout, cmd.stdout)
+ self.assertEqual(abb.stderr, cmd.stderr)
class ForwardReverseTest(DeviceTest):
def _test_no_rebind(self, description, direction_list, direction,