Пример #1
0
    def Start(self, startup_args, startup_url=None):
        self.device.adb.Logcat(clear=True)
        self.platform_backend.DismissCrashDialogIfNeeded()
        user_agent_dict = user_agent.GetChromeUserAgentDictFromType(
            self.browser_options.browser_user_agent_type)
        command_line_name = self._backend_settings.command_line_name
        with flag_changer.CustomCommandLineFlags(self.device,
                                                 command_line_name,
                                                 startup_args):
            # Stop existing browser, if any. This is done *after* setting the
            # command line flags, in case some other Android process manages to
            # trigger Chrome's startup before we do.
            self._StopBrowser()
            self._SetupProfile()
            self.device.StartActivity(intent.Intent(
                package=self._backend_settings.package,
                activity=self._backend_settings.activity,
                action=None,
                data=startup_url,
                category=None,
                extras=user_agent_dict),
                                      blocking=True)

            try:
                self.BindDevToolsClient()
            except:
                self.Close()
                raise
Пример #2
0
def main():
    adapter = WPTAndroidAdapter()
    adapter.parse_args()

    if adapter.options.verbose:
        if adapter.options.verbose == 1:
            logger.setLevel(logging.INFO)
        else:
            logger.setLevel(logging.DEBUG)

    devil_chromium.Initialize()

    # Only 1 device is supported for Android locally, this will work well with
    # sharding support via swarming infra.
    device = device_utils.DeviceUtils.HealthyDevices()[0]

    flags_file = FLAGS_FILE_MAP[adapter.options.product]
    all_flags = HOST_RESOLVER_ARGS + adapter.pass_through_binary_args
    logger.info('Setting flags in ' + flags_file + ' to: ' + str(all_flags))
    flags = flag_changer.CustomCommandLineFlags(device, flags_file, all_flags)

    # WPT setup for chrome and webview requires that PATH contains adb.
    platform_tools_path = os.path.dirname(devil_env.config.FetchPath('adb'))
    os.environ['PATH'] = ':'.join([platform_tools_path] +
                                  os.environ['PATH'].split(':'))

    with flags:
        if adapter.options.product == 'android_webview':
            run_android_webview(device, adapter)
        elif adapter.options.product == 'chrome_android':
            run_chrome_android(device, adapter)
Пример #3
0
def _Setup(device, database_filename):
    """Sets up a device and returns an instance of RemoteChromeController."""
    chrome_controller = prefetch_predictor_common.Setup(device)
    chrome_package = OPTIONS.ChromePackage()
    device.ForceStop(chrome_package.package)
    chrome_controller.ResetBrowserState()
    device_database_filename = prefetch_predictor_common.DatabaseDevicePath()
    owner = group = None

    # Make sure that the speculative prefetch predictor is enabled to ensure
    # that the disk database is re-created.
    with flag_changer.CustomCommandLineFlags(device,
                                             chrome_package.cmdline_file,
                                             ['--disable-fre']):
        # Launch Chrome for the first time to recreate the local state.
        launch_intent = intent.Intent(action='android.intent.action.MAIN',
                                      package=chrome_package.package,
                                      activity=chrome_package.activity)
        device.StartActivity(launch_intent, blocking=True)
        time.sleep(5)
        device.ForceStop(chrome_package.package)
        assert device.FileExists(device_database_filename)
        stats = device.StatPath(device_database_filename)
        owner = stats['st_owner']
        group = stats['st_group']
    # Now push the database. Needs to be done after the first launch, otherwise
    # the profile directory is owned by root. Also change the owner of the
    # database, since adb push sets it to root.
    database_content = open(database_filename, 'r').read()
    device.WriteFile(device_database_filename,
                     database_content,
                     force_push=True)
    command = 'chown %s:%s \'%s\'' % (owner, group, device_database_filename)
    device.RunShellCommand(command, as_root=True)
