Skip to content

Commit 7cb87b6

Browse files
committed
Improved timouts and wait for devices to appear in ADB
- Escaped some regex chars
1 parent 4bb9b84 commit 7cb87b6

1 file changed

Lines changed: 99 additions & 36 deletions

File tree

src/com/dtmilano/android/adb/adbclient.py

Lines changed: 99 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
'''
1919
import threading
2020

21-
__version__ = '12.4.4'
21+
__version__ = '12.5.0'
2222

2323
import sys
2424
import warnings
@@ -38,7 +38,6 @@
3838
import socket
3939
import time
4040
import re
41-
import signal
4241
import os
4342
import platform
4443

@@ -142,7 +141,22 @@ def getWifiState(self):
142141
return self.WIFI_STATE_UNKNOWN
143142

144143

144+
class Timer():
145+
def __init__(self, timeout, handler, args):
146+
self.timer = threading.Timer(timeout, handler, args)
147+
148+
def start(self):
149+
self.timer.start()
150+
151+
def cancel(self):
152+
self.timer.cancel()
153+
154+
class TimeoutException(Exception):
155+
pass
156+
157+
145158
class AdbClient:
159+
146160
def __init__(self, serialno=None, hostname=HOSTNAME, port=PORT, settransport=True, reconnect=True,
147161
ignoreversioncheck=False, timeout=TIMEOUT):
148162
self.Log = AdbClient.__Log(self)
@@ -151,6 +165,8 @@ def __init__(self, serialno=None, hostname=HOSTNAME, port=PORT, settransport=Tru
151165
self.hostname = hostname
152166
self.port = port
153167
self.timeout = timeout
168+
self.timerId = -1
169+
self.timers = {}
154170

155171
self.reconnect = reconnect
156172
self.socket = AdbClient.connect(self.hostname, self.port, self.timeout)
@@ -168,26 +184,26 @@ def __init__(self, serialno=None, hostname=HOSTNAME, port=PORT, settransport=Tru
168184

169185
self.isTransportSet = False
170186
if settransport and serialno != None:
171-
self.__setTransport()
187+
self.__setTransport(timeout)
172188
self.build[VERSION_SDK_PROPERTY] = int(self.__getProp(VERSION_SDK_PROPERTY))
173189
self.initDisplayProperties()
174190

175-
@staticmethod
176-
def alarmHandler(signum, frame):
177-
if signum == signal.SIGALRM:
178-
raise IOError("Socket timeout")
179-
raise RuntimeError("Signal received: %d" % signum)
191+
def timeoutHandler(self, timerId):
192+
print >> sys.stderr, "TIMEOUT HANDLER", timerId
193+
self.timers[timerId] = "EXPIRED"
194+
raise Timer.TimeoutException("Timer %d has expired" % timerId)
180195

181-
@staticmethod
182-
def setAlarm(timeout):
183-
osName = platform.system()
184-
if osName.startswith('Windows'): # alarm is not implemented in Windows
185-
return
186-
if DEBUG:
187-
print >> sys.stderr, "setAlarm(%d)" % timeout
188-
if threading.current_thread().getName() == 'MainThread':
189-
signal.signal(signal.SIGALRM, AdbClient.alarmHandler)
190-
signal.alarm(timeout)
196+
def setTimer(self, timeout):
197+
self.timerId += 1
198+
timer = Timer(timeout, self.timeoutHandler, [self.timerId])
199+
timer.start()
200+
self.timers[self.timerId] = timer
201+
return self.timerId
202+
203+
def cancelTimer(self, timerId):
204+
if self.timers[timerId] != "EXPIRED":
205+
self.timers[timerId].cancel()
206+
del self.timers[timerId]
191207

192208
def setSerialno(self, serialno):
193209
if self.isTransportSet:
@@ -246,19 +262,21 @@ def __send(self, msg, checkok=True, reconnect=False):
246262
self.socket = AdbClient.connect(self.hostname, self.port, self.timeout)
247263
self.__setTransport()
248264

249-
def __receive(self, nob=None):
265+
def __receive(self, nob=None, sock=None):
250266
if DEBUG:
251-
print >> sys.stderr, "__receive()"
252-
self.checkConnected()
267+
print >> sys.stderr, "__receive(nob=%s)" % (nob)
268+
if not sock:
269+
sock = self.socket
270+
self.checkConnected(sock)
253271
if nob is None:
254-
nob = int(self.socket.recv(4), 16)
272+
nob = int(sock.recv(4), 16)
255273
if DEBUG:
256274
print >> sys.stderr, " __receive: receiving", nob, "bytes"
257275
recv = bytearray(nob)
258276
view = memoryview(recv)
259277
nr = 0
260278
while nr < nob:
261-
l = self.socket.recv_into(view, len(view))
279+
l = sock.recv_into(view, len(view))
262280
if DEBUG:
263281
print >> sys.stderr, "l=", l, "nr=", nr
264282
view = view[l:]
@@ -267,32 +285,36 @@ def __receive(self, nob=None):
267285
print >> sys.stderr, " __receive: returning len=", len(recv)
268286
return str(recv)
269287

270-
def __checkOk(self):
288+
def __checkOk(self, sock=None):
271289
if DEBUG:
272290
print >> sys.stderr, "__checkOk()"
273-
self.checkConnected()
274-
self.setAlarm(TIMEOUT)
275-
recv = self.socket.recv(4)
291+
if not sock:
292+
sock = self.socket
293+
self.checkConnected(sock=sock)
294+
timerId = self.setTimer(TIMEOUT)
295+
recv = sock.recv(4)
276296
if DEBUG:
277297
print >> sys.stderr, " __checkOk: recv=", repr(recv)
278298
try:
279299
if recv != OKAY:
280-
error = self.socket.recv(1024)
300+
error = sock.recv(1024)
281301
if error.startswith('0049'):
282302
raise RuntimeError(
283303
"ERROR: This computer is unauthorized. Please check the confirmation dialog on your device.")
284304
else:
285305
raise RuntimeError("ERROR: %s %s" % (repr(recv), error))
286306
finally:
287-
self.setAlarm(0)
307+
self.cancelTimer(timerId)
288308
if DEBUG:
289309
print >> sys.stderr, " __checkOk: returning True"
290310
return True
291311

292-
def checkConnected(self):
312+
def checkConnected(self, sock=None):
293313
if DEBUG:
294314
print >> sys.stderr, "checkConnected()"
295-
if not self.socket:
315+
if not sock:
316+
sock = self.socket
317+
if not sock:
296318
raise RuntimeError("ERROR: Not connected")
297319
if DEBUG:
298320
print >> sys.stderr, " checkConnected: returning True"
@@ -314,7 +336,7 @@ def checkVersion(self, ignoreversioncheck=False, reconnect=True):
314336
if reconnect:
315337
self.socket = AdbClient.connect(self.hostname, self.port, self.timeout)
316338

317-
def __setTransport(self):
339+
def __setTransport(self, timeout=60):
318340
if DEBUG:
319341
print >> sys.stderr, "__setTransport()"
320342
if not self.serialno:
@@ -323,6 +345,47 @@ def __setTransport(self):
323345
serialnoRE = re.compile(self.serialno)
324346
found = False
325347
devices = self.getDevices()
348+
if len(devices) == 0 and timeout > 0:
349+
print >> sys.stderr, "Empty device list, will wait %s secs for devices to appear" % self.timeout
350+
# Sets the timeout to 5 to be able to loop while trying to receive new devices being added
351+
_s = AdbClient.connect(self.hostname, self.port, timeout=5)
352+
msg = 'host:track-devices'
353+
b = bytearray(msg, 'utf-8')
354+
try:
355+
timerId = self.setTimer(timeout=timeout)
356+
_s.send('%04X%s' % (len(b), b))
357+
self.__checkOk(sock=_s)
358+
# eat '0000'
359+
_s.recv(4)
360+
found = False
361+
while not found:
362+
sys.stderr.write(".")
363+
sys.stderr.flush()
364+
try:
365+
for line in _s.recv(1024).splitlines():
366+
# skip first 4 bytes containing the response size
367+
device = Device.factory(line[4:])
368+
if device.status == 'device':
369+
devices.append(device)
370+
found = True
371+
break
372+
if found:
373+
break
374+
except socket.timeout as ex:
375+
# we continue trying until timer times out
376+
pass
377+
finally:
378+
time.sleep(3)
379+
if self.timers[timerId] == "EXPIRED":
380+
break
381+
self.cancelTimer(timerId)
382+
except Timer.TimeoutException as ex:
383+
print >> sys.stderr, "EXCEPTION", ex
384+
pass
385+
finally:
386+
_s.close()
387+
sys.stderr.write("\n")
388+
sys.stderr.flush()
326389
if len(devices) == 0:
327390
raise RuntimeError("ERROR: There are no connected devices")
328391
for device in devices:
@@ -344,7 +407,7 @@ def __checkTransport(self):
344407

345408
def __readExactly(self, sock, size):
346409
if DEBUG:
347-
print >> sys.stderr, "__readExactly(socket=%s, size=%d)" % (socket, size)
410+
print >> sys.stderr, "__readExactly(socket=%s, size=%d)" % (sock, size)
348411
_buffer = bytearray(size)
349412
view = memoryview(_buffer)
350413
nb = 0
@@ -428,7 +491,7 @@ def getLogicalDisplayInfo(self):
428491

429492
self.__checkTransport()
430493
logicalDisplayRE = re.compile(
431-
'.*DisplayViewport{valid=true, .*orientation=(?P<orientation>\d+), .*deviceWidth=(?P<width>\d+), deviceHeight=(?P<height>\d+).*')
494+
'.*DisplayViewport\{valid=true, .*orientation=(?P<orientation>\d+), .*deviceWidth=(?P<width>\d+), deviceHeight=(?P<height>\d+).*')
432495
for line in self.shell('dumpsys display').splitlines():
433496
m = logicalDisplayRE.search(line, 0)
434497
if m:
@@ -993,9 +1056,9 @@ def getWindows(self):
9931056
dww = self.shell('dumpsys window windows')
9941057
if DEBUG_WINDOWS: print >> sys.stderr, dww
9951058
lines = dww.splitlines()
996-
widRE = re.compile('^ *Window #%s Window{%s (u\d+ )?%s?.*}:' %
1059+
widRE = re.compile('^ *Window #%s Window\{%s (u\d+ )?%s?.*\}:' %
9971060
(_nd('num'), _nh('winId'), _ns('activity', greedy=True)))
998-
currentFocusRE = re.compile('^ mCurrentFocus=Window{%s .*' % _nh('winId'))
1061+
currentFocusRE = re.compile('^ mCurrentFocus=Window\{%s .*' % _nh('winId'))
9991062
viewVisibilityRE = re.compile(' mViewVisibility=0x%s ' % _nh('visibility'))
10001063
# This is for 4.0.4 API-15
10011064
containingFrameRE = re.compile('^ *mContainingFrame=\[%s,%s\]\[%s,%s\] mParentFrame=\[%s,%s\]\[%s,%s\]' %

0 commit comments

Comments
 (0)