- add sources.
[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 os
8 import logging as real_logging
9 import re
10 import subprocess
11 import sys
12
13 from telemetry.core import browser
14 from telemetry.core import possible_browser
15 from telemetry.core import util
16 from telemetry.core.backends import adb_commands
17 from telemetry.core.backends.chrome import android_browser_backend
18 from telemetry.core.platform import android_platform_backend
19
20 CHROME_PACKAGE_NAMES = {
21   'android-content-shell':
22       ['org.chromium.content_shell_apk',
23        android_browser_backend.ContentShellBackendSettings,
24        'ContentShell.apk'],
25   'android-chromium-testshell':
26       ['org.chromium.chrome.testshell',
27        android_browser_backend.ChromiumTestShellBackendSettings,
28        'ChromiumTestShell.apk'],
29   'android-webview':
30       ['com.android.webview.chromium.shell',
31        android_browser_backend.WebviewBackendSettings,
32        None],
33   'android-chrome':
34       ['com.google.android.apps.chrome',
35        android_browser_backend.ChromeBackendSettings,
36        'Chrome.apk'],
37   'android-chrome-beta':
38       ['com.chrome.beta',
39        android_browser_backend.ChromeBackendSettings,
40        None],
41   'android-chrome-dev':
42       ['com.google.android.apps.chrome_dev',
43        android_browser_backend.ChromeBackendSettings,
44        None],
45   'android-jb-system-chrome':
46       ['com.android.chrome',
47        android_browser_backend.ChromeBackendSettings,
48        None]
49 }
50
51 ALL_BROWSER_TYPES = ','.join(CHROME_PACKAGE_NAMES.keys())
52
53 # adb shell pm list packages
54 # adb
55 # intents to run (pass -D url for the rest)
56 #   com.android.chrome/.Main
57 #   com.google.android.apps.chrome/.Main
58
59 class PossibleAndroidBrowser(possible_browser.PossibleBrowser):
60   """A launchable android browser instance."""
61   def __init__(self, browser_type, finder_options, backend_settings, apk_name):
62     super(PossibleAndroidBrowser, self).__init__(browser_type, finder_options)
63     self._backend_settings = backend_settings
64     self._local_apk = None
65
66     chrome_root = util.GetChromiumSrcDir()
67     if apk_name:
68       candidate_apks = []
69       for build_dir, build_type in util.GetBuildDirectories():
70         apk_full_name = os.path.join(chrome_root, build_dir, build_type, 'apks',
71                                      apk_name)
72         if os.path.exists(apk_full_name):
73           last_changed = os.path.getmtime(apk_full_name)
74           candidate_apks.append((last_changed, apk_full_name))
75
76       if candidate_apks:
77         # Find the canadidate .apk with the latest modification time.
78         newest_apk_path = sorted(candidate_apks)[-1][1]
79         self._local_apk = newest_apk_path
80
81
82   def __repr__(self):
83     return 'PossibleAndroidBrowser(browser_type=%s)' % self.browser_type
84
85   def Create(self):
86     backend = android_browser_backend.AndroidBrowserBackend(
87         self.finder_options.browser_options, self._backend_settings,
88         self.finder_options.android_rndis,
89         output_profile_path=self.finder_options.output_profile_path,
90         extensions_to_load=self.finder_options.extensions_to_load)
91     platform_backend = android_platform_backend.AndroidPlatformBackend(
92         self._backend_settings.adb.Adb(),
93         self.finder_options.no_performance_mode)
94     b = browser.Browser(backend, platform_backend)
95     return b
96
97   def SupportsOptions(self, finder_options):
98     if len(finder_options.extensions_to_load) != 0:
99       return False
100     return True
101
102   def HaveLocalAPK(self):
103     return self._local_apk and os.path.exists(self._local_apk)
104
105   def UpdateExecutableIfNeeded(self):
106     if self.HaveLocalAPK():
107       real_logging.warn(
108           'Refreshing %s on device if needed.' % self._local_apk)
109       self._backend_settings.adb.Install(self._local_apk)
110
111   def last_modification_time(self):
112     if self.HaveLocalAPK():
113       return os.path.getmtime(self._local_apk)
114     return -1
115
116 def SelectDefaultBrowser(possible_browsers):
117   local_builds_by_date = sorted(possible_browsers,
118                                 key=lambda b: b.last_modification_time())
119
120   if local_builds_by_date:
121     newest_browser = local_builds_by_date[-1]
122     return newest_browser
123   return None
124
125 adb_works = None
126 def CanFindAvailableBrowsers(logging=real_logging):
127   if not adb_commands.IsAndroidSupported():
128     return False
129
130   global adb_works
131
132   if adb_works == None:
133     try:
134       with open(os.devnull, 'w') as devnull:
135         proc = subprocess.Popen(['adb', 'devices'],
136                                 stdout=subprocess.PIPE,
137                                 stderr=subprocess.PIPE,
138                                 stdin=devnull)
139         stdout, _ = proc.communicate()
140         if re.search(re.escape('????????????\tno permissions'), stdout) != None:
141           logging.warn(
142               ('adb devices reported a permissions error. Consider '
143                'restarting adb as root:'))
144           logging.warn('  adb kill-server')
145           logging.warn('  sudo `which adb` devices\n\n')
146         adb_works = True
147     except OSError:
148       platform_tools_path = os.path.join(util.GetChromiumSrcDir(),
149           'third_party', 'android_tools', 'sdk', 'platform-tools')
150       if (sys.platform.startswith('linux') and
151           os.path.exists(os.path.join(platform_tools_path, 'adb'))):
152         os.environ['PATH'] = os.pathsep.join([platform_tools_path,
153                                               os.environ['PATH']])
154         adb_works = True
155       else:
156         adb_works = False
157   return adb_works
158
159 def FindAllAvailableBrowsers(finder_options, logging=real_logging):
160   """Finds all the desktop browsers available on this machine."""
161   if not CanFindAvailableBrowsers(logging=logging):
162     logging.info('No adb command found. ' +
163                  'Will not try searching for Android browsers.')
164     return []
165
166   device = None
167   if finder_options.android_device:
168     devices = [finder_options.android_device]
169   else:
170     devices = adb_commands.GetAttachedDevices()
171
172   if len(devices) == 0:
173     logging.info('No android devices found.')
174     return []
175
176   if len(devices) > 1:
177     logging.warn('Multiple devices attached. ' +
178                  'Please specify a device explicitly.')
179     return []
180
181   device = devices[0]
182
183   adb = adb_commands.AdbCommands(device=device)
184
185   if sys.platform.startswith('linux'):
186     # Host side workaround for crbug.com/268450 (adb instability)
187     # The adb server has a race which is mitigated by binding to a single core.
188     import psutil  # pylint: disable=F0401
189     pids  = [p.pid for p in psutil.process_iter() if 'adb' in p.name]
190     with open(os.devnull, 'w') as devnull:
191       for pid in pids:
192         ret = subprocess.call(['taskset', '-p', '-c', '0', str(pid)],
193                               stdout=subprocess.PIPE,
194                               stderr=subprocess.PIPE,
195                               stdin=devnull)
196         if ret:
197           logging.warn('Failed to taskset %d (%s)', pid, ret)
198
199     # Experimental device side workaround for crbug.com/268450 (adb instability)
200     # The /sbin/adbd process on the device appears to hang which causes adb to
201     # report that the device is offline. Our working theory is that killing
202     # the process and allowing it to be automatically relaunched will allow us
203     # to run for longer before it hangs.
204     if not finder_options.keep_test_server_ports:
205       # This would break forwarder connections, so we cannot do this if
206       # instructed to keep server ports open.
207       logging.info('Killing adbd on device')
208       adb.KillAll('adbd')
209       logging.info('Waiting for adbd to restart')
210       adb.Adb().Adb().SendCommand('wait-for-device')
211
212   packages = adb.RunShellCommand('pm list packages')
213   possible_browsers = []
214
215   for name, package_info in CHROME_PACKAGE_NAMES.iteritems():
216     [package, backend_settings, local_apk] = package_info
217     b = PossibleAndroidBrowser(
218         name,
219         finder_options,
220         backend_settings(adb, package),
221         local_apk)
222
223     if 'package:' + package in packages or b.HaveLocalAPK():
224       possible_browsers.append(b)
225
226   # See if the "forwarder" is installed -- we need this to host content locally
227   # but make it accessible to the device.
228   if (len(possible_browsers) and not finder_options.android_rndis and
229       not adb_commands.HasForwarder()):
230     logging.warn('telemetry detected an android device. However,')
231     logging.warn('Chrome\'s port-forwarder app is not available.')
232     logging.warn('Falling back to prebuilt binaries, but to build locally: ')
233     logging.warn('  ninja -C out/Release forwarder2 md5sum')
234     logging.warn('')
235     logging.warn('')
236     if not adb_commands.SetupPrebuiltTools(device):
237       return []
238   return possible_browsers