Пример #4
0
    def Start(self):
        self.device.adb.Logcat(clear=True)
        if self.browser_options.startup_url:
            url = self.browser_options.startup_url
        elif self.browser_options.profile_dir:
            url = None
        else:
            # If we have no existing tabs start with a blank page since default
            # startup with the NTP can lead to race conditions with Telemetry
            url = 'about:blank'

        self.platform_backend.DismissCrashDialogIfNeeded()

        user_agent_dict = user_agent.GetChromeUserAgentDictFromType(
            self.browser_options.browser_user_agent_type)

        browser_startup_args = self.GetBrowserStartupArgs()
        command_line_name = self._backend_settings.command_line_name
        with flag_changer.CustomCommandLineFlags(self.device,
                                                 command_line_name,
                                                 browser_startup_args):
            # Stop existing browser, if any. This is done *after* setting the
            # command line flags, in case some other Android process manages to
            # trigger Chrome's startup before we do.
            self._StopBrowser()
            self._SetupProfile()

            self.device.StartActivity(intent.Intent(
                package=self._backend_settings.package,
                activity=self._backend_settings.activity,
                action=None,
                data=url,
                category=None,
                extras=user_agent_dict),
                                      blocking=True)

            remote_devtools_port = self._backend_settings.GetDevtoolsRemotePort(
                self.device)
            # Setting local_port=0 allows the forwarder to pick an available port.
            self._forwarder = self.platform_backend.forwarder_factory.Create(
                forwarders.PortPair(0, remote_devtools_port), reverse=True)
            self._port = self._forwarder.port_pair.local_port

            try:
                self._WaitForBrowserToComeUp(remote_devtools_port)
            except exceptions.BrowserGoneException:
                logging.critical('Failed to connect to browser.')
                if not (self.device.HasRoot() or self.device.NeedsSU()):
                    logging.critical(
                        'Resolve this by either: '
                        '(1) Flashing to a userdebug build OR '
                        '(2) Manually enabling web debugging in Chrome at '
                        'Settings > Developer tools > Enable USB Web debugging.'
                    )
                self.Close()
                raise
            except:
                self.Close()
                raise
    def testFlagChanger_removeFlags(self):
        self.device.RemovePath(self.cmdline_path, force=True)
        self.assertFalse(self.device.PathExists(self.cmdline_path))

        with flag_changer.CustomCommandLineFlags(self.device, _CMDLINE_FILE,
                                                 ['--some', '--flags']):
            self.assertTrue(self.device.PathExists(self.cmdline_path))

        self.assertFalse(self.device.PathExists(self.cmdline_path))
Пример #6
0
    def Start(self):
        """Start an Android app and wait for it to finish launching.

    If the app has webviews, the app is launched with the suitable
    command line arguments.

    AppStory derivations can customize the wait-for-ready-state to wait
    for a more specific event if needed.
    """
        if self._app_has_webviews:
            webview_startup_args = self.GetWebviewStartupArgs()
            command_line_name = (android_browser_backend_settings.
                                 ANDROID_WEBVIEW.command_line_name)
            with flag_changer.CustomCommandLineFlags(self.device,
                                                     command_line_name,
                                                     webview_startup_args):
                self._LaunchAndWaitForApplication()
        else:
            self._LaunchAndWaitForApplication()
        self._is_running = True
Пример #7
0
    def Start(self):
        self.device.adb.Logcat(clear=True)
        if self.browser_options.startup_url:
            url = self.browser_options.startup_url
        elif self.browser_options.profile_dir:
            url = None
        else:
            # If we have no existing tabs start with a blank page since default
            # startup with the NTP can lead to race conditions with Telemetry
            url = 'about:blank'

        self.platform_backend.DismissCrashDialogIfNeeded()

        user_agent_dict = user_agent.GetChromeUserAgentDictFromType(
            self.browser_options.browser_user_agent_type)

        browser_startup_args = self.GetBrowserStartupArgs()
        command_line_name = self._backend_settings.command_line_name
        with flag_changer.CustomCommandLineFlags(self.device,
                                                 command_line_name,
                                                 browser_startup_args):
            # Stop existing browser, if any. This is done *after* setting the
            # command line flags, in case some other Android process manages to
            # trigger Chrome's startup before we do.
            self._StopBrowser()
            self._SetupProfile()

            self.device.StartActivity(intent.Intent(
                package=self._backend_settings.package,
                activity=self._backend_settings.activity,
                action=None,
                data=url,
                category=None,
                extras=user_agent_dict),
                                      blocking=True)

            try:
                self.BindDevToolsClient()
            except:
                self.Close()
                raise
Пример #8
0
def main():
    adapter = WPTAndroidAdapter()
    adapter.parse_args()

    # Only 1 device is supported for Android locally, this will work well with
    # sharding support via swarming infra.
    device = device_utils.DeviceUtils.HealthyDevices()[0]

    flags_file = FLAGS_FILE_MAP[adapter.options.product]
    all_flags = HOST_RESOLVER_ARGS + adapter.options.binary_arg
    flags = flag_changer.CustomCommandLineFlags(device, flags_file, all_flags)

    # WPT setup for chrome and webview requires that PATH contains adb.
    os.environ['PATH'] = ':'.join([PLATFORM_TOOLS] +
                                  os.environ['PATH'].split(':'))

    with flags:
        if adapter.options.product == 'android_webview':
            run_android_webview(device, adapter)
        elif adapter.options.product == 'chrome_android':
            run_chrome_android(device, adapter)
