Exemple #1
0
    def restore_app_connection(self, port=None):
        """Restores the app after device got reconnected.

        Instead of creating new instance of the client:
          - Uses the given port (or find a new available host_port if none is
            given).
          - Tries to connect to remote server with selected port.

        Args:
          port: If given, this is the host port from which to connect to remote
              device port. If not provided, find a new available port as host
              port.

        Raises:
            AppRestoreConnectionError: When the app was not able to be started.
        """
        self.host_port = port or utils.get_available_host_port()
        self._adb.forward(
            ['tcp:%d' % self.host_port,
             'tcp:%d' % self.device_port])
        try:
            self.connect()
        except:
            # Failed to connect to app, something went wrong.
            raise jsonrpc_client_base.AppRestoreConnectionError(
                self._ad(
                    'Failed to restore app connection for %s at host port %s, '
                    'device port %s'), self.package, self.host_port,
                self.device_port)

        # Because the previous connection was lost, update self._proc
        self._proc = None
        self._restore_event_client()
Exemple #2
0
    def start_app_and_connect(self):
        """Overrides superclass. Launches a snippet app and connects to it."""
        self._check_app_installed()

        persists_shell_cmd = self._get_persist_command()
        # Try launching the app with the v1 protocol. If that fails, fall back
        # to v0 for compatibility. Use info here so people know exactly what's
        # happening here, which is helpful since they need to create their own
        # instrumentations and manifest.
        self.log.info('Launching snippet apk %s with protocol v1',
                      self.package)
        cmd = _LAUNCH_CMD_V1 % (persists_shell_cmd, self.package)
        start_time = time.time()
        self._proc = self._do_start_app(cmd)

        # "Instrumentation crashed" could be due to several reasons, eg
        # exception thrown during startup or just a launch protocol 0 snippet
        # dying because it needs the port flag. Sadly we have no way to tell so
        # just warn and retry as v0.
        # TODO(adorokhine): delete this in Mobly 1.6 when snippet v0 support is
        # removed.
        line = self._read_protocol_line()
        # Forward the device port to a new host port, and connect to that port
        self.host_port = utils.get_available_host_port()
        if line in ('INSTRUMENTATION_RESULT: shortMsg=Process crashed.',
                    'INSTRUMENTATION_RESULT: shortMsg='
                    'java.lang.IllegalArgumentException'):
            self.log.warning('Snippet %s crashed on startup. This might be an '
                             'actual error or a snippet using deprecated v0 '
                             'start protocol. Retrying as a v0 snippet.',
                             self.package)
            # Reuse the host port as the device port in v0 snippet. This isn't
            # safe in general, but the protocol is deprecated.
            self.device_port = self.host_port
            cmd = _LAUNCH_CMD_V0 % (persists_shell_cmd, self.device_port,
                                    self.package)
            self._proc = self._do_start_app(cmd)
            self._connect_to_v0()
            self._launch_version = 'v0'
        else:
            # Check protocol version and get the device port
            match = re.match('^SNIPPET START, PROTOCOL ([0-9]+) ([0-9]+)$',
                             line)
            if not match or match.group(1) != '1':
                raise ProtocolVersionError(line)

            line = self._read_protocol_line()
            match = re.match('^SNIPPET SERVING, PORT ([0-9]+)$', line)
            if not match:
                raise ProtocolVersionError(line)
            self.device_port = int(match.group(1))
            self._connect_to_v1()
        self.log.debug('Snippet %s started after %.1fs on host port %s',
                       self.package, time.time() - start_time, self.host_port)
