• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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