1# Copyright 2012 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import re 6 7from telemetry.core import util 8from telemetry.internal.app import android_process 9from telemetry.internal.backends import android_browser_backend_settings 10from telemetry.internal.backends import android_command_line_backend 11from telemetry.internal.backends import app_backend 12 13from devil.android import app_ui 14 15 16class AndroidAppBackend(app_backend.AppBackend): 17 18 def __init__(self, android_platform_backend, start_intent, 19 is_app_ready_predicate=None, app_has_webviews=True): 20 super(AndroidAppBackend, self).__init__( 21 start_intent.package, android_platform_backend) 22 self._default_process_name = start_intent.package 23 self._start_intent = start_intent 24 self._is_app_ready_predicate = is_app_ready_predicate 25 self._is_running = False 26 self._app_has_webviews = app_has_webviews 27 self._existing_processes_by_pid = {} 28 self._app_ui = None 29 30 @property 31 def device(self): 32 return self.platform_backend.device 33 34 def GetAppUi(self): 35 if self._app_ui is None: 36 self._app_ui = app_ui.AppUi(self.device, self._start_intent.package) 37 return self._app_ui 38 39 def _LaunchAndWaitForApplication(self): 40 """Launch the app and wait for it to be ready.""" 41 def is_app_ready(): 42 return self._is_app_ready_predicate(self.app) 43 44 # When "is_app_ready_predicate" is provided, we use it to wait for the 45 # app to become ready, otherwise "blocking=True" is used as a fall back. 46 # TODO(slamm): check if the wait for "ps" check is really needed, or 47 # whether the "blocking=True" fall back is sufficient. 48 has_ready_predicate = self._is_app_ready_predicate is not None 49 self.device.StartActivity( 50 self._start_intent, 51 blocking=not has_ready_predicate, 52 force_stop=True, # Ensure app was not running. 53 ) 54 if has_ready_predicate: 55 util.WaitFor(is_app_ready, timeout=60) 56 57 def Start(self): 58 """Start an Android app and wait for it to finish launching. 59 60 If the app has webviews, the app is launched with the suitable 61 command line arguments. 62 63 AppStory derivations can customize the wait-for-ready-state to wait 64 for a more specific event if needed. 65 """ 66 if self._app_has_webviews: 67 webview_startup_args = self.GetWebviewStartupArgs() 68 backend_settings = ( 69 android_browser_backend_settings.WebviewBackendSettings( 70 'android-webview')) 71 with android_command_line_backend.SetUpCommandLineFlags( 72 self.device, backend_settings, webview_startup_args): 73 self._LaunchAndWaitForApplication() 74 else: 75 self._LaunchAndWaitForApplication() 76 self._is_running = True 77 78 def Close(self): 79 self._is_running = False 80 self.platform_backend.KillApplication(self._start_intent.package) 81 82 def IsAppRunning(self): 83 return self._is_running 84 85 def GetStandardOutput(self): 86 raise NotImplementedError 87 88 def GetStackTrace(self): 89 raise NotImplementedError 90 91 def GetProcesses(self, process_filter=None): 92 if process_filter is None: 93 # Match process names of the form: 'process_name[:subprocess]'. 94 process_filter = re.compile( 95 '^%s(:|$)' % re.escape(self._default_process_name)).match 96 97 processes = set() 98 ps_output = self.platform_backend.GetPsOutput(['pid', 'name']) 99 for pid, name in ps_output: 100 if not process_filter(name): 101 continue 102 103 if pid not in self._existing_processes_by_pid: 104 self._existing_processes_by_pid[pid] = android_process.AndroidProcess( 105 self, pid, name) 106 processes.add(self._existing_processes_by_pid[pid]) 107 return processes 108 109 def GetProcess(self, subprocess_name): 110 assert subprocess_name.startswith(':') 111 process_name = self._default_process_name + subprocess_name 112 return self.GetProcesses(lambda n: n == process_name).pop() 113 114 def GetWebViews(self): 115 assert self._app_has_webviews 116 webviews = set() 117 for process in self.GetProcesses(): 118 webviews.update(process.GetWebViews()) 119 return webviews 120 121 def GetWebviewStartupArgs(self): 122 assert self._app_has_webviews 123 args = [] 124 125 # Turn on GPU benchmarking extension for all runs. The only side effect of 126 # the extension being on is that render stats are tracked. This is believed 127 # to be effectively free. And, by doing so here, it avoids us having to 128 # programmatically inspect a pageset's actions in order to determine if it 129 # might eventually scroll. 130 args.append('--enable-gpu-benchmarking') 131 132 return args 133