Пример #9
0
def RunChrome(device, cold, chrome_args, package_info):
    """Runs Chrome on the device.

  Args:
    device: (DeviceUtils) device to run the tests on.
    cold: (bool) Whether caches should be dropped.
    chrome_args: ([str]) List of arguments to pass to Chrome.
    package_info: (PackageInfo) Chrome package info.
  """
    if not device.HasRoot():
        device.EnableRoot()

    cmdline_file = package_info.cmdline_file
    package = package_info.package
    with flag_changer.CustomCommandLineFlags(device, cmdline_file,
                                             chrome_args):
        device.ForceStop(package)

        if cold:
            chrome_setup.ResetChromeLocalState(device, package)
            cache_control.CacheControl(device).DropRamCaches()

        start_intent = intent.Intent(package=package,
                                     data='about:blank',
                                     activity=package_info.activity)
        try:
            device.StartActivity(start_intent, blocking=True)
            print('\n\n'
                  '   +---------------------------------------------+\n'
                  '   | Chrome launched, press Ctrl-C to interrupt. |\n'
                  '   +---------------------------------------------+')
            while True:
                time.sleep(1)
        except KeyboardInterrupt:
            pass
        finally:
            device.ForceStop(package)
Пример #10
0
    def Start(self):
        self.device.adb.Logcat(clear=True)
        if self.browser_options.startup_url:
            url = self.browser_options.startup_url
        elif self.browser_options.profile_dir:
            url = None
        else:
            # If we have no existing tabs start with a blank page since default
            # startup with the NTP can lead to race conditions with Telemetry
            url = 'about:blank'

        self.platform_backend.DismissCrashDialogIfNeeded()

        user_agent_dict = user_agent.GetChromeUserAgentDictFromType(
            self.browser_options.browser_user_agent_type)

        browser_startup_args = self.GetBrowserStartupArgs()
        command_line_name = self._backend_settings.command_line_name
        with flag_changer.CustomCommandLineFlags(self.device,
                                                 command_line_name,
                                                 browser_startup_args):
            # Stop existing browser, if any. This is done *after* setting the
            # command line flags, in case some other Android process manages to
            # trigger Chrome's startup before we do.
            self._StopBrowser()

            if self.device.HasRoot() or self.device.NeedsSU():
                if self.browser_options.profile_dir:
                    self.platform_backend.PushProfile(
                        self._backend_settings.package,
                        self.browser_options.profile_dir)
                elif not self.browser_options.dont_override_profile:
                    self.platform_backend.RemoveProfile(
                        self._backend_settings.package,
                        self._backend_settings.profile_ignore_list)

            self.device.StartActivity(intent.Intent(
                package=self._backend_settings.package,
                activity=self._backend_settings.activity,
                action=None,
                data=url,
                category=None,
                extras=user_agent_dict),
                                      blocking=True)

            # TODO(crbug.com/404771): Move port forwarding to network_controller.
            remote_devtools_port = self._backend_settings.GetDevtoolsRemotePort(
                self.device)
            try:
                # Release reserved port right before forwarding host to device.
                self._port_keeper.Release()
                assert self._port == self._port_keeper.port, (
                    'Android browser backend must use reserved port by _port_keeper'
                )
                self.platform_backend.ForwardHostToDevice(
                    self._port, remote_devtools_port)
            except Exception:
                logging.exception('Failed to forward %s to %s.',
                                  str(self._port), str(remote_devtools_port))
                logging.warning('Currently forwarding:')
                try:
                    for line in self.device.adb.ForwardList().splitlines():
                        logging.warning('  %s', line)
                except Exception:  # pylint: disable=broad-except
                    logging.warning('Exception raised while listing forwarded '
                                    'connections.')

                logging.warning('Host tcp ports in use:')
                try:
                    for line in subprocess.check_output(['netstat',
                                                         '-t']).splitlines():
                        logging.warning('  %s', line)
                except Exception:  # pylint: disable=broad-except
                    logging.warning(
                        'Exception raised while listing tcp ports.')

                logging.warning('Device unix domain sockets in use:')
                try:
                    for line in self.device.ReadFile(
                            '/proc/net/unix', as_root=True,
                            force_pull=True).splitlines():
                        logging.warning('  %s', line)
                except Exception:  # pylint: disable=broad-except
                    logging.warning(
                        'Exception raised while listing unix domain sockets.')

                raise

            try:
                self._WaitForBrowserToComeUp(remote_devtools_port)
            except exceptions.BrowserGoneException:
                logging.critical('Failed to connect to browser.')
                if not (self.device.HasRoot() or self.device.NeedsSU()):
                    logging.critical(
                        'Resolve this by either: '
                        '(1) Flashing to a userdebug build OR '
                        '(2) Manually enabling web debugging in Chrome at '
                        'Settings > Developer tools > Enable USB Web debugging.'
                    )
                self.Close()
                raise
            except:
                self.Close()
                raise