Exemple #3
0
    def _start_app_and_connect(self):
        """Starts snippet apk on the device and connects to it.

    After prechecks, this launches the snippet apk with an adb cmd in a
    standing subprocess, checks the cmd response from the apk for protocol
    version, then sets up the socket connection over adb port-forwarding.

    Args:
      ProtocolVersionError, if protocol info or port info cannot be
        retrieved from the snippet apk.
    """
        self._check_app_installed()
        self.disable_hidden_api_blacklist()

        persists_shell_cmd = self._get_persist_command()
        # Use info here so people can follow along with the snippet startup
        # process. Starting snippets can be slow, especially if there are
        # multiple, and this avoids the perception that the framework is hanging
        # for a long time doing nothing.
        self.log.info('Launching snippet apk %s with protocol %d.%d',
                      self.package, _PROTOCOL_MAJOR_VERSION,
                      _PROTOCOL_MINOR_VERSION)
        cmd = _LAUNCH_CMD.format(shell_cmd=persists_shell_cmd,
                                 user=self._get_user_command_string(),
                                 snippet_package=self.package)
        start_time = time.time()
        self._proc = self._do_start_app(cmd)

        # Check protocol version and get the device port
        line = self._read_protocol_line()
        match = re.match('^SNIPPET START, PROTOCOL ([0-9]+) ([0-9]+)$', line)
        if not match or match.group(1) != '1':
            raise ProtocolVersionError(self._ad, line)

        line = self._read_protocol_line()
        match = re.match('^SNIPPET SERVING, PORT ([0-9]+)$', line)
        if not match:
            raise ProtocolVersionError(self._ad, line)
        self.device_port = int(match.group(1))

        # Forward the device port to a new host port, and connect to that port
        self.host_port = utils.get_available_host_port()
        self._adb.forward(
            ['tcp:%d' % self.host_port,
             'tcp:%d' % self.device_port])
        self.connect()

        # Yaaay! We're done!
        self.log.debug('Snippet %s started after %.1fs on host port %s',
                       self.package,
                       time.time() - start_time, self.host_port)
Exemple #4
0
    def start_app_and_connect(self):
        """Overrides superclass."""
        # Check that sl4a is installed
        out = self._adb.shell('pm list package')
        if not utils.grep('com.googlecode.android_scripting', out):
            raise AppStartError('%s is not installed on %s' %
                                (_APP_NAME, self._adb.serial))

        # sl4a has problems connecting after disconnection, so kill the apk and
        # try connecting again.
        try:
            self.stop_app()
        except Exception as e:
            self.log.warning(e)

        # Launch the app
        self.host_port = utils.get_available_host_port()
        self.device_port = _DEVICE_SIDE_PORT
        self._adb.forward(
            ['tcp:%d' % self.host_port,
             'tcp:%d' % self.device_port])
        self._adb.shell(_LAUNCH_CMD % self.device_port)

        # Connect with retry
        start_time = time.time()
        expiration_time = start_time + _APP_START_WAIT_TIME
        started = False
        while time.time() < expiration_time:
            self.log.debug('Attempting to start %s.', self.app_name)
            try:
                self.connect()
                started = True
                break
            except:
                self.log.debug('%s is not yet running, retrying',
                               self.app_name,
                               exc_info=True)
            time.sleep(1)
        if not started:
            raise jsonrpc_client_base.AppStartError(
                '%s failed to start on %s.' %
                (self.app_name, self._adb.serial))

        # Start an EventDispatcher for the current sl4a session
        event_client = Sl4aClient(self._adb, self.log)
        event_client.host_port = self.host_port
        event_client.connect(uid=self.uid,
                             cmd=jsonrpc_client_base.JsonRpcCommand.CONTINUE)
        self.ed = event_dispatcher.EventDispatcher(event_client)
        self.ed.start()
    def load_snippet(self, name, package):
        """Starts the snippet apk with the given package name and connects.

        Examples:
            >>> ad = AndroidDevice()
            >>> ad.load_snippet(
                    name='maps', package='com.google.maps.snippets')
            >>> ad.maps.activateZoom('3')

        Args:
            name: The attribute name to which to attach the snippet server.
                  e.g. name='maps' will attach the snippet server to ad.maps.
            package: The package name defined in AndroidManifest.xml of the
                     snippet apk.

        Raises:
            SnippetError is raised if illegal load operations are attempted.
        """
        # Should not load snippet with the same attribute more than once.
        if name in self._snippet_clients:
            raise SnippetError(
                self,
                'Attribute "%s" is already registered with package "%s", it '
                'cannot be used again.' %
                (name, self._snippet_clients[name].package))
        # Should not load snippet with an existing attribute.
        if hasattr(self, name):
            raise SnippetError(
                self,
                'Attribute "%s" already exists, please use a different name.' %
                name)
        # Should not load the same snippet package more than once.
        for client_name, client in self._snippet_clients.items():
            if package == client.package:
                raise SnippetError(
                    self,
                    'Snippet package "%s" has already been loaded under name'
                    ' "%s".' % (package, client_name))
        host_port = utils.get_available_host_port()
        client = snippet_client.SnippetClient(
            package=package,
            host_port=host_port,
            adb_proxy=self.adb,
            log=self.log)
        self._start_jsonrpc_client(client)
        self._snippet_clients[name] = client
        setattr(self, name, client)
