def test_init(self): device_with_banner = AdbDevice('IP:5555', 'banner') self.assertEqual(device_with_banner._banner, 'banner') with patch('socket.gethostname', side_effect=Exception): device_banner_unknown = AdbDevice('IP:5555') self.assertEqual(device_banner_unknown._banner, 'unknown') self.device._handle._bulk_read = b''
def __init__(self, host, adbkey=''): self.host = host self.adbkey = adbkey self._adb = AdbDevice(serial=self.host, 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()
def test_init_banner(self): device_with_banner = AdbDevice(handle=patchers.FakeTcpHandle('host', 5555), banner='banner') self.assertEqual(device_with_banner._banner, b'banner') device_with_banner2 = AdbDevice(handle=patchers.FakeTcpHandle('host', 5555), banner=bytearray('banner2', 'utf-8')) self.assertEqual(device_with_banner2._banner, b'banner2') device_with_banner3 = AdbDevice(handle=patchers.FakeTcpHandle('host', 5555), banner=u'banner3') self.assertEqual(device_with_banner3._banner, b'banner3') with patch('socket.gethostname', side_effect=Exception): device_banner_unknown = AdbDevice(handle=patchers.FakeTcpHandle('host', 5555)) self.assertEqual(device_banner_unknown._banner, b'unknown') # Clear the `_bulk_read` buffer so that `self.tearDown()` passes self.device._handle._bulk_read = b''
def _run_in_reactor(self, func, *args, **kwargs): handle = self._connect_adb() adb = AdbDevice(handle) def auth_cb(device): self._awaiting_auth = True self._logger.info( "Waiting for connection to be accepted on the device..") def on_running(): try: signer = RSA_SIGNER(self._adbkey) if adb.connect(rsa_keys=[signer], auth_timeout_s=30, timeout_s=10, auth_callback=auth_cb): func(adb, *args, **kwargs) except WSHandleShutdown: pass except TcpTimeoutException: if self._awaiting_auth: self._logger.error( "Connection was not accepted on the device " "within 30 seconds.") else: self._logger.error("Connection to the device timed out") except Exception as exc: self._logger.debug(exc, exc_info=True) if len(exc.args) >= 1 and type(exc.args[0]) is bytearray: err = str(exc.args[0].decode('utf-8')) else: err = str(exc) if 'Read-only file system' in err: self._logger.error('Permission denied.') else: self._logger.error(err) finally: adb.close() return handle.run(on_running)
def connect(self, always_log_errors=True): """Connect to an Amazon Fire TV device. Will attempt to establish ADB connection to the given host. Failure sets state to UNKNOWN and disables sending actions. :returns: True if successful, False otherwise """ self._adb_lock.acquire(**LOCK_KWARGS) signer = None if self.adbkey: signer = Signer(self.adbkey) try: if USE_ADB_SHELL: # adb_shell self._adb_device = AdbDevice(serial=self.host) # Connect to the device connected = False if signer: connected = self._adb_device.connect(rsa_keys=[signer]) else: connected = self._adb_device.connect() self._available = connected elif not self.adb_server_ip: # python-adb try: if self.adbkey: signer = Signer(self.adbkey) # Connect to the device self._adb = adb_commands.AdbCommands().ConnectDevice( serial=self.host, rsa_keys=[signer], default_timeout_ms=9000) else: self._adb = adb_commands.AdbCommands().ConnectDevice( serial=self.host, default_timeout_ms=9000) # ADB connection successfully established self._available = True except socket_error as serr: if self._available or always_log_errors: if serr.strerror is None: serr.strerror = "Timed out trying to connect to ADB device." logging.warning( "Couldn't connect to host: %s, error: %s", self.host, serr.strerror) # ADB connection attempt failed self._adb = None self._available = False finally: return self._available else: # pure-python-adb try: self._adb_client = AdbClient(host=self.adb_server_ip, port=self.adb_server_port) self._adb_device = self._adb_client.device(self.host) self._available = bool(self._adb_device) except: self._available = False finally: return self._available finally: self._adb_lock.release()
class FireTV: """Represents an Amazon Fire TV device.""" def __init__(self, host, adbkey='', adb_server_ip='', adb_server_port=5037): """Initialize FireTV object. :param host: Host in format <address>:port. :param adbkey: The path to the "adbkey" file :param adb_server_ip: the IP address for the ADB server :param adb_server_port: the port for the ADB server """ self.host = host self.adbkey = adbkey self.adb_server_ip = adb_server_ip self.adb_server_port = adb_server_port # 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() # the attributes used for sending ADB commands; filled in in `self.connect()` self._adb = None # python-adb self._adb_client = None # pure-python-adb self._adb_device = None # pure-python-adb && adb_shell # the methods used for sending ADB commands if USE_ADB_SHELL: # adb_shell self.adb_shell = self._adb_shell_adb_shell self.adb_streaming_shell = self._adb_shell_adb_shell elif not self.adb_server_ip: # python-adb self.adb_shell = self._adb_shell_python_adb self.adb_streaming_shell = self._adb_streaming_shell_python_adb else: # pure-python-adb self.adb_shell = self._adb_shell_pure_python_adb self.adb_streaming_shell = self._adb_streaming_shell_pure_python_adb # establish the ADB connection self.connect() # ======================================================================= # # # # ADB methods # # # # ======================================================================= # def _adb_shell_adb_shell(self, cmd): if not self.available: return None if self._adb_lock.acquire(**LOCK_KWARGS): try: return self._adb_device.shell(cmd) finally: self._adb_lock.release() def _adb_shell_python_adb(self, cmd): if not self.available: return None if self._adb_lock.acquire(**LOCK_KWARGS): try: return self._adb.Shell(cmd) finally: self._adb_lock.release() def _adb_shell_pure_python_adb(self, cmd): if not self._available: return None if self._adb_lock.acquire(**LOCK_KWARGS): try: return self._adb_device.shell(cmd) finally: self._adb_lock.release() def _adb_streaming_shell_adb_shell(self, cmd): if not self.available: return [] if self._adb_lock.acquire(**LOCK_KWARGS): try: return self._adb_device.shell(cmd) finally: self._adb_lock.release() def _adb_streaming_shell_python_adb(self, cmd): if not self.available: return [] if self._adb_lock.acquire(**LOCK_KWARGS): try: return self._adb.StreamingShell(cmd) finally: self._adb_lock.release() def _adb_streaming_shell_pure_python_adb(self, cmd): if not self._available: return None # this is not yet implemented if self._adb_lock.acquire(**LOCK_KWARGS): try: return [] finally: self._adb_lock.release() def _dump(self, service, grep=None): """Perform a service dump. :param service: Service to dump. :param grep: Grep for this string. :returns: Dump, optionally grepped. """ if grep: return self.adb_shell('dumpsys {0} | grep "{1}"'.format( service, grep)) return self.adb_shell('dumpsys {0}'.format(service)) def _dump_has(self, service, grep, search): """Check if a dump has particular content. :param service: Service to dump. :param grep: Grep for this string. :param search: Check for this substring. :returns: Found or not. """ dump_grep = self._dump(service, grep=grep) if not dump_grep: return False return dump_grep.strip().find(search) > -1 def _key(self, key): """Send a key event to device. :param key: Key constant. """ self.adb_shell('input keyevent {0}'.format(key)) def _ps(self, search=''): """Perform a ps command with optional filtering. :param search: Check for this substring. :returns: List of matching fields """ if not self.available: return result = [] ps = self.adb_streaming_shell('ps') try: for bad_line in ps: # The splitting of the StreamingShell doesn't always work # this is to ensure that we get only one line for line in bad_line.splitlines(): if search in line: result.append(line.strip().rsplit(' ', 1)[-1]) return result except InvalidChecksumError as e: print(e) self.connect() raise IOError def _send_intent(self, pkg, intent, count=1): cmd = 'monkey -p {} -c {} {}; echo $?'.format(pkg, intent, count) logging.debug("Sending an intent %s to %s (count: %s)", intent, pkg, count) # adb shell outputs in weird format, so we cut it into lines, # separate the retcode and return info to the user res = self.adb_shell(cmd) if res is None: return {} res = res.strip().split("\r\n") retcode = res[-1] output = "\n".join(res[:-1]) return {"retcode": retcode, "output": output} def connect(self, always_log_errors=True): """Connect to an Amazon Fire TV device. Will attempt to establish ADB connection to the given host. Failure sets state to UNKNOWN and disables sending actions. :returns: True if successful, False otherwise """ self._adb_lock.acquire(**LOCK_KWARGS) signer = None if self.adbkey: signer = Signer(self.adbkey) try: if USE_ADB_SHELL: # adb_shell self._adb_device = AdbDevice(serial=self.host) # Connect to the device connected = False if signer: connected = self._adb_device.connect(rsa_keys=[signer]) else: connected = self._adb_device.connect() self._available = connected elif not self.adb_server_ip: # python-adb try: if self.adbkey: signer = Signer(self.adbkey) # Connect to the device self._adb = adb_commands.AdbCommands().ConnectDevice( serial=self.host, rsa_keys=[signer], default_timeout_ms=9000) else: self._adb = adb_commands.AdbCommands().ConnectDevice( serial=self.host, default_timeout_ms=9000) # ADB connection successfully established self._available = True except socket_error as serr: if self._available or always_log_errors: if serr.strerror is None: serr.strerror = "Timed out trying to connect to ADB device." logging.warning( "Couldn't connect to host: %s, error: %s", self.host, serr.strerror) # ADB connection attempt failed self._adb = None self._available = False finally: return self._available else: # pure-python-adb try: self._adb_client = AdbClient(host=self.adb_server_ip, port=self.adb_server_port) self._adb_device = self._adb_client.device(self.host) self._available = bool(self._adb_device) except: self._available = False finally: return self._available finally: self._adb_lock.release() # ======================================================================= # # # # Home Assistant Update # # # # ======================================================================= # def update(self, get_running_apps=True): """Get the state of the device, the current app, and the running apps. :param get_running_apps: whether or not to get the ``running_apps`` property :return state: the state of the device :return current_app: the current app :return running_apps: the running apps """ # The `screen_on`, `awake`, `wake_lock_size`, `current_app`, and `running_apps` properties. screen_on, awake, wake_lock_size, _current_app, running_apps = self.get_properties( get_running_apps=get_running_apps, lazy=True) # Check if device is off. if not screen_on: state = STATE_OFF current_app = None running_apps = None # Check if screen saver is on. elif not awake: state = STATE_IDLE current_app = None running_apps = None else: # Get the current app. if isinstance(_current_app, dict) and 'package' in _current_app: current_app = _current_app['package'] else: current_app = None # Get the running apps. if running_apps is None and current_app: running_apps = [current_app] # Get the state. # TODO: determine the state differently based on the `current_app`. if current_app in [PACKAGE_LAUNCHER, PACKAGE_SETTINGS]: state = STATE_STANDBY # Amazon Video elif current_app == AMAZON_VIDEO: if wake_lock_size == 5: state = STATE_PLAYING else: # wake_lock_size == 2 state = STATE_PAUSED # Netflix elif current_app == NETFLIX: if wake_lock_size > 3: state = STATE_PLAYING else: state = STATE_PAUSED # Check if `wake_lock_size` is 1 (device is playing). elif wake_lock_size == 1: state = STATE_PLAYING # Otherwise, device is paused. else: state = STATE_PAUSED return state, current_app, running_apps # ======================================================================= # # # # App methods # # # # ======================================================================= # def app_state(self, app): """Informs if application is running.""" if not self.available or not self.screen_on: return STATE_OFF if self.current_app["package"] == app: return STATE_ON return STATE_OFF def launch_app(self, app): """Launch an app.""" return self._send_intent(app, INTENT_LAUNCH) def stop_app(self, app): """Stop an app.""" return self.adb_shell("am force-stop {0}".format(app)) # ======================================================================= # # # # properties # # # # ======================================================================= # @property def state(self): """Compute and return the device state. :returns: Device state. """ # Check if device is disconnected. if not self.available: return STATE_UNKNOWN # Check if device is off. if not self.screen_on: return STATE_OFF # Check if screen saver is on. if not self.awake: return STATE_IDLE # Check if the launcher is active. if self.launcher or self.settings: return STATE_STANDBY # Check for a wake lock (device is playing). if self.wake_lock: return STATE_PLAYING # Otherwise, device is paused. return STATE_PAUSED @property def available(self): """Check whether the ADB connection is intact.""" if USE_ADB_SHELL: # adb_shell if not self._adb_device: return False return self._adb_device.available if not self.adb_server_ip: # python-adb return bool(self._adb) # pure-python-adb try: # make sure the server is available adb_devices = self._adb_client.devices() # make sure the device is available try: # case 1: the device is currently available if any( [self.host in dev.get_serial_no() for dev in adb_devices]): if not self._available: self._available = True return True # case 2: the device is not currently available if self._available: logging.error('ADB server is not connected to the device.') self._available = False return False except RuntimeError: if self._available: logging.error( 'ADB device is unavailable; encountered an error when searching for device.' ) self._available = False return False except RuntimeError: if self._available: logging.error('ADB server is unavailable.') self._available = False return False @property def running_apps(self): """Return a list of running user applications.""" ps = self.adb_shell(RUNNING_APPS_CMD) if ps: return [ line.strip().rsplit(' ', 1)[-1] for line in ps.splitlines() if line.strip() ] return [] @property def current_app(self): """Return the current app.""" current_focus = self.adb_shell(CURRENT_APP_CMD) if current_focus is None: return None current_focus = current_focus.replace("\r", "") matches = WINDOW_REGEX.search(current_focus) # case 1: current app was successfully found if matches: (pkg, activity) = matches.group("package", "activity") return {"package": pkg, "activity": activity} # case 2: current app could not be found logging.warning("Couldn't get current app, reply was %s", current_focus) return None @property def screen_on(self): """Check if the screen is on.""" return self.adb_shell(SCREEN_ON_CMD + SUCCESS1_FAILURE0) == '1' @property def awake(self): """Check if the device is awake (screensaver is not running).""" return self.adb_shell(AWAKE_CMD + SUCCESS1_FAILURE0) == '1' @property def wake_lock(self): """Check for wake locks (device is playing).""" return self.adb_shell(WAKE_LOCK_CMD + SUCCESS1_FAILURE0) == '1' @property def wake_lock_size(self): """Get the size of the current wake lock.""" output = self.adb_shell(WAKE_LOCK_SIZE_CMD) if not output: return None return int(output.split("=")[1].strip()) @property def launcher(self): """Check if the active application is the Amazon TV launcher.""" return self.current_app["package"] == PACKAGE_LAUNCHER @property def settings(self): """Check if the active application is the Amazon menu.""" return self.current_app["package"] == PACKAGE_SETTINGS def get_properties(self, get_running_apps=True, lazy=False): """Get the ``screen_on``, ``awake``, ``wake_lock_size``, ``current_app``, and ``running_apps`` properties.""" if get_running_apps: output = self.adb_shell(SCREEN_ON_CMD + (SUCCESS1 if lazy else SUCCESS1_FAILURE0) + " && " + AWAKE_CMD + (SUCCESS1 if lazy else SUCCESS1_FAILURE0) + " && " + WAKE_LOCK_SIZE_CMD + " && " + CURRENT_APP_CMD + " && " + RUNNING_APPS_CMD) else: output = self.adb_shell(SCREEN_ON_CMD + (SUCCESS1 if lazy else SUCCESS1_FAILURE0) + " && " + AWAKE_CMD + (SUCCESS1 if lazy else SUCCESS1_FAILURE0) + " && " + WAKE_LOCK_SIZE_CMD + " && " + CURRENT_APP_CMD) # ADB command was unsuccessful if output is None: return None, None, None, None, None # `screen_on` property if not output: return False, False, -1, None, None screen_on = output[0] == '1' # `awake` property if len(output) < 2: return screen_on, False, -1, None, None awake = output[1] == '1' lines = output.strip().splitlines() # `wake_lock_size` property if len(lines[0]) < 3: return screen_on, awake, -1, None, None wake_lock_size = int(lines[0].split("=")[1].strip()) # `current_app` property if len(lines) < 2: return screen_on, awake, wake_lock_size, None, None matches = WINDOW_REGEX.search(lines[1]) if matches: # case 1: current app was successfully found (pkg, activity) = matches.group("package", "activity") current_app = {"package": pkg, "activity": activity} else: # case 2: current app could not be found current_app = None # `running_apps` property if not get_running_apps or len(lines) < 3: return screen_on, awake, wake_lock_size, current_app, None running_apps = [ line.strip().rsplit(' ', 1)[-1] for line in lines[2:] if line.strip() ] return screen_on, awake, wake_lock_size, current_app, running_apps # ======================================================================= # # # # turn on/off methods # # # # ======================================================================= # def turn_on(self): """Send power action if device is off.""" self.adb_shell(SCREEN_ON_CMD + " || (input keyevent {0} && input keyevent {1})".format( POWER, HOME)) def turn_off(self): """Send power action if device is not off.""" self.adb_shell(SCREEN_ON_CMD + " && input keyevent {0}".format(SLEEP)) # ======================================================================= # # # # "key" methods: basic commands # # # # ======================================================================= # def power(self): """Send power action.""" self._key(POWER) def sleep(self): """Send sleep action.""" self._key(SLEEP) def home(self): """Send home action.""" self._key(HOME) def up(self): """Send up action.""" self._key(UP) def down(self): """Send down action.""" self._key(DOWN) def left(self): """Send left action.""" self._key(LEFT) def right(self): """Send right action.""" self._key(RIGHT) def enter(self): """Send enter action.""" self._key(ENTER) def back(self): """Send back action.""" self._key(BACK) def space(self): """Send space keypress.""" self._key(SPACE) def menu(self): """Send menu action.""" self._key(MENU) def volume_up(self): """Send volume up action.""" self._key(VOLUME_UP) def volume_down(self): """Send volume down action.""" self._key(VOLUME_DOWN) # ======================================================================= # # # # "key" methods: media commands # # # # ======================================================================= # def media_play_pause(self): """Send media play/pause action.""" self._key(PLAY_PAUSE) def media_play(self): """Send media play action.""" self._key(PLAY) def media_pause(self): """Send media pause action.""" self._key(PAUSE) def media_next(self): """Send media next action (results in fast-forward).""" self._key(NEXT) def media_previous(self): """Send media previous action (results in rewind).""" self._key(PREVIOUS) # ======================================================================= # # # # "key" methods: key commands # # # # ======================================================================= # def key_0(self): """Send 0 keypress.""" self._key(KEY_0) def key_1(self): """Send 1 keypress.""" self._key(KEY_1) def key_2(self): """Send 2 keypress.""" self._key(KEY_2) def key_3(self): """Send 3 keypress.""" self._key(KEY_3) def key_4(self): """Send 4 keypress.""" self._key(KEY_4) def key_5(self): """Send 5 keypress.""" self._key(KEY_5) def key_6(self): """Send 6 keypress.""" self._key(KEY_6) def key_7(self): """Send 7 keypress.""" self._key(KEY_7) def key_8(self): """Send 8 keypress.""" self._key(KEY_8) def key_9(self): """Send 9 keypress.""" self._key(KEY_9) def key_a(self): """Send a keypress.""" self._key(KEY_A) def key_b(self): """Send b keypress.""" self._key(KEY_B) def key_c(self): """Send c keypress.""" self._key(KEY_C) def key_d(self): """Send d keypress.""" self._key(KEY_D) def key_e(self): """Send e keypress.""" self._key(KEY_E) def key_f(self): """Send f keypress.""" self._key(KEY_F) def key_g(self): """Send g keypress.""" self._key(KEY_G) def key_h(self): """Send h keypress.""" self._key(KEY_H) def key_i(self): """Send i keypress.""" self._key(KEY_I) def key_j(self): """Send j keypress.""" self._key(KEY_J) def key_k(self): """Send k keypress.""" self._key(KEY_K) def key_l(self): """Send l keypress.""" self._key(KEY_L) def key_m(self): """Send m keypress.""" self._key(KEY_M) def key_n(self): """Send n keypress.""" self._key(KEY_N) def key_o(self): """Send o keypress.""" self._key(KEY_O) def key_p(self): """Send p keypress.""" self._key(KEY_P) def key_q(self): """Send q keypress.""" self._key(KEY_Q) def key_r(self): """Send r keypress.""" self._key(KEY_R) def key_s(self): """Send s keypress.""" self._key(KEY_S) def key_t(self): """Send t keypress.""" self._key(KEY_T) def key_u(self): """Send u keypress.""" self._key(KEY_U) def key_v(self): """Send v keypress.""" self._key(KEY_V) def key_w(self): """Send w keypress.""" self._key(KEY_W) def key_x(self): """Send x keypress.""" self._key(KEY_X) def key_y(self): """Send y keypress.""" self._key(KEY_Y) def key_z(self): """Send z keypress.""" self._key(KEY_Z)
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 in the format ``<ip address>:<host>`` adbkey : str The path to the ``adbkey`` file for ADB authentication """ def __init__(self, host, adbkey=''): self.host = host self.adbkey = adbkey self._adb = AdbDevice(serial=self.host, 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 """ self._adb_lock.acquire(**LOCK_KWARGS) # pylint: disable=unexpected-keyword-arg # Make sure that we release the lock try: # Catch errors try: 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) # Connect to the device self._adb.connect(rsa_keys=[signer], auth_timeout_s=auth_timeout_s) else: self._adb.connect(auth_timeout_s=auth_timeout_s) # ADB connection successfully established self._available = True _LOGGER.debug("ADB connection to %s successfully established", self.host) except socket_error as serr: if self._available or always_log_errors: if serr.strerror is None: serr.strerror = "Timed out trying to connect to ADB device." _LOGGER.warning("Couldn't connect to host %s, error: %s", self.host, serr.strerror) # ADB connection attempt failed self._adb.close() self._available = False finally: return self._available finally: self._adb_lock.release() 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 because python-adb connection is not established: %s", self.host, cmd) return None if self._adb_lock.acquire(**LOCK_KWARGS): # pylint: disable=unexpected-keyword-arg _LOGGER.debug("Sending command to %s via python-adb: %s", self.host, cmd) try: return self._adb.shell(cmd) finally: self._adb_lock.release() else: _LOGGER.debug( "ADB command not sent to %s because python-adb lock not acquired: %s", self.host, cmd) return None
def test_init_invalid_handle(self): with self.assertRaises(exceptions.InvalidHandleError): device = AdbDevice(handle=123) # Clear the `_bulk_read` buffer so that `self.tearDown()` passes self.device._handle._bulk_read = b''
def setUp(self): self.device = AdbDevice(handle=patchers.FakeTcpHandle('host', 5555)) self.device._handle._bulk_read = b''.join(patchers.BULK_READ_LIST)
class TestAdbDevice(unittest.TestCase): def setUp(self): self.device = AdbDevice(handle=patchers.FakeTcpHandle('host', 5555)) self.device._handle._bulk_read = b''.join(patchers.BULK_READ_LIST) def tearDown(self): self.assertFalse(self.device._handle._bulk_read) def test_init_tcp(self): with patchers.PATCH_TCP_HANDLE: tcp_device = AdbDeviceTcp('host') tcp_device._handle._bulk_read = self.device._handle._bulk_read # Make sure that the `connect()` method works self.assertTrue(tcp_device.connect()) self.assertTrue(tcp_device.available) # Clear the `_bulk_read` buffer so that `self.tearDown()` passes self.device._handle._bulk_read = b'' def test_init_banner(self): device_with_banner = AdbDevice(handle=patchers.FakeTcpHandle( 'host', 5555), banner='banner') self.assertEqual(device_with_banner._banner, b'banner') device_with_banner2 = AdbDevice(handle=patchers.FakeTcpHandle( 'host', 5555), banner=bytearray('banner2', 'utf-8')) self.assertEqual(device_with_banner2._banner, b'banner2') device_with_banner3 = AdbDevice(handle=patchers.FakeTcpHandle( 'host', 5555), banner=u'banner3') self.assertEqual(device_with_banner3._banner, b'banner3') with patch('socket.gethostname', side_effect=Exception): device_banner_unknown = AdbDevice( handle=patchers.FakeTcpHandle('host', 5555)) self.assertEqual(device_banner_unknown._banner, b'unknown') # Clear the `_bulk_read` buffer so that `self.tearDown()` passes self.device._handle._bulk_read = b'' def test_init_invalid_handle(self): with self.assertRaises(exceptions.InvalidHandleError): device = AdbDevice(handle=123) # Clear the `_bulk_read` buffer so that `self.tearDown()` passes self.device._handle._bulk_read = b'' def test_available(self): self.assertFalse(self.device.available) # Clear the `_bulk_read` buffer so that `self.tearDown()` passes self.device._handle._bulk_read = b'' def test_close(self): self.assertFalse(self.device.close()) self.assertFalse(self.device.available) # Clear the `_bulk_read` buffer so that `self.tearDown()` passes self.device._handle._bulk_read = b'' # ======================================================================= # # # # `connect` tests # # # # ======================================================================= # def test_connect(self): self.assertTrue(self.device.connect()) self.assertTrue(self.device.available) def test_connect_no_keys(self): self.device._handle._bulk_read = b''.join( patchers.BULK_READ_LIST_WITH_AUTH[:2]) with self.assertRaises(exceptions.DeviceAuthError): self.device.connect() self.assertFalse(self.device.available) def test_connect_with_key_invalid_response(self): with patch('adb_shell.auth.sign_pythonrsa.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub): keygen('tests/adbkey') signer = PythonRSASigner.FromRSAKeyPath('tests/adbkey') self.device._handle._bulk_read = b''.join( patchers.BULK_READ_LIST_WITH_AUTH_INVALID) with self.assertRaises(exceptions.InvalidResponseError): self.device.connect([signer]) self.assertFalse(self.device.available) def test_connect_with_key(self): with patch('adb_shell.auth.sign_pythonrsa.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub): keygen('tests/adbkey') signer = PythonRSASigner.FromRSAKeyPath('tests/adbkey') self.device._handle._bulk_read = b''.join( patchers.BULK_READ_LIST_WITH_AUTH) self.assertTrue(self.device.connect([signer])) def test_connect_with_new_key(self): with patch('adb_shell.auth.sign_pythonrsa.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub): keygen('tests/adbkey') signer = PythonRSASigner.FromRSAKeyPath('tests/adbkey') signer.pub_key = u'' self.device._handle._bulk_read = b''.join( patchers.BULK_READ_LIST_WITH_AUTH_NEW_KEY) self.assertTrue(self.device.connect([signer])) def test_connect_with_new_key_and_callback(self): with patch('adb_shell.auth.sign_pythonrsa.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub): keygen('tests/adbkey') signer = PythonRSASigner.FromRSAKeyPath('tests/adbkey') signer.pub_key = u'' self._callback_invoked = False def auth_callback(device): self._callback_invoked = True self.device._handle._bulk_read = b''.join( patchers.BULK_READ_LIST_WITH_AUTH_NEW_KEY) self.assertTrue( self.device.connect([signer], auth_callback=auth_callback)) self.assertTrue(self._callback_invoked) # ======================================================================= # # # # `shell` tests # # # # ======================================================================= # def test_shell_no_return(self): self.assertTrue(self.device.connect()) # Provide the `bulk_read` return values self.device._handle._bulk_read = join_messages( AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'')) self.assertEqual(self.device.shell('TEST'), '') def test_shell_return_pass(self): self.assertTrue(self.device.connect()) # Provide the `bulk_read` return values self.device._handle._bulk_read = join_messages( AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'PA'), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'SS'), AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'')) self.assertEqual(self.device.shell('TEST'), 'PASS') def test_shell_dont_decode(self): self.assertTrue(self.device.connect()) # Provide the `bulk_read` return values self.device._handle._bulk_read = join_messages( AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'PA'), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'SS'), AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'')) self.assertEqual(self.device.shell('TEST', decode=False), b'PASS') def test_shell_data_length_exceeds_max(self): self.assertTrue(self.device.connect()) # Provide the `bulk_read` return values self.device._handle._bulk_read = join_messages( AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'0' * (constants.MAX_ADB_DATA + 1)), AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'')) self.device.shell('TEST') self.assertTrue(True) def test_shell_multibytes_sequence_exceeds_max(self): self.assertTrue(self.device.connect()) # Provide the `bulk_read` return values self.device._handle._bulk_read = join_messages( AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'0' * (constants.MAX_ADB_DATA - 1) + b'\xe3\x81\x82'), AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'')) self.assertEqual(u'0' * (constants.MAX_ADB_DATA - 1) + u'\u3042', self.device.shell('TEST')) def test_shell_with_multibytes_sequence_over_two_messages(self): self.assertTrue(self.device.connect()) # Provide the `bulk_read` return values self.device._handle._bulk_read = join_messages( AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'\xe3'), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'\x81\x82'), AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'')) self.assertEqual(u'\u3042', self.device.shell('TEST')) def test_shell_multiple_clse(self): # https://github.com/JeffLIrion/adb_shell/issues/15#issuecomment-536795938 self.assertTrue(self.device.connect()) # Provide the `bulk_read` return values msg1 = AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'') msg2 = AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'PASS') msg3 = AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'') self.device._handle._bulk_read = b''.join([ b'OKAY\xd9R\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb0\xb4\xbe\xa6', b'WRTE\xd9R\x00\x00\x01\x00\x00\x00\x01\x00\x00\x002\x00\x00\x00\xa8\xad\xab\xba', b'2', b'WRTE\xd9R\x00\x00\x01\x00\x00\x00\x0c\x02\x00\x00\xc0\x92\x00\x00\xa8\xad\xab\xba', b'Wake Locks: size=2\ncom.google.android.tvlauncher\n\n- STREAM_MUSIC:\n Muted: true\n Min: 0\n Max: 15\n Current: 2 (speaker): 15, 4 (headset): 10, 8 (headphone): 10, 80 (bt_a2dp): 10, 1000 (digital_dock): 10, 4000000 (usb_headset): 3, 40000000 (default): 15\n Devices: speaker\n- STREAM_ALARM:\n Muted: true\n Min: 1\n Max: 7\n Current: 2 (speaker): 7, 4 (headset): 5, 8 (headphone): 5, 80 (bt_a2dp): 5, 1000 (digital_dock): 5, 4000000 (usb_headset): 1, 40000000 (default): 7\n Devices: speaker\n- STREAM_NOTIFICATION:\n', b'CLSE\xd9R\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbc\xb3\xac\xba', msg1.pack(), b'CLSE\xdaR\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbc\xb3\xac\xba', msg2.pack(), msg2.data, msg3.pack() ]) self.device.shell( "dumpsys power | grep 'Display Power' | grep -q 'state=ON' && echo -e '1\\c' && dumpsys power | grep mWakefulness | grep -q Awake && echo -e '1\\c' && dumpsys audio | grep paused | grep -qv 'Buffer Queue' && echo -e '1\\c' || (dumpsys audio | grep started | grep -qv 'Buffer Queue' && echo '2\\c' || echo '0\\c') && dumpsys power | grep Locks | grep 'size=' && CURRENT_APP=$(dumpsys window windows | grep mCurrentFocus) && CURRENT_APP=${CURRENT_APP#*{* * } && CURRENT_APP=${CURRENT_APP%%/*} && echo $CURRENT_APP && (dumpsys media_session | grep -A 100 'Sessions Stack' | grep -A 100 $CURRENT_APP | grep -m 1 'state=PlaybackState {' || echo) && dumpsys audio | grep '\\- STREAM_MUSIC:' -A 12" ) self.assertEqual(self.device.shell('TEST'), 'PASS') # ======================================================================= # # # # `shell` error tests # # # # ======================================================================= # def test_shell_error_local_id(self): self.assertTrue(self.device.connect()) # Provide the `bulk_read` return values self.device._handle._bulk_read = join_messages( AdbMessage(command=constants.OKAY, arg0=1, arg1=1234, data=b'\x00')) with self.assertRaises(exceptions.InvalidResponseError): self.device.shell('TEST') def test_shell_error_unknown_command(self): self.assertTrue(self.device.connect()) # Provide the `bulk_read` return values self.device._handle._bulk_read = join_messages( AdbMessageForTesting(command=constants.FAIL, arg0=1, arg1=1, data=b'')) with self.assertRaises(exceptions.InvalidCommandError): self.assertEqual(self.device.shell('TEST'), '') def test_shell_error_timeout(self): self.assertTrue(self.device.connect()) # Provide the `bulk_read` return values self.device._handle._bulk_read = join_messages( AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'')) with self.assertRaises(exceptions.InvalidCommandError): self.device.shell('TEST', total_timeout_s=-1) def test_shell_error_timeout_multiple_clse(self): self.assertTrue(self.device.connect()) # Provide the `bulk_read` return values self.device._handle._bulk_read = join_messages( AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''), AdbMessage(command=constants.CLSE, arg0=2, arg1=1, data=b'')) with self.assertRaises(exceptions.InvalidCommandError): self.device.shell('TEST', total_timeout_s=-1) def test_shell_error_checksum(self): self.assertTrue(self.device.connect()) # Provide the `bulk_read` return values msg1 = AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00') msg2 = AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'PASS') self.device._handle._bulk_read = b''.join( [msg1.pack(), msg1.data, msg2.pack(), msg2.data[:-1] + b'0']) with self.assertRaises(exceptions.InvalidChecksumError): self.device.shell('TEST') def test_shell_error_local_id2(self): self.assertTrue(self.device.connect()) # Provide the `bulk_read` return values self.device._handle._bulk_read = join_messages( AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.WRTE, arg0=1, arg1=2, data=b'PASS')) with self.assertRaises(exceptions.InterleavedDataError): self.device.shell('TEST') self.device.shell('TEST') def test_shell_error_remote_id2(self): self.assertTrue(self.device.connect()) # Provide the `bulk_read` return values self.device._handle._bulk_read = join_messages( AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.WRTE, arg0=2, arg1=1, data=b'PASS')) with self.assertRaises(exceptions.InvalidResponseError): self.device.shell('TEST') def test_issue29(self): # https://github.com/JeffLIrion/adb_shell/issues/29 with patch('adb_shell.auth.sign_pythonrsa.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub): keygen('tests/adbkey') signer = PythonRSASigner.FromRSAKeyPath('tests/adbkey') msg1 = AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00') msg2 = AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'') self.device._handle._bulk_read = b''.join([ b'AUTH\x01\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\xc5\n\x00\x00\xbe\xaa\xab\xb7', # Line 22 b"\x17\xbf\xbf\xff\xc7\xa2eo'Sh\xdf\x8e\xf5\xff\xe0\tJ6H", # Line 23 b"CNXN\x00\x00\x00\x01\x00\x10\x00\x00i\x00\x00\x00.'\x00\x00\xbc\xb1\xa7\xb1", # Line 26 b'device::ro.product.name=once;ro.product.model=MIBOX3;ro.product.device=once;features=stat_v2,cmd,shell_v2', # Line 27 b'OKAY\x99\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb0\xb4\xbe\xa6', # Line 290 (modified --> Line 30) b'CLSE\xa2\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbc\xb3\xac\xba', # Line 291 b'CLSE\xa2\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbc\xb3\xac\xba', # Line 292 b'WRTE\x99\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x001\x00\x00\x00\xa8\xad\xab\xba', # Line 31 b'1', # Line 32 b'WRTE\x99\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x001\x00\x00\x00\xa8\xad\xab\xba', # Line 35 b'1', # Line 36 b'WRTE\x99\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x000\x00\x00\x00\xa8\xad\xab\xba', # Line 39 b'0', # Line 40 b'WRTE\x99\x00\x00\x00\x01\x00\x00\x00\x13\x00\x00\x000\x06\x00\x00\xa8\xad\xab\xba', # Line 43 b'Wake Locks: size=0\n', # Line 44 b'WRTE\x99\x00\x00\x00\x01\x00\x00\x00\x1e\x00\x00\x00V\x0b\x00\x00\xa8\xad\xab\xba', # Line 47 b'com.google.android.youtube.tv\n', # Line 48 b'WRTE\x99\x00\x00\x00\x01\x00\x00\x00\x98\x00\x00\x00\xa13\x00\x00\xa8\xad\xab\xba', # Line 51 b' state=PlaybackState {state=0, position=0, buffered position=0, speed=0.0, updated=0, actions=0, custom actions=[], active item id=-1, error=null}\n', # Line 52 b'WRTE\x99\x00\x00\x00\x01\x00\x00\x00.\x01\x00\x00\xceP\x00\x00\xa8\xad\xab\xba', # Line 55 b'- STREAM_MUSIC:\n Muted: false\n Min: 0\n Max: 15\n Current: 2 (speaker): 11, 4 (headset): 10, 8 (headphone): 10, 400 (hdmi): 6, 40000000 (default): 11\n Devices: hdmi\n- STREAM_ALARM:\n Muted: false\n Min: 0\n Max: 7\n Current: 40000000 (default): 6\n Devices: speaker\n- STREAM_NOTIFICATION:\n', # Line 56 b'CLSE\x99\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbc\xb3\xac\xba', # Line 59 b'AUTH\x01\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\x94\t\x00\x00\xbe\xaa\xab\xb7', # Line 297 b'P\xa5\x86\x97\xe8\x01\xb09\x8c>F\x9d\xc6\xbd\xc0J\x80!\xbb\x1a', # Line 298 b"CNXN\x00\x00\x00\x01\x00\x10\x00\x00i\x00\x00\x00.'\x00\x00\xbc\xb1\xa7\xb1", # Line 301 b'device::ro.product.name=once;ro.product.model=MIBOX3;ro.product.device=once;features=stat_v2,cmd,shell_v2', # Line 302 b'OKAY\xa5\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb0\xb4\xbe\xa6', # Line 305 b'CLSE\xa5\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbc\xb3\xac\xba', # Line 306 msg1.pack(), msg1.data, msg2.pack(), b'AUTH\x01\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00e\x0c\x00\x00\xbe\xaa\xab\xb7', # Line 315 b'\xd3\xef\x7f_\xa6\xc0`b\x19\\z\xe4\xf3\xe2\xed\x8d\xe1W\xfbH', # Line 316 b"CNXN\x00\x00\x00\x01\x00\x10\x00\x00i\x00\x00\x00.'\x00\x00\xbc\xb1\xa7\xb1", # Line 319 b'device::ro.product.name=once;ro.product.model=MIBOX3;ro.product.device=once;features=stat_v2,cmd,shell_v2', # Line 320 b'OKAY\xa7\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb0\xb4\xbe\xa6', # Line 323 b'CLSE\xa7\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbc\xb3\xac\xba', # Line 324 msg1.pack(), msg1.data, msg2.pack(), b'AUTH\x01\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\x93\x08\x00\x00\xbe\xaa\xab\xb7', # Line 333 b's\xd4_e\xa4s\x02\x95\x0f\x1e\xec\n\x95Y9[`\x8e\xe1f', # Line 334 b"CNXN\x00\x00\x00\x01\x00\x10\x00\x00i\x00\x00\x00.'\x00\x00\xbc\xb1\xa7\xb1", # Line 337 b'device::ro.product.name=once;ro.product.model=MIBOX3;ro.product.device=once;features=stat_v2,cmd,shell_v2', # Line 338 b'OKAY\xa9\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb0\xb4\xbe\xa6', # Line 341 b'CLSE\xa9\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbc\xb3\xac\xba', # Line 342 msg1.pack(), msg1.data, msg2.pack() ]) self.assertTrue(self.device.connect([signer])) self.device.shell('Android TV update command') self.assertTrue(self.device.connect([signer])) self.device.shell('Android TV update command') self.device.shell('Android TV update command') self.assertTrue(self.device.connect([signer])) self.device.shell('Android TV update command') self.device.shell('Android TV update command') self.assertTrue(self.device.connect([signer])) self.device.shell('Android TV update command') self.device.shell('Android TV update command') # ======================================================================= # # # # `streaming_shell` tests # # # # ======================================================================= # def test_streaming_shell_decode(self): self.assertTrue(self.device.connect()) # Provide the `bulk_read` return values self.device._handle._bulk_read = join_messages( AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'ABC'), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'123'), ) generator = self.device.streaming_shell('TEST', decode=True) self.assertEqual('ABC', next(generator)) self.assertEqual('123', next(generator)) def test_streaming_shell_dont_decode(self): self.assertTrue(self.device.connect()) # Provide the `bulk_read` return values self.device._handle._bulk_read = join_messages( AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'ABC'), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'123'), ) generator = self.device.streaming_shell('TEST', decode=False) self.assertEqual(b'ABC', next(generator)) self.assertEqual(b'123', next(generator)) # ======================================================================= # # # # `filesync` tests # # # # ======================================================================= # def test_list(self): self.assertTrue(self.device.connect()) self.device._handle._bulk_write = b'' # Provide the `bulk_read` return values self.device._handle._bulk_read = join_messages( AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.OKAY, arg0=1, arg1=1), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages( FileSyncListMessage(constants.DENT, 1, 2, 3, data=b'file1'), FileSyncListMessage(constants.DENT, 4, 5, 6, data=b'file2'), FileSyncListMessage(constants.DONE, 0, 0, 0))), AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'')) # Expected `bulk_write` values expected_bulk_write = join_messages( AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\x00'), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages( FileSyncMessage(command=constants.LIST, data=b'/dir'))), AdbMessage(command=constants.OKAY, arg0=1, arg1=1), AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'')) expected_result = [ DeviceFile(filename=bytearray(b'file1'), mode=1, size=2, mtime=3), DeviceFile(filename=bytearray(b'file2'), mode=4, size=5, mtime=6) ] self.assertEqual(expected_result, self.device.list('/dir')) self.assertEqual(expected_bulk_write, self.device._handle._bulk_write) def _test_push(self, mtime): self.assertTrue(self.device.connect()) self.device._handle._bulk_write = b'' filedata = b'Ohayou sekai.\nGood morning world!' # Provide the `bulk_read` return values self.device._handle._bulk_read = join_messages( AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages( FileSyncMessage(constants.OKAY, data=b''))), AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'')) # Expected `bulk_write` values expected_bulk_write = join_messages( AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\x00'), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages( FileSyncMessage(command=constants.SEND, data=b'/data,33272'), FileSyncMessage(command=constants.DATA, data=filedata), FileSyncMessage(command=constants.DONE, arg0=mtime))), AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''), AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'')) with patch('time.time', return_value=mtime): self.device.push(BytesIO(filedata), '/data', mtime=mtime) self.assertEqual(expected_bulk_write, self.device._handle._bulk_write) return True def test_push(self): self.assertTrue(self._test_push(100)) def test_push_mtime0(self): self.assertTrue(self._test_push(0)) def test_push_file(self): self.assertTrue(self.device.connect()) self.device._handle._bulk_write = b'' mtime = 100 filedata = b'Ohayou sekai.\nGood morning world!' # Provide the `bulk_read` return values self.device._handle._bulk_read = join_messages( AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=FileSyncMessage(constants.OKAY).pack()), AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'')) # Expected `bulk_write` values expected_bulk_write = join_messages( AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\x00'), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages( FileSyncMessage(command=constants.SEND, data=b'/data,33272'), FileSyncMessage(command=constants.DATA, data=filedata), FileSyncMessage(command=constants.DONE, arg0=mtime, data=b''))), AdbMessage(command=constants.OKAY, arg0=1, arg1=1), AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'')) with patch('adb_shell.adb_device.open', mock_open(read_data=filedata)): self.device.push('TEST_FILE', '/data', mtime=mtime) self.assertEqual(expected_bulk_write, self.device._handle._bulk_write) def test_push_fail(self): self.assertTrue(self.device.connect()) self.device._handle._bulk_write = b'' mtime = 100 filedata = b'Ohayou sekai.\nGood morning world!' # Provide the `bulk_read` return values self.device._handle._bulk_read = join_messages( AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages( FileSyncMessage(constants.FAIL, data=b'')))) with self.assertRaises(exceptions.PushFailedError), patch( 'adb_shell.adb_device.open', mock_open(read_data=filedata)): self.device.push('TEST_FILE', '/data', mtime=mtime) def test_push_big_file(self): self.assertTrue(self.device.connect()) self.device._handle._bulk_write = b'' mtime = 100 filedata = b'0' * int(3.5 * constants.MAX_PUSH_DATA) # Provide the `bulk_read` return values self.device._handle._bulk_read = join_messages( AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.OKAY, arg0=1, arg1=1), AdbMessage(command=constants.OKAY, arg0=1, arg1=1), AdbMessage(command=constants.OKAY, arg0=1, arg1=1), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(constants.OKAY))), AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'')) # Expected `bulk_write` values mpd0, mpd1, mpd2, mpd3 = 0, constants.MAX_PUSH_DATA, 2 * constants.MAX_PUSH_DATA, 3 * constants.MAX_PUSH_DATA expected_bulk_write = join_messages( AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\x00'), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages( FileSyncMessage(command=constants.SEND, data=b'/data,33272'), FileSyncMessage(command=constants.DATA, data=filedata[mpd0:mpd1]))), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages( FileSyncMessage(command=constants.DATA, data=filedata[mpd1:mpd2]))), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages( FileSyncMessage(command=constants.DATA, data=filedata[mpd2:mpd3]), FileSyncMessage(command=constants.DATA, data=filedata[mpd3:]), FileSyncMessage(command=constants.DONE, arg0=mtime))), AdbMessage(command=constants.OKAY, arg0=1, arg1=1), AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'')) self.device.push(BytesIO(filedata), '/data', mtime=mtime) self.assertEqual(expected_bulk_write, self.device._handle._bulk_write) def test_push_dir(self): self.assertTrue(self.device.connect()) mtime = 100 filedata = b'Ohayou sekai.\nGood morning world!' # Provide the `bulk_read` return values self.device._handle._bulk_read = join_messages( AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''), AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(constants.OKAY))), AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''), AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(constants.OKAY))), AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'')) # Expected `bulk_write` values #TODO with patch('adb_shell.adb_device.open', mock_open(read_data=filedata)), patch( 'os.path.isdir', lambda x: x == 'TEST_DIR/'), patch( 'os.listdir', return_value=['TEST_FILE1', 'TEST_FILE2']): self.device.push('TEST_DIR/', '/data', mtime=mtime) def test_pull(self): self.assertTrue(self.device.connect()) self.device._handle._bulk_write = b'' filedata = b'Ohayou sekai.\nGood morning world!' # Provide the `bulk_read` return values self.device._handle._bulk_read = join_messages( AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages( FileSyncMessage(command=constants.DATA, data=filedata), FileSyncMessage(command=constants.DONE))), AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'')) # Expected `bulk_write` values expected_bulk_write = join_messages( AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\x00'), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages( FileSyncMessage(command=constants.RECV, data=b'/data'))), AdbMessage(command=constants.OKAY, arg0=1, arg1=1), AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'')) self.assertEqual(filedata, self.device.pull('/data')) self.assertEqual(expected_bulk_write, self.device._handle._bulk_write) def test_pull_file(self): self.assertTrue(self.device.connect()) self.device._handle._bulk_write = b'' filedata = b'Ohayou sekai.\nGood morning world!' # Provide the `bulk_read` return values self.device._handle._bulk_read = join_messages( AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages( FileSyncMessage(command=constants.DATA, data=filedata), FileSyncMessage(command=constants.DONE))), AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'')) # Expected `bulk_write` values expected_bulk_write = join_messages( AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\x00'), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages( FileSyncMessage(command=constants.RECV, data=b'/data'))), AdbMessage(command=constants.OKAY, arg0=1, arg1=1), AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'')) with patch('adb_shell.adb_device.open', mock_open()), patch('os.path.exists', return_value=True): self.assertTrue(self.device.pull('/data', 'TEST_FILE')) self.assertEqual(expected_bulk_write, self.device._handle._bulk_write) def test_pull_file_return_true(self): self.assertTrue(self.device.connect()) self.device._handle._bulk_write = b'' filedata = b'Ohayou sekai.\nGood morning world!' # Provide the `bulk_read` return values self.device._handle._bulk_read = join_messages( AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages( FileSyncMessage(command=constants.DATA, data=filedata), FileSyncMessage(command=constants.DONE))), AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'')) # Expected `bulk_write` values expected_bulk_write = join_messages( AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\x00'), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages( FileSyncMessage(command=constants.RECV, data=b'/data'))), AdbMessage(command=constants.OKAY, arg0=1, arg1=1), AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'')) with patch('adb_shell.adb_device.open', mock_open()), patch('adb_shell.adb_device.hasattr', return_value=False): self.assertTrue(self.device.pull('/data', 'TEST_FILE')) self.assertEqual(expected_bulk_write, self.device._handle._bulk_write) def test_pull_big_file(self): self.assertTrue(self.device.connect()) self.device._handle._bulk_write = b'' filedata = b'0' * int(1.5 * constants.MAX_ADB_DATA) # Provide the `bulk_read` return values self.device._handle._bulk_read = join_messages( AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages( FileSyncMessage(command=constants.DATA, data=filedata), FileSyncMessage(command=constants.DONE))), AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'')) # Expected `bulk_write` values expected_bulk_write = join_messages( AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\x00'), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages( FileSyncMessage(command=constants.RECV, data=b'/data'))), AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''), AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'')) with patch('adb_shell.adb_device.open', mock_open()), patch('os.path.exists', return_value=True): self.assertTrue(self.device.pull('/data', 'TEST_FILE')) self.assertEqual(expected_bulk_write, self.device._handle._bulk_write) def test_stat(self): self.assertTrue(self.device.connect()) self.device._handle._bulk_write = b'' # Provide the `bulk_read` return values self.device._handle._bulk_read = join_messages( AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages( FileSyncStatMessage(constants.STAT, 1, 2, 3), FileSyncStatMessage(constants.DONE, 0, 0, 0))), AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'')) # Expected `bulk_write` values expected_bulk_write = join_messages( AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\x00'), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages( FileSyncMessage(command=constants.STAT, data=b'/data'))), AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''), AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'')) self.assertEqual((1, 2, 3), self.device.stat('/data')) self.assertEqual(expected_bulk_write, self.device._handle._bulk_write) # ======================================================================= # # # # `filesync` hidden methods tests # # # # ======================================================================= # def test_filesync_read_adb_command_failure_exceptions(self): self.assertTrue(self.device.connect()) self.device._handle._bulk_write = b'' # Provide the `bulk_read` return values self.device._handle._bulk_read = join_messages( AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages( FileSyncStatMessage(constants.FAIL, 1, 2, 3), FileSyncStatMessage(constants.DONE, 0, 0, 0)))) with self.assertRaises(exceptions.AdbCommandFailureException): self.device.stat('/data') def test_filesync_read_invalid_response_error(self): self.assertTrue(self.device.connect()) self.device._handle._bulk_write = b'' # Provide the `bulk_read` return values self.device._handle._bulk_read = join_messages( AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages( FileSyncStatMessage(constants.DENT, 1, 2, 3), FileSyncStatMessage(constants.DONE, 0, 0, 0)))) with self.assertRaises(exceptions.InvalidResponseError): self.device.stat('/data') # ======================================================================= # # # # `filesync` error tests # # # # ======================================================================= # def test_pull_value_error(self): self.assertTrue(self.device.connect()) self.device._handle._bulk_write = b'' with self.assertRaises(ValueError): self.device.pull('device_filename', 123)
def setUp(self): with patch('socket.gethostname', side_effect=Exception): with patchers.patch_tcp_handle: self.device = AdbDevice('IP:5555') self.device._handle._bulk_read = b''.join( patchers.BULK_READ_LIST)
def setUp(self): with patchers.patch_tcp_handle: self.device = AdbDevice('IP:5555', 'banner') self.device._handle._bulk_read = b''.join(patchers.BULK_READ_LIST)
class TestAdbDevice(unittest.TestCase): def setUp(self): with patchers.patch_tcp_handle: self.device = AdbDevice('IP:5555') self.device._handle._bulk_read = b''.join(patchers.BULK_READ_LIST) def tearDown(self): self.assertFalse(self.device._handle._bulk_read) def test_init(self): device_with_banner = AdbDevice('IP:5555', 'banner') self.assertEqual(device_with_banner._banner, 'banner') with patch('socket.gethostname', side_effect=Exception): device_banner_unknown = AdbDevice('IP:5555') self.assertEqual(device_banner_unknown._banner, 'unknown') self.device._handle._bulk_read = b'' def test_available(self): self.assertFalse(self.device.available) self.device._handle._bulk_read = b'' def test_close(self): self.assertFalse(self.device.close()) self.assertFalse(self.device.available) self.device._handle._bulk_read = b'' # ======================================================================= # # # # `connect` tests # # # # ======================================================================= # def test_connect(self): self.assertTrue(self.device.connect()) self.assertTrue(self.device.available) def test_connect_no_keys(self): self.device._handle._bulk_read = b''.join( patchers.BULK_READ_LIST_WITH_AUTH[:2]) with self.assertRaises(exceptions.DeviceAuthError): self.device.connect() self.assertFalse(self.device.available) def test_connect_with_key_invalid_response(self): with patch('adb_shell.auth.sign_pythonrsa.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub): keygen('tests/adbkey') signer = PythonRSASigner.FromRSAKeyPath('tests/adbkey') self.device._handle._bulk_read = b''.join( patchers.BULK_READ_LIST_WITH_AUTH_INVALID) with self.assertRaises(exceptions.InvalidResponseError): self.device.connect([signer]) self.assertFalse(self.device.available) def test_connect_with_key(self): with patch('adb_shell.auth.sign_pythonrsa.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub): keygen('tests/adbkey') signer = PythonRSASigner.FromRSAKeyPath('tests/adbkey') self.device._handle._bulk_read = b''.join( patchers.BULK_READ_LIST_WITH_AUTH) self.assertTrue(self.device.connect([signer])) def test_connect_with_new_key(self): with patch('adb_shell.auth.sign_pythonrsa.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub): keygen('tests/adbkey') signer = PythonRSASigner.FromRSAKeyPath('tests/adbkey') self.device._handle._bulk_read = b''.join( patchers.BULK_READ_LIST_WITH_AUTH_NEW_KEY) self.assertTrue(self.device.connect([signer])) # ======================================================================= # # # # `shell` tests # # # # ======================================================================= # def test_shell_no_return(self): self.assertTrue(self.device.connect()) # Provide the `bulk_read` return values msg1 = AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00') msg2 = AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'') self.device._handle._bulk_read = b''.join( [msg1.pack(), msg1.data, msg2.pack()]) self.assertEqual(self.device.shell('TEST'), '') def test_shell_return_pass(self): self.assertTrue(self.device.connect()) # Provide the `bulk_read` return values msg1 = AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00') msg2 = AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'PA') msg3 = AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'SS') msg4 = AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'') self.device._handle._bulk_read = b''.join([ msg1.pack(), msg1.data, msg2.pack(), msg2.data, msg3.pack(), msg3.data, msg4.pack() ]) self.assertEqual(self.device.shell('TEST'), 'PASS') def test_shell_data_length_exceeds_max(self): self.assertTrue(self.device.connect()) # Provide the `bulk_read` return values msg1 = AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00') msg2 = AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'0' * (constants.MAX_ADB_DATA + 1)) msg3 = AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'') self.device._handle._bulk_read = b''.join( [msg1.pack(), msg1.data, msg2.pack(), msg2.data, msg3.pack()]) self.device.shell('TEST') self.assertTrue(True) def test_shell_multibytes_sequence_exceeds_max(self): self.assertTrue(self.device.connect()) # Provide the `bulk_read` return values msg1 = AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00') msg2 = AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'0' * (constants.MAX_ADB_DATA - 1) + b'\xe3\x81\x82') msg3 = AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'') self.device._handle._bulk_read = b''.join( [msg1.pack(), msg1.data, msg2.pack(), msg2.data, msg3.pack()]) res = self.device.shell('TEST') self.assertEqual(u'0' * (constants.MAX_ADB_DATA - 1) + u'\u3042', res) def test_shell_with_multibytes_sequence_over_two_messages(self): self.assertTrue(self.device.connect()) # Provide the `bulk_read` return values msg1 = AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00') msg2 = AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'\xe3') msg3 = AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'\x81\x82') msg4 = AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'') self.device._handle._bulk_read = b''.join([ msg1.pack(), msg1.data, msg2.pack(), msg2.data, msg3.pack(), msg3.data, msg4.pack() ]) res = self.device.shell('TEST') self.assertEqual(u'\u3042', res) def test_shell_multiple_clse(self): # https://github.com/JeffLIrion/adb_shell/issues/15#issuecomment-536795938 self.assertTrue(self.device.connect()) # Provide the `bulk_read` return values msg1 = AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'') msg2 = AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'PASS') msg3 = AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'') self.device._handle._bulk_read = b''.join([ b'OKAY\xd9R\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb0\xb4\xbe\xa6', b'WRTE\xd9R\x00\x00\x01\x00\x00\x00\x01\x00\x00\x002\x00\x00\x00\xa8\xad\xab\xba', b'2', b'WRTE\xd9R\x00\x00\x01\x00\x00\x00\x0c\x02\x00\x00\xc0\x92\x00\x00\xa8\xad\xab\xba', b'Wake Locks: size=2\ncom.google.android.tvlauncher\n\n- STREAM_MUSIC:\n Muted: true\n Min: 0\n Max: 15\n Current: 2 (speaker): 15, 4 (headset): 10, 8 (headphone): 10, 80 (bt_a2dp): 10, 1000 (digital_dock): 10, 4000000 (usb_headset): 3, 40000000 (default): 15\n Devices: speaker\n- STREAM_ALARM:\n Muted: true\n Min: 1\n Max: 7\n Current: 2 (speaker): 7, 4 (headset): 5, 8 (headphone): 5, 80 (bt_a2dp): 5, 1000 (digital_dock): 5, 4000000 (usb_headset): 1, 40000000 (default): 7\n Devices: speaker\n- STREAM_NOTIFICATION:\n', b'CLSE\xd9R\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbc\xb3\xac\xba', msg1.pack(), b'CLSE\xdaR\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbc\xb3\xac\xba', msg2.pack(), msg2.data, msg3.pack() ]) self.device.shell( "dumpsys power | grep 'Display Power' | grep -q 'state=ON' && echo -e '1\\c' && dumpsys power | grep mWakefulness | grep -q Awake && echo -e '1\\c' && dumpsys audio | grep paused | grep -qv 'Buffer Queue' && echo -e '1\\c' || (dumpsys audio | grep started | grep -qv 'Buffer Queue' && echo '2\\c' || echo '0\\c') && dumpsys power | grep Locks | grep 'size=' && CURRENT_APP=$(dumpsys window windows | grep mCurrentFocus) && CURRENT_APP=${CURRENT_APP#*{* * } && CURRENT_APP=${CURRENT_APP%%/*} && echo $CURRENT_APP && (dumpsys media_session | grep -A 100 'Sessions Stack' | grep -A 100 $CURRENT_APP | grep -m 1 'state=PlaybackState {' || echo) && dumpsys audio | grep '\\- STREAM_MUSIC:' -A 12" ) self.assertEqual(self.device.shell('TEST'), 'PASS') # ======================================================================= # # # # `shell` error tests # # # # ======================================================================= # def test_shell_error_local_id(self): self.assertTrue(self.device.connect()) # Provide the `bulk_read` return values msg1 = AdbMessage(command=constants.OKAY, arg0=1, arg1=1234, data=b'\x00') self.device._handle._bulk_read = b''.join([msg1.pack(), msg1.data]) with self.assertRaises(exceptions.InvalidResponseError): self.device.shell('TEST') def test_shell_error_unknown_command(self): self.assertTrue(self.device.connect()) # Provide the `bulk_read` return values msg1 = AdbMessageForTesting(command=constants.FAIL, arg0=1, arg1=1, data=b'\x00') self.device._handle._bulk_read = msg1.pack() with self.assertRaises(exceptions.InvalidCommandError): self.assertEqual(self.device.shell('TEST'), '') def test_shell_error_timeout(self): self.assertTrue(self.device.connect()) # Provide the `bulk_read` return values msg1 = AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'') self.device._handle._bulk_read = msg1.pack() with self.assertRaises(exceptions.InvalidCommandError): self.device.shell('TEST', total_timeout_s=-1) def test_shell_error_timeout_multiple_clse(self): self.assertTrue(self.device.connect()) # Provide the `bulk_read` return values msg1 = AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'') msg2 = AdbMessage(command=constants.CLSE, arg0=2, arg1=1, data=b'') self.device._handle._bulk_read = b''.join([msg1.pack(), msg2.pack()]) with self.assertRaises(exceptions.InvalidCommandError): self.device.shell('TEST', total_timeout_s=-1) def test_shell_error_checksum(self): self.assertTrue(self.device.connect()) # Provide the `bulk_read` return values msg1 = AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00') msg2 = AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'PASS') self.device._handle._bulk_read = b''.join( [msg1.pack(), msg1.data, msg2.pack(), msg2.data[:-1] + b'0']) with self.assertRaises(exceptions.InvalidChecksumError): self.device.shell('TEST') def test_shell_error_local_id2(self): self.assertTrue(self.device.connect()) # Provide the `bulk_read` return values msg1 = AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00') msg2 = AdbMessage(command=constants.WRTE, arg0=1, arg1=2, data=b'PASS') self.device._handle._bulk_read = b''.join( [msg1.pack(), msg1.data, msg2.pack(), msg2.data]) with self.assertRaises(exceptions.InterleavedDataError): self.device.shell('TEST') self.device.shell('TEST') def test_shell_error_remote_id2(self): self.assertTrue(self.device.connect()) # Provide the `bulk_read` return values msg1 = AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00') msg2 = AdbMessage(command=constants.WRTE, arg0=2, arg1=1, data=b'PASS') self.device._handle._bulk_read = b''.join( [msg1.pack(), msg1.data, msg2.pack(), msg2.data]) with self.assertRaises(exceptions.InvalidResponseError): self.device.shell('TEST') def test_issue29(self): # https://github.com/JeffLIrion/adb_shell/issues/29 with patch('adb_shell.auth.sign_pythonrsa.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub): keygen('tests/adbkey') signer = PythonRSASigner.FromRSAKeyPath('tests/adbkey') msg1 = AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00') msg2 = AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'') self.device._handle._bulk_read = b''.join([ b'AUTH\x01\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\xc5\n\x00\x00\xbe\xaa\xab\xb7', # Line 22 b"\x17\xbf\xbf\xff\xc7\xa2eo'Sh\xdf\x8e\xf5\xff\xe0\tJ6H", # Line 23 b"CNXN\x00\x00\x00\x01\x00\x10\x00\x00i\x00\x00\x00.'\x00\x00\xbc\xb1\xa7\xb1", # Line 26 b'device::ro.product.name=once;ro.product.model=MIBOX3;ro.product.device=once;features=stat_v2,cmd,shell_v2', # Line 27 b'OKAY\x99\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb0\xb4\xbe\xa6', # Line 290 (modified --> Line 30) b'CLSE\xa2\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbc\xb3\xac\xba', # Line 291 b'CLSE\xa2\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbc\xb3\xac\xba', # Line 292 b'WRTE\x99\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x001\x00\x00\x00\xa8\xad\xab\xba', # Line 31 b'1', # Line 32 b'WRTE\x99\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x001\x00\x00\x00\xa8\xad\xab\xba', # Line 35 b'1', # Line 36 b'WRTE\x99\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x000\x00\x00\x00\xa8\xad\xab\xba', # Line 39 b'0', # Line 40 b'WRTE\x99\x00\x00\x00\x01\x00\x00\x00\x13\x00\x00\x000\x06\x00\x00\xa8\xad\xab\xba', # Line 43 b'Wake Locks: size=0\n', # Line 44 b'WRTE\x99\x00\x00\x00\x01\x00\x00\x00\x1e\x00\x00\x00V\x0b\x00\x00\xa8\xad\xab\xba', # Line 47 b'com.google.android.youtube.tv\n', # Line 48 b'WRTE\x99\x00\x00\x00\x01\x00\x00\x00\x98\x00\x00\x00\xa13\x00\x00\xa8\xad\xab\xba', # Line 51 b' state=PlaybackState {state=0, position=0, buffered position=0, speed=0.0, updated=0, actions=0, custom actions=[], active item id=-1, error=null}\n', # Line 52 b'WRTE\x99\x00\x00\x00\x01\x00\x00\x00.\x01\x00\x00\xceP\x00\x00\xa8\xad\xab\xba', # Line 55 b'- STREAM_MUSIC:\n Muted: false\n Min: 0\n Max: 15\n Current: 2 (speaker): 11, 4 (headset): 10, 8 (headphone): 10, 400 (hdmi): 6, 40000000 (default): 11\n Devices: hdmi\n- STREAM_ALARM:\n Muted: false\n Min: 0\n Max: 7\n Current: 40000000 (default): 6\n Devices: speaker\n- STREAM_NOTIFICATION:\n', # Line 56 b'CLSE\x99\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbc\xb3\xac\xba', # Line 59 b'AUTH\x01\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\x94\t\x00\x00\xbe\xaa\xab\xb7', # Line 297 b'P\xa5\x86\x97\xe8\x01\xb09\x8c>F\x9d\xc6\xbd\xc0J\x80!\xbb\x1a', # Line 298 b"CNXN\x00\x00\x00\x01\x00\x10\x00\x00i\x00\x00\x00.'\x00\x00\xbc\xb1\xa7\xb1", # Line 301 b'device::ro.product.name=once;ro.product.model=MIBOX3;ro.product.device=once;features=stat_v2,cmd,shell_v2', # Line 302 b'OKAY\xa5\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb0\xb4\xbe\xa6', # Line 305 b'CLSE\xa5\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbc\xb3\xac\xba', # Line 306 msg1.pack(), msg1.data, msg2.pack(), b'AUTH\x01\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00e\x0c\x00\x00\xbe\xaa\xab\xb7', # Line 315 b'\xd3\xef\x7f_\xa6\xc0`b\x19\\z\xe4\xf3\xe2\xed\x8d\xe1W\xfbH', # Line 316 b"CNXN\x00\x00\x00\x01\x00\x10\x00\x00i\x00\x00\x00.'\x00\x00\xbc\xb1\xa7\xb1", # Line 319 b'device::ro.product.name=once;ro.product.model=MIBOX3;ro.product.device=once;features=stat_v2,cmd,shell_v2', # Line 320 b'OKAY\xa7\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb0\xb4\xbe\xa6', # Line 323 b'CLSE\xa7\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbc\xb3\xac\xba', # Line 324 msg1.pack(), msg1.data, msg2.pack(), b'AUTH\x01\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\x93\x08\x00\x00\xbe\xaa\xab\xb7', # Line 333 b's\xd4_e\xa4s\x02\x95\x0f\x1e\xec\n\x95Y9[`\x8e\xe1f', # Line 334 b"CNXN\x00\x00\x00\x01\x00\x10\x00\x00i\x00\x00\x00.'\x00\x00\xbc\xb1\xa7\xb1", # Line 337 b'device::ro.product.name=once;ro.product.model=MIBOX3;ro.product.device=once;features=stat_v2,cmd,shell_v2', # Line 338 b'OKAY\xa9\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb0\xb4\xbe\xa6', # Line 341 b'CLSE\xa9\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbc\xb3\xac\xba', # Line 342 msg1.pack(), msg1.data, msg2.pack() ]) self.assertTrue(self.device.connect([signer])) self.device.shell('Android TV update command') self.assertTrue(self.device.connect([signer])) self.device.shell('Android TV update command') self.device.shell('Android TV update command') self.assertTrue(self.device.connect([signer])) self.device.shell('Android TV update command') self.device.shell('Android TV update command') self.assertTrue(self.device.connect([signer])) self.device.shell('Android TV update command') self.device.shell('Android TV update command')
def setUp(self): self.device = AdbDevice(transport=patchers.FakeTcpTransport('host', 5555)) self.device._transport._bulk_read = b''.join(patchers.BULK_READ_LIST)
def test_init_invalid_transport(self): with self.assertRaises(exceptions.InvalidTransportError): device = AdbDevice(transport=123) # Clear the `_bulk_read` buffer so that `self.tearDown()` passes self.device._transport._bulk_read = b''