Пример #11
0
def RunOnce(device, url, speculated_url, parallel_url, warmup,
            skip_launcher_activity, speculation_mode, delay_to_may_launch_url,
            delay_to_launch_url, cold, pinning_benchmark, pin_filename,
            pin_offset, pin_length, extra_brief_memory_mb, chrome_args,
            reset_chrome_state):
    """Runs a test on a device once.

  Args:
    device: (DeviceUtils) device to run the tests on.
    url: (str) URL to load. End of the redirect chain when using a
        parallel request.
    speculated_url: (str) Speculated URL.
    parallel_url: ([str]) URL to load in parallel, typically
        the start of the redirect chain.
    warmup: (bool) Whether to call warmup.
    skip_launcher_activity: (bool) Whether to skip the launcher activity.
    speculation_mode: (str) Speculation Mode.
    delay_to_may_launch_url: (int) Delay to mayLaunchUrl() in ms.
    delay_to_launch_url: (int) Delay to launchUrl() in ms.
    cold: (bool) Whether the page cache should be dropped.
    pinning_benchmark: (bool) Whether to perform the 'pinning benchmark'.
    pin_filename: (str) The file to pin on the device.
    pin_offset: (int) Start offset of the range to pin.
    pin_length: (int) Number of bytes to pin.
    extra_brief_memory_mb: (int) Number of MiB to consume before starting
        Chrome. Applies only to the 'pinning benchmark' scenario.
    chrome_args: ([str]) List of arguments to pass to Chrome.
    reset_chrome_state: (bool) Whether to reset the Chrome local state before
        the run.

  Returns:
    The output line (str), like this (one line only):
    <warmup>,<prerender_mode>,<delay_to_may_launch_url>,<delay_to_launch>,
      <intent_sent_ms>,<page_load_started_ms>,<page_load_finished_ms>,
      <first_contentful_paint>
    or None on error.
  """
    if not device.HasRoot():
        device.EnableRoot()

    timeout_s = 64
    logcat_timeout = int(timeout_s + delay_to_may_launch_url / 1000. +
                         delay_to_launch_url / 1000.)

    with flag_changer.CustomCommandLineFlags(device, _COMMAND_LINE_FILE,
                                             chrome_args):
        launch_intent = intent.Intent(
            action='android.intent.action.MAIN',
            package=_TEST_APP_PACKAGE_NAME,
            activity='org.chromium.customtabs.test.MainActivity',
            extras={
                'url': str(url),
                'speculated_url': str(speculated_url),
                'parallel_url': str(parallel_url),
                'warmup': warmup,
                'skip_launcher_activity': skip_launcher_activity,
                'speculation_mode': str(speculation_mode),
                'delay_to_may_launch_url': delay_to_may_launch_url,
                'delay_to_launch_url': delay_to_launch_url,
                'pinning_benchmark': pinning_benchmark,
                'pin_filename': str(pin_filename),
                'pin_offset': pin_offset,
                'pin_length': pin_length,
                'extra_brief_memory_mb': extra_brief_memory_mb,
                'timeout': timeout_s
            })
        result_line_re = re.compile(r'CUSTOMTABSBENCHCSV.*: (.*)')
        logcat_monitor = device.GetLogcatMonitor(clear=True)
        logcat_monitor.Start()
        device.ForceStop(_CHROME_PACKAGE)
        device.ForceStop(_TEST_APP_PACKAGE_NAME)

        if reset_chrome_state:
            chrome_setup.ResetChromeLocalState(device, _CHROME_PACKAGE)

        if cold:
            cache_control.CacheControl(device).DropRamCaches()

        device.StartActivity(launch_intent, blocking=True)

        match = None
        try:
            match = logcat_monitor.WaitFor(result_line_re,
                                           timeout=logcat_timeout)
        except device_errors.CommandTimeoutError as _:
            logging.warning('Timeout waiting for the result line')
        logcat_monitor.Stop()
        logcat_monitor.Close()
        return match.group(1) if match is not None else None