Exemple #6
0
 def test_get_available_port_returns_free_port(
         self, mock_list_occupied_adb_ports):
     """This test checks we can bind to a socket on the port returned by portpicker 
     and should pass as long as we can bind to either an ipv4 or ipv6 socket on 
     such port."""
     port = utils.get_available_host_port()
     got_socket = False
     for family in (socket.AF_INET6, socket.AF_INET):
         try:
             s = socket.socket(family, socket.SOCK_STREAM)
             got_socket = True
         except socket.error:
             continue
         s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
         try:
             s.bind(('localhost', port))
         finally:
             s.close()
     self.assertTrue(got_socket)
Exemple #7
0
    def restore_app_connection(self, port=None):
        """Restores the sl4a after device got disconnected.

        Instead of creating new instance of the client:
          - Uses the given port (or find a new available host_port if none is
            given).
          - Tries to connect to remote server with selected port.

        Args:
          port: If given, this is the host port from which to connect to remote
              device port. If not provided, find a new available port as host
              port.

        Raises:
            AppRestoreConnectionError: When the app was not able to be started.
        """
        self.host_port = port or utils.get_available_host_port()
        self._retry_connect()
        self.ed = self._start_event_client()
Exemple #8
0
    def load_sl4a(self):
        """Start sl4a service on the Android device.

        Launch sl4a server if not already running, spin up a session on the
        server, and two connections to this session.

        Creates an sl4a client (self.sl4a) with one connection, and one
        EventDispatcher obj (self.ed) with the other connection.
        """
        host_port = utils.get_available_host_port()
        self.sl4a = sl4a_client.Sl4aClient(host_port=host_port,
                                           adb_proxy=self.adb)
        self._start_jsonrpc_client(self.sl4a)

        # Start an EventDispatcher for the current sl4a session
        event_client = sl4a_client.Sl4aClient(host_port=host_port,
                                              adb_proxy=self.adb)
        event_client.connect(uid=self.sl4a.uid,
                             cmd=jsonrpc_client_base.JsonRpcCommand.CONTINUE)
        self.ed = event_dispatcher.EventDispatcher(event_client)
        self.ed.start()
Exemple #9
0
    def test_get_available_port_returns_free_port(self, _):
        """Verifies logic to pick a free port on the host.

    Test checks we can bind to either an ipv4 or ipv6 socket on the port
    returned by get_available_host_port.
    """
        port = utils.get_available_host_port()
        got_socket = False
        for family in (socket.AF_INET, socket.AF_INET6):
            try:
                s = socket.socket(family, socket.SOCK_STREAM)
                got_socket = True
                break
            except socket.error:
                continue
        self.assertTrue(got_socket)
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        try:
            s.bind(('localhost', port))
        finally:
            s.close()
