class AdbConnection: device = None TMP_PATH = '/data/local/tmp' server = None port = 0 signer = None connected = False apks = None def __init__(self, server, port, signer=None): self.server = server self.port = port self.signer = signer def connect(self): self.device = AdbDeviceTcp(self.server, self.port) try: if self.signer: self.device.connect(rsa_keys=[self.signer], timeout_s=10.0) else: self.device.connect(timeout_s=10.0) except OSError as e: logger.error("OSError: {}".format(e)) raise AdbConnectionError(e) except (DeviceAuthError, TcpTimeoutException) as e: logger.error("Could not authenticate to the ADB server: {}".format(e)) raise AdbConnectionError(e) def install_apk(self, apk): apk.progress.set_fraction(0.0) apk.info('Copying...') # Copy the APK to the target dst_filename = ''.join(random.choice(string.ascii_lowercase) for i in range(16)) + '.apk' dst_path = os.path.join(AdbConnection.TMP_PATH, dst_filename) logger.debug("dst_path: {}".format(dst_path)) self.device.push(apk.src_path, dst_path, total_timeout_s=10.0, progress_callback=apk.update_progress) apk.progress.set_fraction(1.0) apk.info('Installing...') # Install the APK on the target try: output = self.device.shell('pm install -r {}'.format(dst_path)) except Exception as e: logger.error("Installation failed: {}".format(e)) raise if output != "Success\n": apk.fail(output.split('\n')[0]) else: apk.succeed()
class ADBPython(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 """ def __init__(self, host, port, adbkey=''): self.host = host self.port = int(port) self.adbkey = adbkey self._adb = AdbDeviceTcp(host=self.host, port=self.port, default_timeout_s=9.) # keep track of whether the ADB connection is intact self._available = False # 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, always_log_errors=True, auth_timeout_s=DEFAULT_AUTH_TIMEOUT_S): """Connect to an Android TV / Fire TV device. Parameters ---------- always_log_errors : bool If True, errors will always be logged; otherwise, errors will only be logged on the first failed reconnect attempt auth_timeout_s : float Authentication 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: # private key with open(self.adbkey) as f: priv = f.read() # public key try: with open(self.adbkey + '.pub') as f: pub = f.read() except FileNotFoundError: pub = '' signer = PythonRSASigner(pub, priv) self._adb.connect(rsa_keys=[signer], auth_timeout_s=auth_timeout_s) # Connect without authentication else: self._adb.connect(auth_timeout_s=auth_timeout_s) # ADB connection successfully established _LOGGER.debug( "ADB connection to %s:%d successfully established", self.host, self.port) self._available = True return True except OSError as exc: if self._available or always_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() self._available = False return False except Exception as exc: # pylint: disable=broad-except if self._available or always_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() self._available = False return False except LockNotAcquiredException: _LOGGER.warning( "Couldn't connect to %s:%d because adb-shell lock not acquired.", self.host, self.port) self.close() self._available = False return False 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 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)