1818'''
1919import threading
2020
21- __version__ = '12.4.4 '
21+ __version__ = '12.5.0 '
2222
2323import sys
2424import warnings
3838import socket
3939import time
4040import re
41- import signal
4241import os
4342import 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+
145158class 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