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. 4 5"""Finds android browsers that can be controlled by telemetry.""" 6 7import logging 8import os 9import re 10import subprocess 11import sys 12 13from telemetry import decorators 14from telemetry.core import browser 15from telemetry.core import exceptions 16from telemetry.core import possible_browser 17from telemetry.core import platform 18from telemetry.core import util 19from telemetry.core.backends import adb_commands 20from telemetry.core.platform import android_device 21from telemetry.core.backends.chrome import android_browser_backend 22 23try: 24 import psutil # pylint: disable=F0401 25except ImportError: 26 psutil = None 27 28 29CHROME_PACKAGE_NAMES = { 30 'android-content-shell': 31 ['org.chromium.content_shell_apk', 32 android_browser_backend.ContentShellBackendSettings, 33 'ContentShell.apk'], 34 'android-chrome-shell': 35 ['org.chromium.chrome.shell', 36 android_browser_backend.ChromeShellBackendSettings, 37 'ChromeShell.apk'], 38 'android-webview': 39 ['org.chromium.telemetry_shell', 40 android_browser_backend.WebviewBackendSettings, 41 None], 42 'android-webview-shell': 43 ['org.chromium.android_webview.shell', 44 android_browser_backend.WebviewShellBackendSettings, 45 'AndroidWebView.apk'], 46 'android-chrome': 47 ['com.google.android.apps.chrome', 48 android_browser_backend.ChromeBackendSettings, 49 'Chrome.apk'], 50 'android-chrome-beta': 51 ['com.chrome.beta', 52 android_browser_backend.ChromeBackendSettings, 53 None], 54 'android-chrome-dev': 55 ['com.google.android.apps.chrome_dev', 56 android_browser_backend.ChromeBackendSettings, 57 None], 58 'android-chrome-canary': 59 ['com.chrome.canary', 60 android_browser_backend.ChromeBackendSettings, 61 None], 62 'android-jb-system-chrome': 63 ['com.android.chrome', 64 android_browser_backend.ChromeBackendSettings, 65 None] 66} 67 68 69class 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' % 77 browser_type) 78 self._platform = android_platform 79 self._platform_backend = ( 80 android_platform._platform_backend # pylint: disable=W0212 81 ) 82 self._backend_settings = backend_settings 83 self._local_apk = None 84 85 chrome_root = util.GetChromiumSrcDir() 86 if apk_name: 87 candidate_apks = [] 88 for build_dir, build_type in util.GetBuildDirectories(): 89 apk_full_name = os.path.join(chrome_root, build_dir, build_type, 'apks', 90 apk_name) 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)) 94 95 if candidate_apks: 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 99 100 def __repr__(self): 101 return 'PossibleAndroidBrowser(browser_type=%s)' % self.browser_type 102 103 def _InitPlatformIfNeeded(self): 104 pass 105 106 def Create(self): 107 self._InitPlatformIfNeeded() 108 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, 114 use_rndis_forwarder, 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, 121 self._archive_path, 122 self._append_to_existing_wpr, 123 self._make_javascript_deterministic, 124 self._credentials_path) 125 return b 126 127 def SupportsOptions(self, finder_options): 128 if len(finder_options.extensions_to_load) != 0: 129 return False 130 return True 131 132 def HaveLocalAPK(self): 133 return self._local_apk and os.path.exists(self._local_apk) 134 135 @decorators.Cache 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) 140 141 def last_modification_time(self): 142 if self.HaveLocalAPK(): 143 return os.path.getmtime(self._local_apk) 144 return -1 145 146 147def SelectDefaultBrowser(possible_browsers): 148 local_builds_by_date = sorted(possible_browsers, 149 key=lambda b: b.last_modification_time()) 150 151 if local_builds_by_date: 152 newest_browser = local_builds_by_date[-1] 153 return newest_browser 154 return None 155 156 157def CanFindAvailableBrowsers(): 158 if not adb_commands.IsAndroidSupported(): 159 logging.info('Android build commands unavailable on this machine. Have ' 160 'you installed Android build dependencies?') 161 return False 162 163 try: 164 with open(os.devnull, 'w') as devnull: 165 proc = subprocess.Popen( 166 ['adb', 'devices'], 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') 174 return True 175 except OSError: 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, 181 os.environ['PATH']]) 182 return True 183 return False 184 185 186def FindAllBrowserTypes(_options): 187 return CHROME_PACKAGE_NAMES.keys() 188 189 190def 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.') 195 return [] 196 if finder_options.android_device: 197 devices = [android_device.AndroidDevice(finder_options.android_device, 198 finder_options.no_performance_mode)] 199 else: 200 devices = android_device.AndroidDevice.GetAllConnectedDevices() 201 202 if len(devices) == 0: 203 logging.info('No android devices found.') 204 return [] 205 elif len(devices) > 1: 206 logging.warn( 207 'Multiple devices attached. Please specify one of the following:\n' + 208 '\n'.join([' --device=%s' % d.device_id for d in devices])) 209 return [] 210 211 try: 212 android_platform = platform.GetPlatformForDevice(devices[0]) 213 except exceptions.PlatformError: 214 return [] 215 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. 218 if psutil: 219 for proc in psutil.process_iter(): 220 try: 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. 226 else: 227 logging.warn( 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') 232 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, 237 finder_options, 238 android_platform, 239 backend_settings(package), 240 local_apk) 241 if b.platform.CanLaunchApplication(package) or b.HaveLocalAPK(): 242 possible_browsers.append(b) 243 return possible_browsers 244