def _SetupTracing(self): # TODO(lizeb): Figure out how to clean up the command-line file when # _TearDownTracing() is not executed in StopTracing(). flags = ['--trace-startup', '--enable-perfetto'] if self._trace_time is not None: flags.append('--trace-startup-duration={}'.format( self._trace_time)) self._flag_changer.AddFlags(flags) self._device.ForceStop(self._package_info.package) if self._webapk_package: self._device.ForceStop(self._webapk_package) logging.warning( 'Forces to stop the WebAPK and the browser provided by ' '--browser: %s. Please make sure that this browser ' 'matches the host browser of the WebAPK %s. ', self._package_info.package, self._webapk_package) if self._cold: self._device.EnableRoot() cache_control.CacheControl(self._device).DropRamCaches() launch_intent = None if self._webapk_package: launch_intent = intent.Intent(package=self._webapk_package, activity=webapk.WEBAPK_MAIN_ACTIVITY, data=self._url) elif self._url == '': launch_intent = intent.Intent(action='android.intent.action.MAIN', package=self._package_info.package, activity=self._package_info.activity) else: launch_intent = intent.Intent(package=self._package_info.package, activity=self._package_info.activity, data=self._url, extras={'create_new_tab': True}) self._logcat_monitor.Start() self._device.StartActivity(launch_intent, blocking=True)
def _RunTest(self, device, test): device.ClearApplicationState(self._test_instance.package) # Chrome crashes are not always caught by Monkey test runner. # Launch Chrome and verify Chrome has the same PID before and after # the test. device.StartActivity( intent.Intent(package=self._test_instance.package, activity=self._test_instance.activity, action='android.intent.action.MAIN'), blocking=True, force_stop=True) before_pids = device.GetPids(self._test_instance.package) output = '' if before_pids: if len(before_pids.get(self._test_instance.package, [])) > 1: raise Exception( 'At most one instance of process %s expected but found pids: ' '%s' % (self._test_instance.package, before_pids)) output = '\n'.join(self._LaunchMonkeyTest(device)) after_pids = device.GetPids(self._test_instance.package) crashed = True if not self._test_instance.package in before_pids: logging.error('Failed to start the process.') elif not self._test_instance.package in after_pids: logging.error('Process %s has died.', before_pids[self._test_instance.package]) elif (before_pids[self._test_instance.package] != after_pids[self._test_instance.package]): logging.error('Detected process restart %s -> %s', before_pids[self._test_instance.package], after_pids[self._test_instance.package]) else: crashed = False success_pattern = 'Events injected: %d' % self._test_instance.event_count if success_pattern in output and not crashed: result = base_test_result.BaseTestResult( test, base_test_result.ResultType.PASS, log=output) else: result = base_test_result.BaseTestResult( test, base_test_result.ResultType.FAIL, log=output) if 'chrome' in self._test_instance.package: logging.warning('Starting MinidumpUploadService...') # TODO(jbudorick): Update this after upstreaming. minidump_intent = intent.Intent( action='%s.crash.ACTION_FIND_ALL' % _CHROME_PACKAGE, package=self._test_instance.package, activity='%s.crash.MinidumpUploadService' % _CHROME_PACKAGE) try: device.RunShellCommand( ['am', 'startservice'] + minidump_intent.am_args, as_root=True, check_return=True) except device_errors.CommandFailedError: logging.exception('Failed to start MinidumpUploadService') return result, None
def launch(device): # Set debug app in order to enable reading command line flags on user # builds. cmd = ['am', 'set-debug-app', debug_process_name] if wait_for_java_debugger: cmd[-1:-1] = ['-w'] # Ignore error since it will fail if apk is not debuggable. device.RunShellCommand(cmd, check_return=False) # The flags are first updated with input args. if command_line_flags_file: changer = flag_changer.FlagChanger(device, command_line_flags_file) flags = [] if argv: flags = shlex.split(argv) changer.ReplaceFlags(flags) # Then launch the apk. if url is None: # Simulate app icon click if no url is present. cmd = [ 'monkey', '-p', package_name, '-c', 'android.intent.category.LAUNCHER', '1' ] device.RunShellCommand(cmd, check_return=True) else: launch_intent = intent.Intent(action='android.intent.action.VIEW', activity=view_activity, data=url, package=package_name) device.StartActivity(launch_intent)
def setUp(self): self.platform_backend = mock.Mock() self.start_intent = intent_module.Intent( package='com.example.my_app', activity='com.example.my_app.LaunchMyApp') self.app_backend = android_app_backend.AndroidAppBackend( self.platform_backend, self.start_intent)
def testWebView(self): if self._device is None: logging.warning('No presentation.device found, skipping test.') return start_intent = intent.Intent( package='com.google.android.googlequicksearchbox', activity='.SearchActivity', action='com.google.android.googlequicksearchbox.GOOGLE_SEARCH', data=None, extras={'query': 'google'}, category=None) search_app = self.CreateAndroidApp(start_intent) search_process = search_app.GetProcess(':search') search_process._UpdateDevToolsClient() # TODO(ariblue): Replace the app used in this test with one in which the # setWebContentsDebuggingEnabled method is called on the WebView class. # This will configure webviews for debugging with chrome devtools inspector # and allow us to remove this check. if search_process._devtools_client is None: return webview = search_app.GetProcess(':search').GetWebViews().pop() webview.Navigate('https://www.google.com/search?q=flowers') time.sleep(5)
def StartAgentTracing(self, config, timeout=None): self._categories = _ComputeChromeCategories(config) self._logcat_monitor.Start() start_extras = {'categories': ','.join(self._categories)} if self._ring_buffer: start_extras['continuous'] = None self._device.BroadcastIntent( intent.Intent(action='%s.GPU_PROFILER_START' % self._package_info.package, extras=start_extras)) if self._trace_memory: self._device.EnableRoot() self._device.SetProp(_HEAP_PROFILE_MMAP_PROPERTY, 1) # Chrome logs two different messages related to tracing: # # 1. "Logging performance trace to file" # 2. "Profiler finished. Results are in [...]" # # The first one is printed when tracing starts and the second one indicates # that the trace file is ready to be pulled. try: self._logcat_monitor.WaitFor(self._trace_start_re, timeout=5) self._is_tracing = True except device_errors.CommandTimeoutError: raise RuntimeError( 'Trace start marker not found. Possible causes: 1) Is the correct ' 'version of the browser running? 2) Is the browser already launched?' ) return True
def _StartActivityAndWaitForLinkerTestStatus(device, timeout): """Force-start an activity and wait up to |timeout| seconds until the full linker test status lines appear in the logcat, recorded through |device|. Args: device: A DeviceUtils instance. timeout: Timeout in seconds Returns: A (status, logs) tuple, where status is a ResultType constant, and logs if the final logcat output as a string. """ # 1. Start recording logcat with appropriate filters. with device.GetLogcatMonitor(filter_specs=_LOGCAT_FILTERS) as logmon: # 2. Force-start activity. device.StartActivity( intent.Intent(package=_PACKAGE_NAME, activity=_ACTIVITY_NAME), force_stop=True) # 3. Wait up to |timeout| seconds until the test status is in the logcat. result = ResultType.PASS try: browser_match = logmon.WaitFor(_RE_BROWSER_STATUS_LINE, timeout=timeout) logging.debug('Found browser match: %s', browser_match.group(0)) renderer_match = logmon.WaitFor(_RE_RENDERER_STATUS_LINE, timeout=timeout) logging.debug('Found renderer match: %s', renderer_match.group(0)) if (browser_match.group(1) != 'SUCCESS' or renderer_match.group(1) != 'SUCCESS'): result = ResultType.FAIL except device_errors.CommandTimeoutError: result = ResultType.TIMEOUT return result, '\n'.join(device.adb.Logcat(dump=True))
def GetCategories(device, package_info): with device.GetLogcatMonitor() as logmon: device.BroadcastIntent( intent.Intent(action='%s.GPU_PROFILER_LIST_CATEGORIES' % package_info.package)) try: json_category_list = logmon.WaitFor( re.compile(r'{"traceCategoriesList(.*)'), timeout=5).group(0) except device_errors.CommandTimeoutError: raise RuntimeError( 'Performance trace category list marker not found. ' 'Is the correct version of the browser running?') record_categories = set() disabled_by_default_categories = set() json_data = json.loads(json_category_list)['traceCategoriesList'] for item in json_data: for category in item.split(','): if category.startswith('disabled-by-default'): disabled_by_default_categories.add(category) else: record_categories.add(category) return list(record_categories), list(disabled_by_default_categories)
def launch(device): # --persistent is required to have Settings.Global.DEBUG_APP be set, which # we currently use to allow reading of flags. https://crbug.com/784947 if not nokill: cmd = ['am', 'set-debug-app', '--persistent', debug_process_name] if wait_for_java_debugger: cmd[-1:-1] = ['-w'] # Ignore error since it will fail if apk is not debuggable. device.RunShellCommand(cmd, check_return=False) # The flags are first updated with input args. if command_line_flags_file: changer = flag_changer.FlagChanger(device, command_line_flags_file) flags = [] if argv: flags = shlex.split(argv) try: changer.ReplaceFlags(flags) except device_errors.AdbShellCommandFailedError: logging.exception('Failed to set flags') if url is None: # Simulate app icon click if no url is present. cmd = ['monkey', '-p', package_name, '-c', 'android.intent.category.LAUNCHER', '1'] device.RunShellCommand(cmd, check_return=True) else: launch_intent = intent.Intent(action='android.intent.action.VIEW', activity=view_activity, data=url, package=package_name) device.StartActivity(launch_intent)
def Foreground(self): self.device.StartActivity(intent.Intent( package=self._start_intent.package, activity=self._start_intent.activity, action=None, flags=[intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED]), blocking=True)
def SetUpAndExecute(device, package, fn): """Start logging process. Sets up any device and tracing appropriately and then executes the core logging function. Args: device: Android device, or None for a local run. package: the key for chrome package info. fn: the function to execute that launches chrome and performs the appropriate instrumentation, see _Log*Internal(). Returns: As fn() returns. """ package_info = constants.PACKAGE_INFO[package] command_line_path = '/data/local/chrome-command-line' new_flags = [ '--enable-test-events', '--remote-debugging-port=%d' % DEVTOOLS_PORT ] if device: _SetUpDevice(device, package_info) with FlagChanger(device, command_line_path, new_flags): if device: start_intent = intent.Intent(package=package_info.package, activity=package_info.activity, data='about:blank') device.StartActivity(start_intent, blocking=True) time.sleep(2) # If no device, we don't care about chrome startup so skip the about page. with ForwardPort(device, 'tcp:%d' % DEVTOOLS_PORT, 'localabstract:chrome_devtools_remote'): return fn()
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
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. command_line_path = '/data/local/tmp/chrome-command-line' with device_setup.FlagReplacer(device, command_line_path, ['--disable-fre', _EXTERNAL_PREFETCH_FLAG]): # 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)
def test_tracing(self): TRACE_BUFFER_SIZE = '16384' TRACE_TIME = '5' devices = device_utils.DeviceUtils.HealthyDevices() package_info = util.get_supported_browsers()['stable'] presentation.device = devices[0] output_file_name = util.generate_random_filename_for_test() try: # Launch the browser before tracing. presentation.device.StartActivity(intent.Intent( activity=package_info.activity, package=package_info.package, data='about:blank', extras={'create_new_tab': True}), blocking=True, force_stop=True) # Run atrace agent. run_systrace.main_impl([ './run_systrace.py', '-b', TRACE_BUFFER_SIZE, '-t', TRACE_TIME, '-o', output_file_name, '-e', str(presentation.device), '--atrace-categories=gfx,input,view' ]) # Verify results. with open(output_file_name, 'r') as f: full_trace = f.read() self.assertTrue('CPU#' in full_trace) except: raise finally: if os.path.exists(output_file_name): os.remove(output_file_name)
def test_tracing(self): TRACE_BUFFER_SIZE = '16384' TRACE_TIME = '5' devices = device_utils.DeviceUtils.HealthyDevices() package_info = util.get_supported_browsers()['stable'] device = devices[0] with tempfile_ext.TemporaryFileName() as output_file_name: # Launch the browser before tracing. device.StartActivity(intent.Intent(activity=package_info.activity, package=package_info.package, data='about:blank', extras={'create_new_tab': True}), blocking=True, force_stop=True) # Run atrace agent. run_systrace.main_impl([ './run_systrace.py', '-b', TRACE_BUFFER_SIZE, '-t', TRACE_TIME, '-o', output_file_name, '-e', str(device), '--atrace-categories=gfx,input,view' ]) # Verify results. with open(output_file_name, 'r') as f: full_trace = f.read() self.assertTrue('CPU#' in full_trace)
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 _StartChrome(self, package_info, url): print 'Launching chrome...' self._device.StartActivity( intent.Intent(package=package_info.package, activity=package_info.activity, data=url, extras={'create_new_tab': True}), blocking=True, force_stop=True)
def Background(self): package = 'org.chromium.push_apps_to_background' activity = package + '.PushAppsToBackgroundActivity' self.device.StartActivity(intent.Intent( package=package, activity=activity, action=None, flags=[intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED]), blocking=True)
def LaunchBrowser(self, url): # TODO: Android Go stories could, e.g., use the customtabs helper app to # start Chrome as a custom tab. self.platform.StartActivity(intent.Intent( package=self._possible_browser.settings.package, activity=self._possible_browser.settings.activity, action=None, data=url), blocking=True)
def RunOnce(device, url, warmup, speculation_mode, delay_to_may_launch_url, delay_to_launch_url, cold, chrome_args): """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. 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() with device_setup.FlagReplacer(device, _COMMAND_LINE_PATH, 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 }) 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) ResetChromeLocalState(device) if cold: cache_control.CacheControl(device).DropRamCaches() device.StartActivity(launch_intent, blocking=True) match = None try: match = logcat_monitor.WaitFor(result_line_re, timeout=20) 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
def LaunchBrowser(self, url, flush_caches): if flush_caches: self.platform.FlushDnsCache() self._possible_browser.FlushOsPageCaches() self.platform.WaitForBatteryTemperature(32) self.platform.StartActivity( intent.Intent(package=self._possible_browser.settings.package, activity=self._possible_browser.settings.activity, action=None, data=url), blocking=True)
def StopAgentTracing(self, timeout=None): if self._is_tracing: self._device.BroadcastIntent( intent.Intent(action='%s.GPU_PROFILER_STOP' % self._package_info.package)) self._trace_file = self._logcat_monitor.WaitFor( self._trace_finish_re, timeout=120).group(1) self._is_tracing = False if self._trace_memory: self._device.SetProp(_HEAP_PROFILE_MMAP_PROPERTY, 0)
def _SetupTracing(self): # TODO(lizeb): Figure out how to clean up the command-line file when # _TearDownTracing() is not executed in StopTracing(). self._flag_changer.AddFlags(['--trace-startup']) self._device.ForceStop(self._package_info.package) if self._cold: self._device.EnableRoot() cache_control.CacheControl(self._device).DropRamCaches() launch_intent = None if self._url == '': launch_intent = intent.Intent(action='android.intent.action.MAIN', package=self._package_info.package, activity=self._package_info.activity) else: launch_intent = intent.Intent(package=self._package_info.package, activity=self._package_info.activity, data=self._url, extras={'create_new_tab': True}) self._device.StartActivity(launch_intent, blocking=True)
def Background(self): # Launch clock app, pushing the app to the background. # TODO(crbug.com/586148): The clock app isn't necessarily on every phone, # replace this with the PushAppsToBackground instead. self.device.StartActivity(intent.Intent( package='com.google.android.deskclock', activity='com.android.deskclock.DeskClock', action=None, flags=[intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED]), blocking=True)
def LaunchBrowser(self, url): self.platform.FlushDnsCache() # TODO(crbug.com/811244): Determine whether this ensures the page cache is # cleared after |FlushOsPageCaches()| returns. self._possible_browser.FlushOsPageCaches() self.platform.StartActivity( intent.Intent(package=self._possible_browser.settings.package, activity=self._possible_browser.settings.activity, action=None, data=url), blocking=True)
def LaunchMapsPwa(self): # Launches a bound webapk. The APK should be installed by the shared state # constructor. Upon launch, Chrome extracts the icon and the URL from the # APK. self.platform.WaitForBatteryTemperature(_MAX_BATTERY_TEMP) self.platform.StartActivity(intent.Intent( package='org.chromium.maps_go_webapk', activity='org.chromium.webapk.shell_apk.h2o.H2OMainActivity', category='android.intent.category.LAUNCHER', action='android.intent.action.MAIN'), blocking=True)
def LaunchBrowser(self, url, flush_caches): if flush_caches: self.platform.FlushDnsCache() self._possible_browser.FlushOsPageCaches() self.platform.WaitForBatteryTemperature(_MAX_BATTERY_TEMP) self.platform.StartActivity(intent.Intent( package=self._possible_browser.settings.package, activity=self._possible_browser.settings.activity, data=url, action='android.intent.action.VIEW'), blocking=True)
def _LoadPage(device, url): """Load a page on chrome on our device. Args: device: an AdbWrapper for the device on which to load the page. url: url as a string to load. """ load_intent = intent.Intent( package=CHROME.package, activity=CHROME.activity, data=url) logging.warning('Loading ' + url) device.StartActivity(load_intent, blocking=True)
def setUp(self): devices = device_utils.DeviceUtils.HealthyDevices() self.browser = 'stable' self.package_info = profiler.GetSupportedBrowsers()[self.browser] self.device = devices[0] self.device.ForceStop(self.package_info.package) self.device.StartActivity(intent.Intent( activity=self.package_info.activity, package=self.package_info.package), blocking=True)
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() command_line_path = '/data/local/chrome-command-line' self._device.ForceStop(package_info.package) chrome_args = self._GetChromeArguments() logging.info( 'Launching %s with flags: %s' % (package_info.package, subprocess.list2cmdline(chrome_args))) with device_setup.FlagReplacer(self._device, command_line_path, self._GetChromeArguments()): 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) with device_setup.ForwardPort( self._device, 'tcp:%d' % OPTIONS.devtools_port, 'localabstract:chrome_devtools_remote'): try: connection = devtools_monitor.DevToolsConnection( OPTIONS.devtools_hostname, OPTIONS.devtools_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 else: raise ChromeControllerInternalError( 'Failed to connect to Chrome devtools after {} ' 'attempts.'.format(self.DEVTOOLS_CONNECTION_ATTEMPTS)) except: logcat = ''.join( [l + '\n' for l in self._device.adb.Logcat(dump=True)]) raise ChromeControllerError(log=logcat) finally: self._device.ForceStop(package_info.package)