From 3d155b6355670a874841d3c82f6ba57d26469199 Mon Sep 17 00:00:00 2001 From: Fabien Sanglard Date: Thu, 30 Nov 2023 14:52:40 -0800 Subject: Device speed/maxSpeed retrieval + API Retrieve negotiated speed and max speed to allow tools to detect when user have sub-optimal setup (a.k.a: Using USB2 cable when device is USB3 capable). Design doc: go/android_usb_speed Test: test_device.py Bug: 314216895 Change-Id: I848ef14236135b8428893ce80f0afdb6dd612bea --- Android.bp | 13 +++++- SERVICES.TXT | 10 +++-- adb.cpp | 9 ++++- client/commandline.cpp | 18 +++++++-- client/usb_libusb.cpp | 76 +++++++++++++++++++++++++++++++++++ proto/Android.bp | 44 +++++++++++++++++++++ proto/devices.proto | 61 ++++++++++++++++++++++++++++ services.cpp | 8 +++- sockets.cpp | 3 +- test_device.py | 105 ++++++++++++++++++++++++++++++++++++++++++++++--- transport.cpp | 95 +++++++++++++++++++++++++++++++++++++++----- transport.h | 21 +++++++++- 12 files changed, 432 insertions(+), 31 deletions(-) create mode 100644 proto/devices.proto diff --git a/Android.bp b/Android.bp index 34270798..2a0a6a8e 100644 --- a/Android.bp +++ b/Android.bp @@ -310,6 +310,7 @@ cc_library_host_static { "libcrypto_utils", "libcutils", "libdiagnose_usb", + "libdevices_protos", "liblog", "libmdnssd", "libopenscreen-discovery", @@ -379,12 +380,13 @@ cc_test_host { "libcrypto", "libcrypto_utils", "libcutils", + "libdevices_protos", "libdiagnose_usb", "liblog", "libmdnssd", "libopenscreen-discovery", "libopenscreen-platform-impl", - "libprotobuf-cpp-lite", + "libprotobuf-cpp-full", "libssl", "libusb", ], @@ -447,6 +449,7 @@ cc_binary_host { "libcrypto_utils", "libcutils", "libdiagnose_usb", + "libdevices_protos", "libfastdeploy_host", "liblog", "liblog", @@ -889,10 +892,15 @@ python_test_host { name: "adb_integration_test_device", main: "test_device.py", srcs: [ + "proto/devices.proto", "test_device.py", ], + proto: { + canonical_path_from_root: false, + }, libs: [ "adb_py", + "libprotobuf-python", ], test_config: "adb_integration_test_device.xml", test_suites: ["general-tests"], @@ -974,13 +982,14 @@ cc_test_host { "libcrypto", "libcrypto_utils", "libcutils", + "libdevices_protos", "libdiagnose_usb", "libfastdeploy_host", "liblog", "libmdnssd", "libopenscreen-discovery", "libopenscreen-platform-impl", - "libprotobuf-cpp-lite", + "libprotobuf-cpp-full", "libssl", "libusb", "libutils", diff --git a/SERVICES.TXT b/SERVICES.TXT index c8d06a32..380f2cf0 100644 --- a/SERVICES.TXT +++ b/SERVICES.TXT @@ -21,12 +21,14 @@ host:devices-l the connection is closed host:track-devices - This is a variant of host:devices which doesn't close the +host:track-devices-proto-binary +host:track-devices-proto-text + These are variants of host:devices which doesn't close the connection. Instead, a new device list description is sent each time a device is added/removed or the state of a given - device changes (hex4 + content). This allows tools like DDMS - to track the state of connected devices in real-time without - polling the server repeatedly. + device changes. + Variant [-proto-binary] is binary protobuf format. + Variant [-proto-text] is text protobuf format. host:emulator: This is a special query that is sent to the ADB server when a diff --git a/adb.cpp b/adb.cpp index bb077a0e..f2cf8fc7 100644 --- a/adb.cpp +++ b/adb.cpp @@ -1326,9 +1326,14 @@ HostRequestResult handle_host_request(std::string_view service, TransportType ty // return a list of all connected devices if (service == "devices" || service == "devices-l") { - bool long_listing = service == "devices-l"; + TrackerOutputType output_type; + if (service == "devices-l") { + output_type = LONG_TEXT; + } else { + output_type = SHORT_TEXT; + } D("Getting device list..."); - std::string device_list = list_transports(long_listing); + std::string device_list = list_transports(output_type); D("Sending device list..."); SendOkay(reply_fd, device_list); return HostRequestResult::Handled; diff --git a/client/commandline.cpp b/client/commandline.cpp index aa3e7130..88d40c81 100644 --- a/client/commandline.cpp +++ b/client/commandline.cpp @@ -2067,10 +2067,22 @@ int adb_commandline(int argc, const char** argv) { TrackAppStreamsCallback callback; return adb_connect_command("track-app", nullptr, &callback); } else if (!strcmp(argv[0], "track-devices")) { - if (argc > 2 || (argc == 2 && strcmp(argv[1], "-l"))) { - error_exit("usage: adb track-devices [-l]"); + const char* listopt; + if (argc < 2) { + listopt = ""; + } else { + if (!strcmp(argv[1], "-l")) { + listopt = argv[1]; + } else if (!strcmp(argv[1], "--proto-text")) { + listopt = "-proto-text"; + } else if (!strcmp(argv[1], "--proto-binary")) { + listopt = "-proto-binary"; + } else { + error_exit("usage: adb track-devices [-l][--proto-text][--proto-binary]"); + } } - return adb_connect_command(argc == 2 ? "host:track-devices-l" : "host:track-devices"); + std::string query = android::base::StringPrintf("host:track-devices%s", listopt); + return adb_connect_command(query); } else if (!strcmp(argv[0], "raw")) { if (argc != 2) { error_exit("usage: adb raw SERVICE"); diff --git a/client/usb_libusb.cpp b/client/usb_libusb.cpp index dbe62b2c..b726199d 100644 --- a/client/usb_libusb.cpp +++ b/client/usb_libusb.cpp @@ -493,6 +493,74 @@ struct LibusbConnection : public Connection { return serial; } + // libusb gives us an int which is a value from 'enum libusb_speed' + static ConnectionSpeed ToConnectionSpeed(int speed) { + switch (speed) { + case LIBUSB_SPEED_LOW: + return USB1_0; + case LIBUSB_SPEED_FULL: + return USB2_0_FULL; + case LIBUSB_SPEED_HIGH: + return USB2_0_HIGH; + case LIBUSB_SPEED_SUPER: + return USB3_0; + case LIBUSB_SPEED_SUPER_PLUS: + return USB3_1; + case LIBUSB_SPEED_UNKNOWN: + default: + return UNKNOWN; + } + } + + // libusb gives us a bitfield made of 'enum libusb_supported_speed' values + static ConnectionSpeed ExtractMaxSpeed(uint16_t wSpeedSupported) { + if (wSpeedSupported == 0) { + return UNKNOWN; + } + + int msb = 0; + while (wSpeedSupported >>= 1) { + msb++; + } + + switch (1 << msb) { + case LIBUSB_LOW_SPEED_OPERATION: + return USB1_0; + case LIBUSB_FULL_SPEED_OPERATION: + return USB2_0_FULL; + case LIBUSB_HIGH_SPEED_OPERATION: + return USB2_0_HIGH; + case LIBUSB_SUPER_SPEED_OPERATION: + return USB3_0; + default: + return UNKNOWN; + } + } + + void RetrieveSpeeds() { + negotiated_speed_ = ToConnectionSpeed(libusb_get_device_speed(device_.get())); + + // The set of supported speed is in a SuperSpeed capability + struct libusb_bos_descriptor* bos = nullptr; + if (!libusb_get_bos_descriptor(device_handle_.get(), &bos)) { + for (int i = 0; i < bos->bNumDeviceCaps; i++) { + if (bos->dev_capability[i]->bDevCapabilityType != + LIBUSB_BT_SS_USB_DEVICE_CAPABILITY) { + continue; + } + + libusb_ss_usb_device_capability_descriptor* ss_usb_device_cap = nullptr; + int r = libusb_get_ss_usb_device_capability_descriptor( + nullptr, bos->dev_capability[i], &ss_usb_device_cap); + if (!r) { + max_speed_ = ExtractMaxSpeed(ss_usb_device_cap->wSpeedSupported); + libusb_free_ss_usb_device_capability_descriptor(ss_usb_device_cap); + } + } + libusb_free_bos_descriptor(bos); + } + } + bool OpenDevice(std::string* error) { if (device_handle_) { LOG_ERR(error, "device already open"); @@ -548,6 +616,7 @@ struct LibusbConnection : public Connection { } } + RetrieveSpeeds(); return true; } @@ -766,6 +835,10 @@ struct LibusbConnection : public Connection { return connection; } + virtual ConnectionSpeed MaxSpeedMbps() override final { return max_speed_; } + + virtual ConnectionSpeed NegotiatedSpeedMbps() override final { return negotiated_speed_; } + unique_device device_; unique_device_handle device_handle_; std::string device_address_; @@ -791,6 +864,9 @@ struct LibusbConnection : public Connection { std::condition_variable destruction_cv_; size_t zero_mask_ = 0; + + ConnectionSpeed negotiated_speed_ = UNKNOWN; + ConnectionSpeed max_speed_ = UNKNOWN; }; static libusb_hotplug_callback_handle hotplug_handle; diff --git a/proto/Android.bp b/proto/Android.bp index fae321f2..383e4a70 100644 --- a/proto/Android.bp +++ b/proto/Android.bp @@ -143,3 +143,47 @@ cc_library_host_static { type: "full", }, } + +cc_defaults { + name: "libdevices_protos_defaults", + cflags: [ + "-Wall", + "-Wextra", + "-Wthread-safety", + "-Werror", + ], + + compile_multilib: "both", + + srcs: [ + "devices.proto", + ], + target: { + windows: { + compile_multilib: "first", + enabled: true, + }, + }, + + visibility: [ + "//packages/modules/adb:__subpackages__", + ], + + stl: "libc++_static", + + apex_available: [ + "com.android.adbd", + "test_com.android.adbd", + ], +} + +cc_library_host_static { + name: "libdevices_protos", + defaults: ["libdevices_protos_defaults"], + + proto: { + export_proto_headers: true, + type: "full", + }, +} + diff --git a/proto/devices.proto b/proto/devices.proto new file mode 100644 index 00000000..48ebadf8 --- /dev/null +++ b/proto/devices.proto @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2023 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. + */ + +syntax = "proto3"; + +option java_package = "com.android.server.adb.protos"; +option java_outer_classname = "DevicesProto"; + +package adb.proto; + +// This mirrors adb.h's "enum ConnectionState" +enum ConnectionState { + ANY = 0; + CONNECTING = 1; + AUTHORIZING = 2; + UNAUTHORIZED = 3; + NOPERMISSION = 4; + DETACHED = 5; + OFFLINE = 6; + BOOTLOADER = 7; + DEVICE = 8; + HOST = 9; + RECOVERY = 10; + SIDELOAD = 11; + RESCUE = 12; +} + +enum ConnectionType { + UNKNOWN = 0; + USB = 1; + SOCKET = 2; +} + +message Device { + string serial = 1; + ConnectionState state = 2; + string bus_address = 3; + string product = 4; + string model = 5; + string device = 6; + ConnectionType connection_type = 7; + int64 negotiated_speed = 8; + int64 max_speed = 9; +} + +message Devices { + repeated Device device = 1; +} diff --git a/services.cpp b/services.cpp index 0c3d061d..2e9499f4 100644 --- a/services.cpp +++ b/services.cpp @@ -250,9 +250,13 @@ static void wait_service(unique_fd fd, std::string serial, TransportId transport asocket* host_service_to_socket(std::string_view name, std::string_view serial, TransportId transport_id) { if (name == "track-devices") { - return create_device_tracker(false); + return create_device_tracker(SHORT_TEXT); } else if (name == "track-devices-l") { - return create_device_tracker(true); + return create_device_tracker(LONG_TEXT); + } else if (name == "track-devices-proto-binary") { + return create_device_tracker(PROTOBUF); + } else if (name == "track-devices-proto-text") { + return create_device_tracker(TEXT_PROTOBUF); } else if (android::base::ConsumePrefix(&name, "wait-for-")) { std::string spec(name); unique_fd fd = diff --git a/sockets.cpp b/sockets.cpp index 87905cdb..e10f710e 100644 --- a/sockets.cpp +++ b/sockets.cpp @@ -879,7 +879,8 @@ static int smart_socket_enqueue(asocket* s, apacket::payload_type data) { s2 = host_service_to_socket(service, serial, transport_id); if (s2 == nullptr) { LOG(VERBOSE) << "SS(" << s->id << "): couldn't create host service '" << service << "'"; - SendFail(s->peer->fd, "unknown host service"); + std::string msg = std::string("unknown host service '") + std::string(service) + "'"; + SendFail(s->peer->fd, msg); goto fail; } diff --git a/test_device.py b/test_device.py index c606adf2..20cd98da 100755 --- a/test_device.py +++ b/test_device.py @@ -19,6 +19,7 @@ from __future__ import print_function import contextlib import hashlib +import io import os import posixpath import random @@ -35,6 +36,8 @@ import threading import time import unittest +import proto.devices_pb2 as proto_devices + from datetime import datetime import adb @@ -1761,15 +1764,107 @@ class WindowsConsoleTest(DeviceTest): console_output = read_screen(screen) self.assertEqual(unicode_string, console_output) +class DevicesListing(DeviceTest): + + serial = subprocess.check_output(['adb', 'get-serialno']).strip().decode("utf-8") + # def get_serial(self): + # return subprocess.check_output(self.device.adb_cmd + ['get-serialno']).strip().decode("utf-8") + + def test_devices(self): + proc = subprocess.Popen(['adb', 'devices'], stdin=subprocess.PIPE, stdout=subprocess.PIPE) + lines = list(map(lambda b: b.decode("utf-8"), proc.stdout.readlines())) + self.assertEqual(len(lines), 3) + line = lines[1] + self.assertTrue(self.serial in line) + self.assertFalse("{" in line) + self.assertFalse("}" in line) + self.assertTrue("device" in line) + self.assertFalse("product" in line) + self.assertFalse("transport" in line) + + def test_devices_l(self): + proc = subprocess.Popen(['adb', 'devices', '-l'], stdin=subprocess.PIPE, stdout=subprocess.PIPE) + lines = list(map(lambda b: b.decode("utf-8"), proc.stdout.readlines())) + self.assertEqual(len(lines), 3) + line = lines[1] + self.assertTrue(self.serial in line) + self.assertFalse("{" in line) + self.assertFalse("}" in line) + self.assertTrue("device" in line) + self.assertTrue("product" in line) + self.assertTrue("transport" in line) + + def test_track_devices(self): + proc = subprocess.Popen(['adb', 'track-devices'], stdin=subprocess.PIPE, stdout=subprocess.PIPE) + reader = io.TextIOWrapper(proc.stdout, encoding='utf8') + output_size = int(reader.read(4), 16) + output = reader.read(output_size) + self.assertFalse("{" in output) + self.assertFalse("}" in output) + self.assertTrue(self.serial in output) + self.assertTrue("device" in output) + self.assertFalse("product" in output) + self.assertFalse("transport" in output) + + def test_track_devices_l(self): + proc = subprocess.Popen(['adb', 'track-devices', '-l'], stdin=subprocess.PIPE, stdout=subprocess.PIPE) + reader = io.TextIOWrapper(proc.stdout, encoding='utf8') + output_size = int(reader.read(4), 16) + output = reader.read(output_size) + self.assertFalse("{" in output) + self.assertFalse("}" in output) + self.assertTrue(self.serial in output) + self.assertTrue("device" in output) + self.assertTrue("product" in output) + self.assertTrue("transport" in output) + + def test_track_devices_proto_text(self): + proc = subprocess.Popen(['adb', 'track-devices', '--proto-text'], stdin=subprocess.PIPE, stdout=subprocess.PIPE) + reader = io.TextIOWrapper(proc.stdout, encoding='utf8') + output_size = int(reader.read(4), 16) + output = reader.read(output_size) + self.assertTrue("{" in output) + self.assertTrue("}" in output) + self.assertTrue(self.serial in output) + self.assertTrue("device" in output) + self.assertTrue("product" in output) + self.assertTrue("connection_type" in output) + + def test_track_devices_proto_binary(self): + proc = subprocess.Popen(['adb', 'track-devices', '--proto-binary'], stdin=subprocess.PIPE, stdout=subprocess.PIPE) + + output_size = int(proc.stdout.read(4).decode("utf-8"), 16) + proto = proc.stdout.read(output_size) + + devices = proto_devices.Devices() + devices.ParseFromString(proto) + + device = devices.device[0] + self.assertTrue(device.serial == self.serial) + self.assertFalse(device.bus_address == "") + self.assertFalse(device.product == "") + self.assertFalse(device.model == "") + self.assertFalse(device.device == "") + self.assertTrue(device.negotiated_speed == int(device.negotiated_speed)) + self.assertTrue(device.max_speed == int(device.max_speed)) def main(): random.seed(0) - if len(adb.get_devices()) > 0: - suite = unittest.TestLoader().loadTestsFromName(__name__) - unittest.TextTestRunner(verbosity=3).run(suite) - else: + if len(adb.get_devices()) == 0: print('Test suite must be run with attached devices') + return + + # Run only specific test if given on command-line e.g: + # ./test_device.py ForwardReverseTest + # ./test_device.py ForwardReverseTest.test_forward_no_rebind + if len(sys.argv) == 2: + test_name = "." + sys.argv[1] + else: + test_name = "" + + suite = unittest.TestLoader().loadTestsFromName("__main__" + test_name) + unittest.TextTestRunner(verbosity=3).run(suite) if __name__ == '__main__': - main() + main() \ No newline at end of file diff --git a/transport.cpp b/transport.cpp index 5f06f291..71756d36 100644 --- a/transport.cpp +++ b/transport.cpp @@ -56,7 +56,9 @@ #include "sysdeps/chrono.h" #if ADB_HOST +#include #include "client/usb.h" +#include "devices.pb.h" #endif using namespace adb::crypto; @@ -95,6 +97,7 @@ const char* const kFeatureSendRecv2DryRunSend = "sendrecv_v2_dry_run_send"; const char* const kFeatureDelayedAck = "delayed_ack"; // TODO(joshuaduong): Bump to v2 when openscreen discovery is enabled by default const char* const kFeatureOpenscreenMdns = "openscreen_mdns"; +const char* const kFeatureDeviceTrackerProtoFormat = "devicetracker_proto_format"; namespace { @@ -602,7 +605,7 @@ void kick_transport(atransport* t, bool reset) { struct device_tracker { asocket socket; bool update_needed = false; - bool long_output = false; + TrackerOutputType output_type = SHORT_TEXT; device_tracker* next = nullptr; }; @@ -662,11 +665,11 @@ static void device_tracker_ready(asocket* socket) { // for the first time, even if no update occurred. if (tracker->update_needed) { tracker->update_needed = false; - device_tracker_send(tracker, list_transports(tracker->long_output)); + device_tracker_send(tracker, list_transports(tracker->output_type)); } } -asocket* create_device_tracker(bool long_output) { +asocket* create_device_tracker(TrackerOutputType output_type) { device_tracker* tracker = new device_tracker(); if (tracker == nullptr) LOG(FATAL) << "cannot allocate device tracker"; @@ -676,7 +679,7 @@ asocket* create_device_tracker(bool long_output) { tracker->socket.ready = device_tracker_ready; tracker->socket.close = device_tracker_close; tracker->update_needed = true; - tracker->long_output = long_output; + tracker->output_type = output_type; tracker->next = device_tracker_list; device_tracker_list = tracker; @@ -709,7 +712,7 @@ void update_transports() { while (tracker != nullptr) { device_tracker* next = tracker->next; // This may destroy the tracker if the connection is closed. - device_tracker_send(tracker, list_transports(tracker->long_output)); + device_tracker_send(tracker, list_transports(tracker->output_type)); tracker = next; } } @@ -1202,6 +1205,7 @@ const FeatureSet& supported_features() { kFeatureSendRecv2Zstd, kFeatureSendRecv2DryRunSend, kFeatureOpenscreenMdns, + kFeatureDeviceTrackerProtoFormat, }; // clang-format on @@ -1316,6 +1320,63 @@ static std::string sanitize(std::string str, bool alphanumeric) { return str; } +static adb::proto::ConnectionState adbStateFromProto(ConnectionState state) { + switch (state) { + case kCsConnecting: + return adb::proto::ConnectionState::CONNECTING; + case kCsAuthorizing: + return adb::proto::ConnectionState::AUTHORIZING; + case kCsUnauthorized: + return adb::proto::ConnectionState::UNAUTHORIZED; + case kCsNoPerm: + return adb::proto::ConnectionState::NOPERMISSION; + case kCsDetached: + return adb::proto::ConnectionState::DETACHED; + case kCsOffline: + return adb::proto::ConnectionState::OFFLINE; + case kCsBootloader: + return adb::proto::ConnectionState::BOOTLOADER; + case kCsDevice: + return adb::proto::ConnectionState::DEVICE; + case kCsHost: + return adb::proto::ConnectionState::HOST; + case kCsRecovery: + return adb::proto::ConnectionState::RECOVERY; + case kCsSideload: + return adb::proto::ConnectionState::SIDELOAD; + case kCsRescue: + return adb::proto::ConnectionState::RESCUE; + case kCsAny: + return adb::proto::ConnectionState::ANY; + } +} + +static std::string transportListToProto(const std::list& sorted_transport_list, + bool text_version) { + adb::proto::Devices devices; + for (const auto& t : sorted_transport_list) { + auto* device = devices.add_device(); + device->set_serial(t->serial.c_str()); + device->set_connection_type(t->type == kTransportUsb ? adb::proto::ConnectionType::USB + : adb::proto::ConnectionType::SOCKET); + device->set_state(adbStateFromProto(t->GetConnectionState())); + device->set_bus_address(sanitize(t->devpath, false)); + device->set_product(sanitize(t->product, false)); + device->set_model(sanitize(t->model, true)); + device->set_device(sanitize(t->device, false)); + device->set_max_speed(t->connection()->MaxSpeedMbps()); + device->set_negotiated_speed(t->connection()->NegotiatedSpeedMbps()); + } + + std::string proto; + if (text_version) { + google::protobuf::TextFormat::PrintToString(devices, &proto); + } else { + devices.SerializeToString(&proto); + } + return proto; +} + static void append_transport_info(std::string* result, const char* key, const std::string& value, bool alphanumeric) { if (value.empty()) { @@ -1354,7 +1415,16 @@ static void append_transport(const atransport* t, std::string* result, bool long *result += '\n'; } -std::string list_transports(bool long_listing) { +static std::string transportListToText(const std::list& sorted_transport_list, + bool long_listing) { + std::string result; + for (const auto& t : sorted_transport_list) { + append_transport(t, &result, long_listing); + } + return result; +} + +std::string list_transports(TrackerOutputType outputType) { std::lock_guard lock(transport_lock); auto sorted_transport_list = transport_list; @@ -1365,11 +1435,16 @@ std::string list_transports(bool long_listing) { return x->serial < y->serial; }); - std::string result; - for (const auto& t : sorted_transport_list) { - append_transport(t, &result, long_listing); + switch (outputType) { + case SHORT_TEXT: + case LONG_TEXT: { + return transportListToText(sorted_transport_list, outputType == LONG_TEXT); + } + case PROTOBUF: + case TEXT_PROTOBUF: { + return transportListToProto(sorted_transport_list, outputType == TEXT_PROTOBUF); + } } - return result; } void close_usb_devices(std::function predicate, bool reset) { diff --git a/transport.h b/transport.h index eb634ed7..97391a88 100644 --- a/transport.h +++ b/transport.h @@ -136,6 +136,20 @@ struct Connection { atransport* transport_ = nullptr; static std::unique_ptr FromFd(unique_fd fd); + + enum ConnectionSpeed { + UNKNOWN = 0, + USB1_0 = 1, + USB2_0_FULL = 12, + USB2_0_HIGH = 480, + USB3_0 = 5000, + USB3_1 = 10000, + USB3_2 = 20000, + USB4_0 = 40000, + }; + + virtual ConnectionSpeed NegotiatedSpeedMbps() { return UNKNOWN; } + virtual ConnectionSpeed MaxSpeedMbps() { return UNKNOWN; } }; // Abstraction for a blocking packet transport. @@ -474,7 +488,6 @@ bool iterate_transports(std::function fn); void init_reconnect_handler(void); void init_mdns_transport_discovery(void); -std::string list_transports(bool long_listing); #if ADB_HOST atransport* find_transport(const char* serial); @@ -519,7 +532,11 @@ void close_usb_devices(std::function predicate, bool re void send_packet(apacket* p, atransport* t); -asocket* create_device_tracker(bool long_output); +#if ADB_HOST +enum TrackerOutputType { SHORT_TEXT, LONG_TEXT, PROTOBUF, TEXT_PROTOBUF }; +asocket* create_device_tracker(TrackerOutputType type); +std::string list_transports(TrackerOutputType type); +#endif #if !ADB_HOST unique_fd adb_listen(std::string_view addr, std::string* error); -- cgit v1.2.3