Skip to content
This repository has been archived by the owner on Jan 10, 2023. It is now read-only.

More robust handling of duplicate CLOSE commands from device. #151

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 31 additions & 12 deletions adb/adb_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ def ReadUntil(self, *expected_cmds):
self.usb, expected_cmds, self.timeout_ms)
if local_id != 0 and self.local_id != local_id:
raise InterleavedDataError("We don't support multiple streams...")
if remote_id != 0 and self.remote_id != remote_id:
if remote_id != 0 and self.remote_id != remote_id and cmd != b'CLSE':
raise InvalidResponseError(
'Incorrect remote id, expected %s got %s' % (
self.remote_id, remote_id))
Expand Down Expand Up @@ -185,6 +185,7 @@ class AdbMessage(object):
format = b'<6I'

connections = 0
_local_id = 1

def __init__(self, command=None, arg0=None, arg1=None, data=b''):
self.command = self.commands[command]
Expand Down Expand Up @@ -347,6 +348,18 @@ def Connect(cls, usb, banner=b'notadb', rsa_keys=None, auth_timeout_ms=100):
return banner
return banner

@classmethod
def _NextLocalId(cls):
next_id = cls._local_id
cls._local_id += 1
# ADB message arguments are 32-bit words
# Not very likely to reach this point but wrap back around to 1
# just in case:
if cls._local_id >= 2**32:
cls._local_id = 1

return next_id

@classmethod
def Open(cls, usb, destination, timeout_ms=None):
"""Opens a new connection to the device via an OPEN message.
Expand All @@ -365,23 +378,26 @@ def Open(cls, usb, destination, timeout_ms=None):
Returns:
The local connection id.
"""
local_id = 1
local_id = cls._NextLocalId()
msg = cls(
command=b'OPEN', arg0=local_id, arg1=0,
data=destination + b'\0')
msg.Send(usb, timeout_ms)
cmd, remote_id, their_local_id, _ = cls.Read(usb, [b'CLSE', b'OKAY'],
timeout_ms=timeout_ms)
if local_id != their_local_id:
raise InvalidResponseError(
'Expected the local_id to be {}, got {}'.format(local_id, their_local_id))
if cmd == b'CLSE':
# Some devices seem to be sending CLSE once more after a request, this *should* handle it
while True:
cmd, remote_id, their_local_id, _ = cls.Read(usb, [b'CLSE', b'OKAY'],
timeout_ms=timeout_ms)
# Device doesn't support this service.
if cmd == b'CLSE':
return None
if local_id != their_local_id:
if cmd != b'CLSE':
raise InvalidResponseError(
'Expected the local_id to be {}, got {}'.format(local_id, their_local_id))
continue

break

# Device doesn't support this service.
if cmd == b'CLSE':
return None

if cmd != b'OKAY':
raise InvalidCommandError('Expected a ready response, got {}'.format(cmd),
cmd, (remote_id, their_local_id))
Expand Down Expand Up @@ -436,6 +452,9 @@ def StreamingCommand(cls, usb, service, command='', timeout_ms=None):
connection = cls.Open(
usb, destination=b'%s:%s' % (service, command),
timeout_ms=timeout_ms)
if connection is None:
raise usb_exceptions.AdbCommandFailureException(
'Command failed: Device immediately closed the stream.')
for data in connection.ReadUntilClose():
yield data.decode('utf8')

Expand Down
5 changes: 5 additions & 0 deletions test/adb_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@


class BaseAdbTest(unittest.TestCase):
def setUp(self):
# Ensure that the next local-id used by the AdbMessage class
# is reset back to LOCAL_ID to account for the changes in
# https://github.com/google/python-adb/pull/151
adb_protocol.AdbMessage._local_id = LOCAL_ID

@classmethod
def _ExpectWrite(cls, usb, command, arg0, arg1, data):
Expand Down