Loading adb/test_adb.py +47 −6 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import socket import struct import subprocess import threading import time import unittest Loading Loading @@ -90,7 +91,7 @@ def fake_adbd(protocol=socket.AF_INET, port=0): server_thread.start() try: yield port yield port, writesock finally: writesock.close() server_thread.join() Loading Loading @@ -120,7 +121,7 @@ def adb_connect(unittest, serial): def adb_server(): """Context manager for an ADB server. This creates an ADB server and returns the port it"s listening on. This creates an ADB server and returns the port it's listening on. """ port = 5038 Loading Loading @@ -342,7 +343,7 @@ class EmulatorTest(unittest.TestCase): Bug: http://b/78991667 """ with adb_server() as server_port: with fake_adbd() as port: with fake_adbd() as (port, _): serial = "emulator-{}".format(port - 1) # Ensure that the emulator is not there. try: Loading Loading @@ -380,7 +381,7 @@ class ConnectionTest(unittest.TestCase): """ for protocol in (socket.AF_INET, socket.AF_INET6): try: with fake_adbd(protocol=protocol) as port: with fake_adbd(protocol=protocol) as (port, _): serial = "localhost:{}".format(port) with adb_connect(self, serial): pass Loading @@ -391,7 +392,7 @@ class ConnectionTest(unittest.TestCase): def test_already_connected(self): """Ensure that an already-connected device stays connected.""" with fake_adbd() as port: with fake_adbd() as (port, _): serial = "localhost:{}".format(port) with adb_connect(self, serial): # b/31250450: this always returns 0 but probably shouldn't. Loading @@ -403,7 +404,7 @@ class ConnectionTest(unittest.TestCase): def test_reconnect(self): """Ensure that a disconnected device reconnects.""" with fake_adbd() as port: with fake_adbd() as (port, _): serial = "localhost:{}".format(port) with adb_connect(self, serial): output = subprocess.check_output(["adb", "-s", serial, Loading Loading @@ -439,6 +440,46 @@ class ConnectionTest(unittest.TestCase): "error: device '{}' not found".format(serial).encode("utf8")) class DisconnectionTest(unittest.TestCase): """Tests for adb disconnect.""" def test_disconnect(self): """Ensure that `adb disconnect` takes effect immediately.""" 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:]] with adb_server() as server_port: with fake_adbd() as (port, sock): device_name = "localhost:{}".format(port) output = subprocess.check_output(["adb", "-P", str(server_port), "connect", device_name]) self.assertEqual(output.strip(), "connected to {}".format(device_name).encode("utf8")) self.assertEqual(_devices(server_port), [[device_name, "device"]]) # Send a deliberately malformed packet to make the device go offline. packet = struct.pack("IIIIII", 0, 0, 0, 0, 0, 0) sock.sendall(packet) # Wait a bit. time.sleep(0.1) self.assertEqual(_devices(server_port), [[device_name, "offline"]]) # Disconnect the device. output = subprocess.check_output(["adb", "-P", str(server_port), "disconnect", device_name]) # Wait a bit. time.sleep(0.1) self.assertEqual(_devices(server_port), []) def main(): """Main entrypoint.""" random.seed(0) Loading adb/transport.cpp +31 −7 Original line number Diff line number Diff line Loading @@ -97,6 +97,9 @@ class ReconnectHandler { // Adds the atransport* to the queue of reconnect attempts. void TrackTransport(atransport* transport); // Wake up the ReconnectHandler thread to have it check for kicked transports. void CheckForKicked(); private: // The main thread loop. void Run(); Loading Loading @@ -166,6 +169,10 @@ void ReconnectHandler::TrackTransport(atransport* transport) { reconnect_cv_.notify_one(); } void ReconnectHandler::CheckForKicked() { reconnect_cv_.notify_one(); } void ReconnectHandler::Run() { while (true) { ReconnectAttempt attempt; Loading @@ -184,10 +191,25 @@ void ReconnectHandler::Run() { } if (!running_) return; // Scan the whole list for kicked transports, so that we immediately handle an explicit // disconnect request. bool kicked = false; for (auto it = reconnect_queue_.begin(); it != reconnect_queue_.end();) { if (it->transport->kicked()) { D("transport %s was kicked. giving up on it.", it->transport->serial.c_str()); remove_transport(it->transport); it = reconnect_queue_.erase(it); } else { ++it; } kicked = true; } if (reconnect_queue_.empty()) continue; // Go back to sleep in case |reconnect_cv_| woke up spuriously and we still // have more time to wait for the current attempt. // Go back to sleep if we either woke up spuriously, or we were woken up to remove // a kicked transport, and the first transport isn't ready for reconnection yet. auto now = std::chrono::steady_clock::now(); if (reconnect_queue_.begin()->reconnect_time > now) { continue; Loading @@ -195,11 +217,6 @@ void ReconnectHandler::Run() { attempt = *reconnect_queue_.begin(); reconnect_queue_.erase(reconnect_queue_.begin()); if (attempt.transport->kicked()) { D("transport %s was kicked. giving up on it.", attempt.transport->serial.c_str()); remove_transport(attempt.transport); continue; } } D("attempting to reconnect %s", attempt.transport->serial.c_str()); Loading Loading @@ -448,6 +465,10 @@ void kick_transport(atransport* t) { if (std::find(transport_list.begin(), transport_list.end(), t) != transport_list.end()) { t->Kick(); } #if ADB_HOST reconnect_handler.CheckForKicked(); #endif } static int transport_registration_send = -1; Loading Loading @@ -1276,6 +1297,9 @@ void kick_all_tcp_devices() { t->Kick(); } } #if ADB_HOST reconnect_handler.CheckForKicked(); #endif } #endif Loading Loading
adb/test_adb.py +47 −6 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import socket import struct import subprocess import threading import time import unittest Loading Loading @@ -90,7 +91,7 @@ def fake_adbd(protocol=socket.AF_INET, port=0): server_thread.start() try: yield port yield port, writesock finally: writesock.close() server_thread.join() Loading Loading @@ -120,7 +121,7 @@ def adb_connect(unittest, serial): def adb_server(): """Context manager for an ADB server. This creates an ADB server and returns the port it"s listening on. This creates an ADB server and returns the port it's listening on. """ port = 5038 Loading Loading @@ -342,7 +343,7 @@ class EmulatorTest(unittest.TestCase): Bug: http://b/78991667 """ with adb_server() as server_port: with fake_adbd() as port: with fake_adbd() as (port, _): serial = "emulator-{}".format(port - 1) # Ensure that the emulator is not there. try: Loading Loading @@ -380,7 +381,7 @@ class ConnectionTest(unittest.TestCase): """ for protocol in (socket.AF_INET, socket.AF_INET6): try: with fake_adbd(protocol=protocol) as port: with fake_adbd(protocol=protocol) as (port, _): serial = "localhost:{}".format(port) with adb_connect(self, serial): pass Loading @@ -391,7 +392,7 @@ class ConnectionTest(unittest.TestCase): def test_already_connected(self): """Ensure that an already-connected device stays connected.""" with fake_adbd() as port: with fake_adbd() as (port, _): serial = "localhost:{}".format(port) with adb_connect(self, serial): # b/31250450: this always returns 0 but probably shouldn't. Loading @@ -403,7 +404,7 @@ class ConnectionTest(unittest.TestCase): def test_reconnect(self): """Ensure that a disconnected device reconnects.""" with fake_adbd() as port: with fake_adbd() as (port, _): serial = "localhost:{}".format(port) with adb_connect(self, serial): output = subprocess.check_output(["adb", "-s", serial, Loading Loading @@ -439,6 +440,46 @@ class ConnectionTest(unittest.TestCase): "error: device '{}' not found".format(serial).encode("utf8")) class DisconnectionTest(unittest.TestCase): """Tests for adb disconnect.""" def test_disconnect(self): """Ensure that `adb disconnect` takes effect immediately.""" 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:]] with adb_server() as server_port: with fake_adbd() as (port, sock): device_name = "localhost:{}".format(port) output = subprocess.check_output(["adb", "-P", str(server_port), "connect", device_name]) self.assertEqual(output.strip(), "connected to {}".format(device_name).encode("utf8")) self.assertEqual(_devices(server_port), [[device_name, "device"]]) # Send a deliberately malformed packet to make the device go offline. packet = struct.pack("IIIIII", 0, 0, 0, 0, 0, 0) sock.sendall(packet) # Wait a bit. time.sleep(0.1) self.assertEqual(_devices(server_port), [[device_name, "offline"]]) # Disconnect the device. output = subprocess.check_output(["adb", "-P", str(server_port), "disconnect", device_name]) # Wait a bit. time.sleep(0.1) self.assertEqual(_devices(server_port), []) def main(): """Main entrypoint.""" random.seed(0) Loading
adb/transport.cpp +31 −7 Original line number Diff line number Diff line Loading @@ -97,6 +97,9 @@ class ReconnectHandler { // Adds the atransport* to the queue of reconnect attempts. void TrackTransport(atransport* transport); // Wake up the ReconnectHandler thread to have it check for kicked transports. void CheckForKicked(); private: // The main thread loop. void Run(); Loading Loading @@ -166,6 +169,10 @@ void ReconnectHandler::TrackTransport(atransport* transport) { reconnect_cv_.notify_one(); } void ReconnectHandler::CheckForKicked() { reconnect_cv_.notify_one(); } void ReconnectHandler::Run() { while (true) { ReconnectAttempt attempt; Loading @@ -184,10 +191,25 @@ void ReconnectHandler::Run() { } if (!running_) return; // Scan the whole list for kicked transports, so that we immediately handle an explicit // disconnect request. bool kicked = false; for (auto it = reconnect_queue_.begin(); it != reconnect_queue_.end();) { if (it->transport->kicked()) { D("transport %s was kicked. giving up on it.", it->transport->serial.c_str()); remove_transport(it->transport); it = reconnect_queue_.erase(it); } else { ++it; } kicked = true; } if (reconnect_queue_.empty()) continue; // Go back to sleep in case |reconnect_cv_| woke up spuriously and we still // have more time to wait for the current attempt. // Go back to sleep if we either woke up spuriously, or we were woken up to remove // a kicked transport, and the first transport isn't ready for reconnection yet. auto now = std::chrono::steady_clock::now(); if (reconnect_queue_.begin()->reconnect_time > now) { continue; Loading @@ -195,11 +217,6 @@ void ReconnectHandler::Run() { attempt = *reconnect_queue_.begin(); reconnect_queue_.erase(reconnect_queue_.begin()); if (attempt.transport->kicked()) { D("transport %s was kicked. giving up on it.", attempt.transport->serial.c_str()); remove_transport(attempt.transport); continue; } } D("attempting to reconnect %s", attempt.transport->serial.c_str()); Loading Loading @@ -448,6 +465,10 @@ void kick_transport(atransport* t) { if (std::find(transport_list.begin(), transport_list.end(), t) != transport_list.end()) { t->Kick(); } #if ADB_HOST reconnect_handler.CheckForKicked(); #endif } static int transport_registration_send = -1; Loading Loading @@ -1276,6 +1297,9 @@ void kick_all_tcp_devices() { t->Kick(); } } #if ADB_HOST reconnect_handler.CheckForKicked(); #endif } #endif Loading