58f37b25e2a9dae080dc6e26c8e380516f334262
[platform/framework/web/crosswalk.git] / src / tools / telemetry / telemetry / core / backends / chrome / android_browser_finder.py
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
7 import logging
8 import os
9 import re
10 import subprocess
11 import sys
12
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
22
23 try:
24   import psutil  # pylint: disable=F0401
25 except ImportError:
26   psutil = None
27
28
29 CHROME_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
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' %
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
147 def 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
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?')
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
186 def FindAllBrowserTypes(_options):
187   return CHROME_PACKAGE_NAMES.keys()
188
189
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.')
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