class AdbPlug(EZBasePlug): ''' Communicate with DUT using adb protocol via USB ''' def __init__(self): super().__init__() subclass_name = self.get_subclass_name() plug_config = self.get_plug_config() # set defaults here defaults = { 'name': subclass_name, 'rsa_key_path': None, 'serial': None } # override defaults with plug config from station_config.yaml self._settings = dict(defaults, **plug_config) self.connected = False self.priv = None self.signer = None # check if adb server is running. If it is, terminate it. for proc in psutil.process_iter(): if proc.name() == 'adb': proc.terminate() time.sleep(1) break if self._settings['rsa_key_path'] == 'default': self._settings['rsa_key_path'] = os.path.expanduser( '~/.android/adbkey') if self._settings['rsa_key_path']: with open(self._settings['rsa_key_path']) as f: self.priv = f.read() self.signer = [PythonRSASigner('', self.priv)] self.device = AdbDeviceUsb(self._settings['serial']) self._connect() assert self.connected, "Unable to connect to Adb Usb device" def _connect(self): self.connected = self.device.connect(rsa_keys=self.signer, auth_timeout_s=0.1) def _close(self): self.device.close() self.connected = False def command(self, cmd): response = self.device.shell(cmd) return response Shell = command def tearDown(self): if self.connected: self._close()
class ADBPythonSync(object): """A manager for ADB connections that uses a Python implementation of the ADB protocol. Parameters ---------- host : str The address of the device; may be an IP address or a host name port : int The device port to which we are connecting (default is 5555) adbkey : str The path to the ``adbkey`` file for ADB authentication signer : PythonRSASigner, None The signer for the ADB keys, as loaded by :meth:`ADBPythonSync.load_adbkey` """ def __init__(self, host, port, adbkey="", signer=None): self.host = host self.port = int(port) self.adbkey = adbkey if host: self._adb = AdbDeviceTcp( host=self.host, port=self.port, default_transport_timeout_s=DEFAULT_ADB_TIMEOUT_S) else: self._adb = AdbDeviceUsb( default_transport_timeout_s=DEFAULT_ADB_TIMEOUT_S) self._signer = signer # use a lock to make sure that ADB commands don't overlap self._adb_lock = threading.Lock() @property def available(self): """Check whether the ADB connection is intact. Returns ------- bool Whether or not the ADB connection is intact """ return self._adb.available def close(self): """Close the ADB socket connection.""" self._adb.close() def connect( self, log_errors=True, auth_timeout_s=DEFAULT_AUTH_TIMEOUT_S, transport_timeout_s=DEFAULT_TRANSPORT_TIMEOUT_S, ): """Connect to an Android TV / Fire TV device. Parameters ---------- log_errors : bool Whether errors should be logged auth_timeout_s : float Authentication timeout (in seconds) transport_timeout_s : float Transport timeout (in seconds) Returns ------- bool Whether or not the connection was successfully established and the device is available """ try: with _acquire(self._adb_lock): # Catch exceptions try: # Connect with authentication if self.adbkey: if not self._signer: self._signer = self.load_adbkey(self.adbkey) self._adb.connect( rsa_keys=[self._signer], transport_timeout_s=transport_timeout_s, auth_timeout_s=auth_timeout_s, ) # Connect without authentication else: self._adb.connect( transport_timeout_s=transport_timeout_s, auth_timeout_s=auth_timeout_s) # ADB connection successfully established _LOGGER.debug( "ADB connection to %s:%d successfully established", self.host, self.port) return True except OSError as exc: if log_errors: if exc.strerror is None: exc.strerror = "Timed out trying to connect to ADB device." _LOGGER.warning( "Couldn't connect to %s:%d. %s: %s", self.host, self.port, exc.__class__.__name__, exc.strerror, ) # ADB connection attempt failed self.close() return False except Exception as exc: # pylint: disable=broad-except if log_errors: _LOGGER.warning("Couldn't connect to %s:%d. %s: %s", self.host, self.port, exc.__class__.__name__, exc) # ADB connection attempt failed self.close() return False except LockNotAcquiredException: _LOGGER.warning( "Couldn't connect to %s:%d because adb-shell lock not acquired.", self.host, self.port) self.close() return False @staticmethod def load_adbkey(adbkey): """Load the ADB keys. Parameters ---------- adbkey : str The path to the ``adbkey`` file for ADB authentication Returns ------- PythonRSASigner The ``PythonRSASigner`` with the key files loaded """ # private key with open(adbkey) as f: priv = f.read() # public key try: with open(adbkey + ".pub") as f: pub = f.read() except FileNotFoundError: pub = "" return PythonRSASigner(pub, priv) def pull(self, local_path, device_path): """Pull a file from the device using the Python ADB implementation. Parameters ---------- local_path : str The path where the file will be saved device_path : str The file on the device that will be pulled """ if not self.available: _LOGGER.debug( "ADB command not sent to %s:%d because adb-shell connection is not established: pull(%s, %s)", self.host, self.port, local_path, device_path, ) return with _acquire(self._adb_lock): _LOGGER.debug( "Sending command to %s:%d via adb-shell: pull(%s, %s)", self.host, self.port, local_path, device_path) self._adb.pull(device_path, local_path) return def push(self, local_path, device_path): """Push a file to the device using the Python ADB implementation. Parameters ---------- local_path : str The file that will be pushed to the device device_path : str The path where the file will be saved on the device """ if not self.available: _LOGGER.debug( "ADB command not sent to %s:%d because adb-shell connection is not established: push(%s, %s)", self.host, self.port, local_path, device_path, ) return with _acquire(self._adb_lock): _LOGGER.debug( "Sending command to %s:%d via adb-shell: push(%s, %s)", self.host, self.port, local_path, device_path) self._adb.push(local_path, device_path) return def screencap(self): """Take a screenshot using the Python ADB implementation. Returns ------- bytes The screencap as a binary .png image """ if not self.available: _LOGGER.debug( "ADB screencap not taken from %s:%d because adb-shell connection is not established", self.host, self.port, ) return None with _acquire(self._adb_lock): _LOGGER.debug("Taking screencap from %s:%d via adb-shell", self.host, self.port) result = self._adb.shell("screencap -p", decode=False) if result and result[5:6] == b"\r": return result.replace(b"\r\n", b"\n") return result def shell(self, cmd): """Send an ADB command using the Python ADB implementation. Parameters ---------- cmd : str The ADB command to be sent Returns ------- str, None The response from the device, if there is a response """ if not self.available: _LOGGER.debug( "ADB command not sent to %s:%d because adb-shell connection is not established: %s", self.host, self.port, cmd, ) return None with _acquire(self._adb_lock): _LOGGER.debug("Sending command to %s:%d via adb-shell: %s", self.host, self.port, cmd) return self._adb.shell(cmd)