1 # Copyright 2013 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.
5 """Finds android browsers that can be controlled by telemetry."""
13 from telemetry import decorators
14 from telemetry.core import browser
15 from telemetry.core import exceptions
16 from telemetry.core import possible_browser
17 from telemetry.core import platform
18 from telemetry.core import util
19 from telemetry.core.backends import adb_commands
20 from telemetry.core.platform import android_device
21 from telemetry.core.backends.chrome import android_browser_backend
24 import psutil # pylint: disable=F0401
29 CHROME_PACKAGE_NAMES = {
30 'android-content-shell':
31 ['org.chromium.content_shell_apk',
32 android_browser_backend.ContentShellBackendSettings,
34 'android-chrome-shell':
35 ['org.chromium.chrome.shell',
36 android_browser_backend.ChromeShellBackendSettings,
39 ['org.chromium.telemetry_shell',
40 android_browser_backend.WebviewBackendSettings,
42 'android-webview-shell':
43 ['org.chromium.android_webview.shell',
44 android_browser_backend.WebviewShellBackendSettings,
45 'AndroidWebView.apk'],
47 ['com.google.android.apps.chrome',
48 android_browser_backend.ChromeBackendSettings,
50 'android-chrome-beta':
52 android_browser_backend.ChromeBackendSettings,
55 ['com.google.android.apps.chrome_dev',
56 android_browser_backend.ChromeBackendSettings,
58 'android-chrome-canary':
60 android_browser_backend.ChromeBackendSettings,
62 'android-jb-system-chrome':
63 ['com.android.chrome',
64 android_browser_backend.ChromeBackendSettings,
69 class PossibleAndroidBrowser(possible_browser.PossibleBrowser):
70 """A launchable android browser instance."""
71 def __init__(self, browser_type, finder_options, android_platform,
72 backend_settings, apk_name):
73 super(PossibleAndroidBrowser, self).__init__(browser_type, 'android',
74 finder_options, backend_settings.supports_tab_control)
75 assert browser_type in FindAllBrowserTypes(finder_options), \
76 ('Please add %s to android_browser_finder.FindAllBrowserTypes' %
78 self._platform = android_platform
79 self._platform_backend = (
80 android_platform._platform_backend # pylint: disable=W0212
82 self._backend_settings = backend_settings
83 self._local_apk = None
85 chrome_root = util.GetChromiumSrcDir()
88 for build_dir, build_type in util.GetBuildDirectories():
89 apk_full_name = os.path.join(chrome_root, build_dir, build_type, 'apks',
91 if os.path.exists(apk_full_name):
92 last_changed = os.path.getmtime(apk_full_name)
93 candidate_apks.append((last_changed, apk_full_name))
96 # Find the canadidate .apk with the latest modification time.
97 newest_apk_path = sorted(candidate_apks)[-1][1]
98 self._local_apk = newest_apk_path
101 return 'PossibleAndroidBrowser(browser_type=%s)' % self.browser_type
103 def _InitPlatformIfNeeded(self):
107 self._InitPlatformIfNeeded()
109 use_rndis_forwarder = (self.finder_options.android_rndis or
110 self.finder_options.browser_options.netsim or
111 platform.GetHostPlatform().GetOSName() != 'linux')
112 backend = android_browser_backend.AndroidBrowserBackend(
113 self.finder_options.browser_options, self._backend_settings,
115 output_profile_path=self.finder_options.output_profile_path,
116 extensions_to_load=self.finder_options.extensions_to_load,
117 target_arch=self.finder_options.target_arch,
118 android_platform_backend=self._platform_backend)
119 b = browser.Browser(backend,
120 self._platform_backend,
122 self._append_to_existing_wpr,
123 self._make_javascript_deterministic,
124 self._credentials_path)
127 def SupportsOptions(self, finder_options):
128 if len(finder_options.extensions_to_load) != 0:
132 def HaveLocalAPK(self):
133 return self._local_apk and os.path.exists(self._local_apk)
136 def UpdateExecutableIfNeeded(self):
137 if self.HaveLocalAPK():
138 logging.warn('Installing %s on device if needed.' % self._local_apk)
139 self.platform.InstallApplication(self._local_apk)
141 def last_modification_time(self):
142 if self.HaveLocalAPK():
143 return os.path.getmtime(self._local_apk)
147 def SelectDefaultBrowser(possible_browsers):
148 local_builds_by_date = sorted(possible_browsers,
149 key=lambda b: b.last_modification_time())
151 if local_builds_by_date:
152 newest_browser = local_builds_by_date[-1]
153 return newest_browser
157 def CanFindAvailableBrowsers():
158 if not adb_commands.IsAndroidSupported():
159 logging.info('Android build commands unavailable on this machine. Have '
160 'you installed Android build dependencies?')
164 with open(os.devnull, 'w') as devnull:
165 proc = subprocess.Popen(
167 stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=devnull)
168 stdout, _ = proc.communicate()
169 if re.search(re.escape('????????????\tno permissions'), stdout) != None:
170 logging.warn('adb devices reported a permissions error. Consider '
171 'restarting adb as root:')
172 logging.warn(' adb kill-server')
173 logging.warn(' sudo `which adb` devices\n\n')
176 platform_tools_path = os.path.join(util.GetChromiumSrcDir(),
177 'third_party', 'android_tools', 'sdk', 'platform-tools')
178 if (sys.platform.startswith('linux') and
179 os.path.exists(os.path.join(platform_tools_path, 'adb'))):
180 os.environ['PATH'] = os.pathsep.join([platform_tools_path,
186 def FindAllBrowserTypes(_options):
187 return CHROME_PACKAGE_NAMES.keys()
190 def FindAllAvailableBrowsers(finder_options):
191 """Finds all the desktop browsers available on this machine."""
192 if not CanFindAvailableBrowsers():
193 logging.info('No adb command found. ' +
194 'Will not try searching for Android browsers.')
196 if finder_options.android_device:
197 devices = [android_device.AndroidDevice(finder_options.android_device,
198 finder_options.no_performance_mode)]
200 devices = android_device.AndroidDevice.GetAllConnectedDevices()
202 if len(devices) == 0:
203 logging.info('No android devices found.')
205 elif len(devices) > 1:
207 'Multiple devices attached. Please specify one of the following:\n' +
208 '\n'.join([' --device=%s' % d.device_id for d in devices]))
212 android_platform = platform.GetPlatformForDevice(devices[0])
213 except exceptions.PlatformError:
216 # Host side workaround for crbug.com/268450 (adb instability).
217 # The adb server has a race which is mitigated by binding to a single core.
219 for proc in psutil.process_iter():
221 if 'adb' in proc.name:
222 if 'cpu_affinity' in dir(proc):
223 proc.cpu_affinity([0]) # New versions of psutil.
224 elif 'set_cpu_affinity' in dir(proc):
225 proc.set_cpu_affinity([0]) # Older versions.
228 'Cannot set CPU affinity due to stale psutil version: %s',
229 '.'.join(str(x) for x in psutil.version_info))
230 except (psutil.NoSuchProcess, psutil.AccessDenied):
231 logging.warn('Failed to set adb process CPU affinity')
233 possible_browsers = []
234 for name, package_info in CHROME_PACKAGE_NAMES.iteritems():
235 [package, backend_settings, local_apk] = package_info
236 b = PossibleAndroidBrowser(name,
239 backend_settings(package),
241 if b.platform.CanLaunchApplication(package) or b.HaveLocalAPK():
242 possible_browsers.append(b)
243 return possible_browsers