Пример #12
0
def main():
    parser = argparse.ArgumentParser(description="""
Configures WebView to start recording a netlog. This script chooses a suitable
netlog filename for the application, and will pull the netlog off the device
when the user terminates the script (with ctrl-C). For a more complete usage
guide, open your web browser to:
https://chromium.googlesource.com/chromium/src/+/HEAD/android_webview/docs/net-debugging.md
""")
    parser.add_argument(
        '--package',
        required=True,
        type=str,
        help='Package name of the application you intend to use.')
    parser.add_argument('--force',
                        default=False,
                        action='store_true',
                        help='Suppress user checks.')

    script_common.AddEnvironmentArguments(parser)
    script_common.AddDeviceArguments(parser)
    logging_common.AddLoggingArguments(parser)

    args = parser.parse_args()
    logging_common.InitializeLogging(args)
    devil_chromium.Initialize(adb_path=args.adb_path)

    # Only use a single device, for the sake of simplicity (of implementation and
    # user experience).
    devices = device_utils.DeviceUtils.HealthyDevices(device_arg=args.devices)
    device = devices[0]
    if len(devices) > 1:
        raise device_errors.MultipleDevicesError(devices)

    package_name = args.package
    device_netlog_file_name = 'netlog.json'
    device_netlog_path = os.path.join(
        device.GetApplicationDataDirectory(package_name), 'app_webview',
        device_netlog_file_name)

    CheckAppNotRunning(device, package_name, args.force)

    # Append to the existing flags, to allow users to experiment with other
    # features/flags enabled. The CustomCommandLineFlags will restore the original
    # flag state after the user presses 'ctrl-C'.
    changer = flag_changer.FlagChanger(device, WEBVIEW_COMMAND_LINE)
    new_flags = changer.GetCurrentFlags()
    new_flags.append('--log-net-log={}'.format(device_netlog_path))

    logging.info('Running with flags %r', new_flags)
    with flag_changer.CustomCommandLineFlags(device, WEBVIEW_COMMAND_LINE,
                                             new_flags):
        print(
            'Netlog will start recording as soon as app starts up. Press ctrl-C '
            'to stop recording.')
        _WaitUntilCtrlC()

    host_netlog_path = 'netlog.json'
    print('Pulling netlog to "%s"' % host_netlog_path)
    # The netlog file will be under the app's uid, which the default shell doesn't
    # have permission to read (but root does). Prefer this to EnableRoot(), which
    # restarts the adb daemon.
    device.PullFile(device_netlog_path, host_netlog_path, as_root=True)
    device.RemovePath(device_netlog_path, as_root=True)
