Beispiel #1
0
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()
Beispiel #2
0
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)