Exemple #10
0
    def start_app_and_connect(self):
        """Overrides superclass. Launches a snippet app and connects to it."""
        self._check_app_installed()
        self.disable_hidden_api_blacklist()

        persists_shell_cmd = self._get_persist_command()
        # Use info here so people can follow along with the snippet startup
        # process. Starting snippets can be slow, especially if there are
        # multiple, and this avoids the perception that the framework is hanging
        # for a long time doing nothing.
        self.log.info('Launching snippet apk %s with protocol %d.%d',
                      self.package, _PROTOCOL_MAJOR_VERSION,
                      _PROTOCOL_MINOR_VERSION)
        cmd = _LAUNCH_CMD % (persists_shell_cmd, self.package)
        start_time = time.time()
        self._proc = self._do_start_app(cmd)

        # Check protocol version and get the device port
        line = self._read_protocol_line()
        match = re.match('^SNIPPET START, PROTOCOL ([0-9]+) ([0-9]+)$', line)
        if not match or match.group(1) != '1':
            raise ProtocolVersionError(self._ad, line)

        line = self._read_protocol_line()
        match = re.match('^SNIPPET SERVING, PORT ([0-9]+)$', line)
        if not match:
            raise ProtocolVersionError(self._ad, line)
        self.device_port = int(match.group(1))

        # Forward the device port to a new host port, and connect to that port
        self.host_port = utils.get_available_host_port()
        self._adb.forward(
            ['tcp:%d' % self.host_port,
             'tcp:%d' % self.device_port])
        self.connect()

        # Yaaay! We're done!
        self.log.debug('Snippet %s started after %.1fs on host port %s',
                       self.package,
                       time.time() - start_time, self.host_port)
Exemple #11
0
    def get_droid(self, handle_event=True):
        """Create an sl4a connection to the device.

        Return the connection handler 'droid'. By default, another connection
        on the same session is made for EventDispatcher, and the dispatcher is
        returned to the caller as well.
        If sl4a server is not started on the device, try to start it.

        Args:
            handle_event: True if this droid session will need to handle
                events.

        Returns:
            droid: Android object used to communicate with sl4a on the android
                device.
            ed: An optional EventDispatcher to organize events for this droid.

        Examples:
            Don't need event handling:
            >>> ad = AndroidDevice()
            >>> droid = ad.get_droid(False)

            Need event handling:
            >>> ad = AndroidDevice()
            >>> droid, ed = ad.get_droid()
        """
        if not self.h_port or not utils.is_port_available(self.h_port):
            self.h_port = utils.get_available_host_port()
        self.adb.tcp_forward(self.h_port, self.d_port)

        try:
            droid = self.start_new_session()
        except:
            sl4a_client.start_sl4a(self.adb)
            droid = self.start_new_session()

        if handle_event:
            ed = self.get_dispatcher(droid)
            return droid, ed
        return droid
Exemple #12
0
 def test_get_available_port_negative(self, mock_list_occupied_adb_ports,
                                      mock_pick_unused_port):
     with self.assertRaisesRegex(utils.Error, 'Failed to find.* retries'):
         utils.get_available_host_port()
Exemple #13
0
 def test_get_available_port_positive(self, mock_list_occupied_adb_ports,
                                      mock_pick_unused_port):
     self.assertEqual(utils.get_available_host_port(), MOCK_AVAILABLE_PORT)
Exemple #14
0
 def _forward_device_port(self):
     """Forwards the device port to a host port."""
     if not self.host_port:
         self.host_port = utils.get_available_host_port()
     self._adb.forward([f'tcp:{self.host_port}', f'tcp:{self.device_port}'])
Exemple #15
0
 def test_get_available_port_negative(self, *_):
     with self.assertRaisesRegex(utils.Error, 'Failed to find.* retries'):
         utils.get_available_host_port()
Exemple #16
0
 def test_get_available_port_positive_no_adb(self,
                                             mock_list_occupied_adb_ports,
                                             *_):
     self.assertEqual(utils.get_available_host_port(), MOCK_AVAILABLE_PORT)
     mock_list_occupied_adb_ports.assert_not_called()
Exemple #17
0
 def test_get_available_port_positive(self, *_):
     self.assertEqual(utils.get_available_host_port(), MOCK_AVAILABLE_PORT)