Пример #13
0
 def Open(self):
     """Overridden connection creation."""
     if self._wpr_attributes:
         assert self._wpr_attributes.chrome_env_override == {}, \
             'Remote controller doesn\'t support chrome environment variables.'
     package_info = OPTIONS.ChromePackage()
     self._device.ForceStop(package_info.package)
     with flag_changer.CustomCommandLineFlags(self._device,
                                              package_info.cmdline_file,
                                              self._GetChromeArguments()):
         self._DismissCrashDialogIfNeeded()
         start_intent = intent.Intent(package=package_info.package,
                                      activity=package_info.activity,
                                      data='about:blank')
         self._device.adb.Logcat(clear=True, dump=True)
         self._device.StartActivity(start_intent, blocking=True)
         try:
             for attempt_id in xrange(self.DEVTOOLS_CONNECTION_ATTEMPTS):
                 logging.info('Devtools connection attempt %d' % attempt_id)
                 # Adb forwarding does not provide a way to print the port number if
                 # it is allocated atomically by the OS by passing port=0, but we need
                 # dynamically allocated listening port here to handle parallel run on
                 # different devices.
                 host_side_port = _AllocateTcpListeningPort()
                 logging.info(
                     'Allocated host sided listening port for devtools '
                     'connection: %d', host_side_port)
                 try:
                     with device_setup.ForwardPort(
                             self._device, 'tcp:%d' % host_side_port,
                             'localabstract:chrome_devtools_remote'):
                         try:
                             connection = devtools_monitor.DevToolsConnection(
                                 OPTIONS.devtools_hostname, host_side_port)
                             self._StartConnection(connection)
                         except socket.error as e:
                             if e.errno != errno.ECONNRESET:
                                 raise
                             time.sleep(
                                 self.
                                 DEVTOOLS_CONNECTION_ATTEMPT_INTERVAL_SECONDS
                             )
                             continue
                         yield connection
                         if self._slow_death:
                             self._device.adb.Shell(
                                 'am start com.google.android.launcher')
                             time.sleep(self.TIME_TO_IDLE_SECONDS)
                         break
                 except device_errors.AdbCommandFailedError as error:
                     _KNOWN_ADB_FORWARDER_FAILURES = [
                         'cannot bind to socket: Address already in use',
                         'cannot rebind existing socket: Resource temporarily unavailable'
                     ]
                     for message in _KNOWN_ADB_FORWARDER_FAILURES:
                         if message in error.message:
                             break
                     else:
                         raise
                     continue
             else:
                 raise ChromeControllerInternalError(
                     'Failed to connect to Chrome devtools after {} '
                     'attempts.'.format(self.DEVTOOLS_CONNECTION_ATTEMPTS))
         except ChromeControllerError._PASSTHROUGH_WHITE_LIST:
             raise
         except Exception:
             logcat = ''.join(
                 [l + '\n' for l in self._device.adb.Logcat(dump=True)])
             raise ChromeControllerError(log=logcat)
         finally:
             self._device.ForceStop(package_info.package)
             self._DismissCrashDialogIfNeeded()
Пример #14
0
def RunOnce(device, url, warmup, speculation_mode, delay_to_may_launch_url,
            delay_to_launch_url, cold, chrome_args, reset_chrome_state):
    """Runs a test on a device once.

  Args:
    device: (DeviceUtils) device to run the tests on.
    url: (str) URL to load.
    warmup: (bool) Whether to call warmup.
    speculation_mode: (str) Speculation Mode.
    delay_to_may_launch_url: (int) Delay to mayLaunchUrl() in ms.
    delay_to_launch_url: (int) Delay to launchUrl() in ms.
    cold: (bool) Whether the page cache should be dropped.
    chrome_args: ([str]) List of arguments to pass to Chrome.
    reset_chrome_state: (bool) Whether to reset the Chrome local state before
                        the run.

  Returns:
    The output line (str), like this (one line only):
    <warmup>,<prerender_mode>,<delay_to_may_launch_url>,<delay_to_launch>,
      <intent_sent_ms>,<page_load_started_ms>,<page_load_finished_ms>,
      <first_contentful_paint>
    or None on error.
  """
    if not device.HasRoot():
        device.EnableRoot()

    timeout_s = 20
    logcat_timeout = int(timeout_s + delay_to_may_launch_url / 1000. +
                         delay_to_launch_url / 1000.) + 3

    with flag_changer.CustomCommandLineFlags(device, _COMMAND_LINE_FILE,
                                             chrome_args):
        launch_intent = intent.Intent(
            action='android.intent.action.MAIN',
            package=_TEST_APP_PACKAGE_NAME,
            activity='org.chromium.customtabs.test.MainActivity',
            extras={
                'url': str(url),
                'warmup': warmup,
                'speculation_mode': str(speculation_mode),
                'delay_to_may_launch_url': delay_to_may_launch_url,
                'delay_to_launch_url': delay_to_launch_url,
                'timeout': timeout_s
            })
        result_line_re = re.compile(r'CUSTOMTABSBENCH.*: (.*)')
        logcat_monitor = device.GetLogcatMonitor(clear=True)
        logcat_monitor.Start()
        device.ForceStop(_CHROME_PACKAGE)
        device.ForceStop(_TEST_APP_PACKAGE_NAME)

        if reset_chrome_state:
            chrome_setup.ResetChromeLocalState(device, _CHROME_PACKAGE)

        if cold:
            cache_control.CacheControl(device).DropRamCaches()

        device.StartActivity(launch_intent, blocking=True)

        match = None
        try:
            match = logcat_monitor.WaitFor(result_line_re,
                                           timeout=logcat_timeout)
        except device_errors.CommandTimeoutError as _:
            logging.warning('Timeout waiting for the result line')
        logcat_monitor.Stop()
        logcat_monitor.Close()
        return match.group(1) if match is not None else None