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
5import logging
6import collections
7import os
8
9from telemetry.core import util
10
11from devil.android.sdk import version_codes
12
13import py_utils
14
15
16_BackendSettingsTuple = collections.namedtuple('_BackendSettingsTuple', [
17    'browser_type', 'package', 'activity', 'command_line_name',
18    'devtools_port', 'apk_name', 'embedder_apk_name',
19    'supports_tab_control', 'supports_spki_list', 'additional_apk_name'])
20
21
22class AndroidBrowserBackendSettings(_BackendSettingsTuple):
23  """Base class for backend settings of Android browsers.
24
25  These abstract away the differences that may exist between different
26  browsers that may be installed or controlled on Android.
27
28  Args:
29    browser_type: The short browser name used by Telemetry to identify
30      this from, e.g., the --browser command line argument.
31    package: The package name used to identify the browser on Android.
32      Each browser_type must match to a single package name and viceversa.
33    activity: The activity name used to launch this browser via an intent.
34    command_line_name: File name where the browser reads command line flags.
35    devtools_port: Default remote port used to set up a DevTools conneciton.
36      Subclasses may override how this value is interpreted.
37    apk_name: Default apk name as built on a chromium checkout, used to
38      find local apks on the host platform. Subclasses may override
39      how this value is interpreted.
40    embedder_apk_name: Name of an additional apk needed, also expected to be
41      found in the chromium checkout, used as an app which embbeds e.g.
42      the WebView implementation given by the apk_name above.
43    supports_tab_control: Whether this browser variant supports the concept
44      of tabs.
45    supports_spki_list: Whether this browser supports spki-list for ignoring
46      certificate errors. See: crbug.com/753948
47  """
48  __slots__ = ()
49
50  def __str__(self):
51    return '%s (%s)' % (self.browser_type, self.package)
52
53  @property
54  def profile_ignore_list(self):
55    # Don't delete lib, since it is created by the installer.
56    return ('lib', )
57
58  @property
59  def requires_embedder(self):
60    return self.embedder_apk_name is not None
61
62  def GetDevtoolsRemotePort(self, device):
63    del device
64    # By default return the devtools_port defined in the constructor.
65    return self.devtools_port
66
67  def GetApkName(self, device):
68    # Subclasses may override this method to pick the correct apk based on
69    # specific device features.
70    del device  # Unused.
71    return self.apk_name
72
73  def FindLocalApk(self, device, chrome_root):
74    apk_name = self.GetApkName(device)
75    logging.info('Picked apk name %s for browser_type %s',
76                 apk_name, self.browser_type)
77    if apk_name is None:
78      return None
79    else:
80      return util.FindLatestApkOnHost(chrome_root, apk_name)
81
82  # returns True if this is a WebView browser and WebView-specific
83  # field trial configurations should apply.
84  def IsWebView(self):
85    return False
86
87
88class GenericChromeBackendSettings(AndroidBrowserBackendSettings):
89  def __new__(cls, **kwargs):
90    # Provide some defaults common to Chrome based backends.
91    kwargs.setdefault('activity', 'com.google.android.apps.chrome.Main')
92    kwargs.setdefault('command_line_name', 'chrome-command-line')
93    kwargs.setdefault('devtools_port', 'localabstract:chrome_devtools_remote')
94    kwargs.setdefault('apk_name', None)
95    kwargs.setdefault('embedder_apk_name', None)
96    kwargs.setdefault('supports_tab_control', True)
97    kwargs.setdefault('supports_spki_list', True)
98    kwargs.setdefault('additional_apk_name', None)
99    return super(GenericChromeBackendSettings, cls).__new__(cls, **kwargs)
100
101
102class GenericChromeBundleBackendSettings(GenericChromeBackendSettings):
103  def GetApkName(self, device):
104    assert self.apk_name.endswith('_bundle')
105    del device  # unused
106    # Bundles are created using the generated tool in the output directory's
107    # bin directory instead of being output to the apk directory at compile
108    # time like a normal APK.
109    return os.path.join('..', 'bin', self.apk_name)
110
111
112class ChromeBackendSettings(GenericChromeBackendSettings):
113  def GetApkName(self, device):
114    assert self.apk_name is None
115    # The APK to install depends on the OS version of the deivce.
116    if device.build_version_sdk >= version_codes.NOUGAT:
117      return 'Monochrome.apk'
118    else:
119      return 'Chrome.apk'
120
121
122class WebViewBasedBackendSettings(AndroidBrowserBackendSettings):
123  def __new__(cls, **kwargs):
124    # Provide some defaults common to WebView based backends.
125    kwargs.setdefault('devtools_port',
126                      'localabstract:webview_devtools_remote_{pid}')
127    kwargs.setdefault('apk_name', None)
128    kwargs.setdefault('embedder_apk_name', None)
129    kwargs.setdefault('supports_tab_control', False)
130    # TODO(crbug.com/753948): Switch to True when spki-list support is
131    # implemented on WebView.
132    kwargs.setdefault('supports_spki_list', False)
133    kwargs.setdefault('additional_apk_name', None)
134    return super(WebViewBasedBackendSettings, cls).__new__(cls, **kwargs)
135
136  def GetDevtoolsRemotePort(self, device):
137    # The DevTools port for WebView based backends depends on the browser PID.
138    def get_activity_pid():
139      return device.GetApplicationPids(self.package, at_most_one=True)
140
141    pid = py_utils.WaitFor(get_activity_pid, timeout=30)
142    return self.devtools_port.format(pid=pid)
143
144
145class WebViewBackendSettings(WebViewBasedBackendSettings):
146  def __new__(cls, **kwargs):
147    # Provide some defaults for backends that work via system_webview_shell,
148    # a testing app with source code available at:
149    # https://cs.chromium.org/chromium/src/android_webview/tools/system_webview_shell
150    kwargs.setdefault('package', 'org.chromium.webview_shell')
151    kwargs.setdefault('activity',
152                      'org.chromium.webview_shell.TelemetryActivity')
153    kwargs.setdefault('embedder_apk_name', 'SystemWebViewShell.apk')
154    kwargs.setdefault('command_line_name', 'webview-command-line')
155    return super(WebViewBackendSettings, cls).__new__(cls, **kwargs)
156
157  def GetApkName(self, device):
158    if self.apk_name is not None:
159      return self.apk_name
160    # The APK to install depends on the OS version of the deivce unless
161    # explicitly overridden.
162    if device.build_version_sdk >= version_codes.NOUGAT:
163      return 'MonochromePublic.apk'
164    else:
165      return 'SystemWebView.apk'
166
167  def FindSupportApks(self, apk_path, chrome_root):
168    del chrome_root
169    all_apks = []
170    # Try to find the WebView embedder next to the local APK found.
171    if apk_path is not None:
172      embedder_apk_path = os.path.join(
173          os.path.dirname(apk_path), self.embedder_apk_name)
174      if os.path.exists(embedder_apk_path):
175        all_apks.append(embedder_apk_path)
176      if self.additional_apk_name is not None:
177        additional_apk_path = os.path.join(
178            os.path.dirname(apk_path), self.additional_apk_name)
179        if os.path.exists(additional_apk_path):
180          all_apks.append(additional_apk_path)
181    return all_apks
182
183  def IsWebView(self):
184    return True
185
186
187class WebViewGoogleBackendSettings(WebViewBackendSettings):
188  def GetApkName(self, device):
189    assert self.apk_name is None
190    # The APK to install depends on the OS version of the deivce.
191    if device.build_version_sdk >= version_codes.NOUGAT:
192      return 'Monochrome.apk'
193    else:
194      return 'SystemWebViewGoogle.apk'
195
196
197class WebViewBundleBackendSettings(WebViewBackendSettings):
198  def GetApkName(self, device):
199    assert self.apk_name.endswith('_bundle')
200    del device  # unused
201    # Bundles are created using the generated tool in the output directory's
202    # bin directory instead of being output to the apk directory at compile
203    # time like a normal APK.
204    return os.path.join('..', 'bin', self.apk_name)
205
206  def FindSupportApks(self, apk_path, chrome_root):
207    del chrome_root
208    # Try to find the WebView embedder in apk directory
209    if apk_path is not None:
210      embedder_apk_path = os.path.join(
211          os.path.dirname(apk_path), '..', 'apks', self.embedder_apk_name)
212      if os.path.exists(embedder_apk_path):
213        return [embedder_apk_path]
214    return []
215
216
217class WebLayerBackendSettings(WebViewBackendSettings):
218  def __new__(cls, **kwargs):
219    # Provide some defaults for backends that work via weblayer_shell,
220    # a testing app with source code available at:
221    # https://cs.chromium.org/chromium/src/weblayer/shell
222    kwargs.setdefault('devtools_port',
223                      'localabstract:weblayer_devtools_remote_{pid}')
224    kwargs.setdefault('package', 'org.chromium.weblayer.shell')
225    kwargs.setdefault('activity',
226                      'org.chromium.weblayer.shell.TelemetryActivity')
227    kwargs.setdefault('embedder_apk_name', 'WebLayerShellSystemWebView.apk')
228    kwargs.setdefault('command_line_name', 'weblayer-command-line')
229    return super(WebLayerBackendSettings, cls).__new__(cls, **kwargs)
230
231  def GetApkName(self, device):
232    del device # Unused
233    assert self.apk_name is None
234    return 'Monochrome.apk'
235
236  def IsWebView(self):
237    return False
238
239class WebLayerBundleBackendSettings(WebLayerBackendSettings):
240  def GetApkName(self, device):
241    assert self.apk_name.endswith('_bundle')
242    del device  # unused
243    # Bundles are created using the generated tool in the output directory's
244    # bin directory instead of being output to the apk directory at compile
245    # time like a normal APK.
246    return os.path.join('..', 'bin', self.apk_name)
247
248  def FindSupportApks(self, apk_path, chrome_root):
249    del chrome_root
250    # Try to find the WebLayer embedder in apk directory.
251    if apk_path is not None:
252      embedder_apk_path = os.path.join(
253          os.path.dirname(apk_path), '..', 'apks', self.embedder_apk_name)
254      if os.path.exists(embedder_apk_path):
255        return [embedder_apk_path]
256    return []
257
258
259ANDROID_CONTENT_SHELL = AndroidBrowserBackendSettings(
260    browser_type='android-content-shell',
261    package='org.chromium.content_shell_apk',
262    activity='org.chromium.content_shell_apk.ContentShellActivity',
263    command_line_name='content-shell-command-line',
264    devtools_port='localabstract:content_shell_devtools_remote',
265    apk_name='ContentShell.apk',
266    embedder_apk_name=None,
267    supports_tab_control=False,
268    supports_spki_list=True,
269    additional_apk_name=None)
270
271# TODO(crbug.com/1038137): Add reference setting for android-weblayer
272ANDROID_WEBLAYER = WebLayerBackendSettings(
273    browser_type='android-weblayer')
274
275# TODO(crbug.com/1038137): Add reference setting for android-weblayer
276ANDROID_WEBLAYER_GOOGLE_BUNDLE = WebLayerBundleBackendSettings(
277    browser_type='android-weblayer-google-bundle',
278    apk_name='monochrome_bundle')
279
280# TODO(crbug.com/1038137): Add reference setting for android-weblayer
281ANDROID_WEBLAYER_STANDALONE_GOOGLE_BUNDLE = WebLayerBundleBackendSettings(
282    browser_type='android-weblayer-standalone-google-bundle',
283    apk_name='system_webview_google_bundle')
284
285# TODO(crbug.com/1038137): Add reference setting for android-webview
286ANDROID_WEBVIEW = WebViewBackendSettings(
287    browser_type='android-webview')
288
289# TODO(crbug.com/1038137): Add reference setting for android-webview
290ANDROID_WEBVIEW_STANDALONE = WebViewBackendSettings(
291    apk_name='SystemWebView.apk',
292    browser_type='android-webview-standalone')
293
294# TODO(crbug.com/1038137): Add reference setting for android-webview
295ANDROID_WEBVIEW_STANDALONE_BUNDLE = WebViewBundleBackendSettings(
296    browser_type='android-webview-standalone-bundle',
297    apk_name='system_webview_bundle')
298
299# TODO(crbug.com/1038137): Add reference setting for android-webview
300ANDROID_WEBVIEW_TRICHROME = WebViewBackendSettings(
301    apk_name='TrichromeWebView.apk',
302    additional_apk_name='TrichromeLibrary.apk',
303    browser_type='android-webview-trichrome')
304
305# TODO(crbug.com/1038137): Add reference setting for android-webview
306ANDROID_WEBVIEW_TRICHROME_BUNDLE = WebViewBackendSettings(
307    apk_name='trichrome_webview_bundle',
308    additional_apk_name='TrichromeLibrary.apk',
309    browser_type='android-webview-trichrome-bundle')
310
311# TODO(crbug.com/1038137): Add reference setting for android-webview-bundle
312ANDROID_WEBVIEW_BUNDLE = WebViewBundleBackendSettings(
313    browser_type='android-webview-bundle',
314    apk_name='monochrome_public_bundle')
315
316# TODO(crbug.com/1038137): Add reference setting for android-webview-google
317ANDROID_WEBVIEW_GOOGLE = WebViewGoogleBackendSettings(
318    browser_type='android-webview-google')
319
320# TODO(crbug.com/1038137): Add reference setting for
321# android-webview-google-bundle
322ANDROID_WEBVIEW_GOOGLE_BUNDLE = WebViewBundleBackendSettings(
323    browser_type='android-webview-google-bundle',
324    apk_name='monochrome_bundle')
325
326# TODO(crbug.com/1038137): Add reference setting for android-webview
327ANDROID_WEBVIEW_STANDALONE_GOOGLE = WebViewBackendSettings(
328    apk_name='SystemWebViewGoogle.apk',
329    browser_type='android-webview-standalone-google')
330
331# TODO(crbug.com/1038137): Add reference setting for android-webview
332ANDROID_WEBVIEW_STANDALONE_GOOGLE_BUNDLE = WebViewBundleBackendSettings(
333    browser_type='android-webview-standalone-google-bundle',
334    apk_name='system_webview_google_bundle')
335
336# TODO(crbug.com/1038137): Add reference setting for android-webview
337ANDROID_WEBVIEW_TRICHROME_GOOGLE = WebViewBackendSettings(
338    apk_name='TrichromeWebViewGoogle.apk',
339    additional_apk_name='TrichromeLibraryGoogle.apk',
340    browser_type='android-webview-trichrome-google')
341
342# TODO(crbug.com/1038137): Add reference setting for android-webview
343ANDROID_WEBVIEW_TRICHROME_GOOGLE_BUNDLE = WebViewBackendSettings(
344    apk_name='trichrome_webview_google_bundle',
345    additional_apk_name='TrichromeLibraryGoogle.apk',
346    browser_type='android-webview-trichrome-google-bundle')
347
348ANDROID_WEBVIEW_INSTRUMENTATION = WebViewBasedBackendSettings(
349    browser_type='android-webview-instrumentation',
350    package='org.chromium.android_webview.shell',
351    activity='org.chromium.android_webview.shell.AwShellActivity',
352    command_line_name='android-webview-command-line',
353    apk_name='WebViewInstrumentation.apk')
354
355ANDROID_CHROMIUM = GenericChromeBackendSettings(
356    browser_type='android-chromium',
357    package='org.chromium.chrome',
358    apk_name='ChromePublic.apk')
359
360ANDROID_CHROMIUM_BUNDLE = GenericChromeBundleBackendSettings(
361    browser_type='android-chromium-bundle',
362    package='org.chromium.chrome',
363    apk_name='chrome_modern_public_bundle')
364
365ANDROID_CHROMIUM_MONOCHROME = GenericChromeBackendSettings(
366    browser_type='android-chromium-monochrome',
367    package='org.chromium.chrome',
368    apk_name='MonochromePublic.apk'
369)
370
371# TODO(crbug.com/1038137): Add reference setting for android-chrome
372ANDROID_CHROME = ChromeBackendSettings(
373    browser_type='android-chrome',
374    package='com.google.android.apps.chrome')
375
376ANDROID_CHROME_BUNDLE = GenericChromeBundleBackendSettings(
377    browser_type='android-chrome-bundle',
378    package='com.google.android.apps.chrome',
379    apk_name='monochrome_bundle')
380
381REFERENCE_ANDROID_CHROME_BUNDLE = GenericChromeBackendSettings(
382    browser_type='reference-android-chrome-bundle',
383    package='com.google.android.apps.chrome')
384
385# TODO(crbug.com/1038137): Add reference setting for android-chrome-64-bundle
386ANDROID_CHROME_64_BUNDLE = GenericChromeBundleBackendSettings(
387    browser_type='android-chrome-64-bundle',
388    package='com.google.android.apps.chrome',
389    apk_name='monochrome_64_32_bundle')
390
391ANDROID_CHROME_BETA = GenericChromeBackendSettings(
392    browser_type='android-chrome-beta',
393    package='com.chrome.beta')
394
395ANDROID_CHROME_DEV = GenericChromeBackendSettings(
396    browser_type='android-chrome-dev',
397    package='com.chrome.dev')
398
399ANDROID_CHROME_CANARY = GenericChromeBackendSettings(
400    browser_type='android-chrome-canary',
401    package='com.chrome.canary')
402
403ANDROID_SYSTEM_CHROME = GenericChromeBackendSettings(
404    browser_type='android-system-chrome',
405    package='com.android.chrome')
406
407
408ANDROID_BACKEND_SETTINGS = (
409    ANDROID_CONTENT_SHELL,
410    ANDROID_WEBLAYER,
411    ANDROID_WEBLAYER_GOOGLE_BUNDLE,
412    ANDROID_WEBLAYER_STANDALONE_GOOGLE_BUNDLE,
413    ANDROID_WEBVIEW,
414    ANDROID_WEBVIEW_BUNDLE,
415    ANDROID_WEBVIEW_GOOGLE,
416    ANDROID_WEBVIEW_GOOGLE_BUNDLE,
417    ANDROID_WEBVIEW_INSTRUMENTATION,
418    ANDROID_WEBVIEW_STANDALONE,
419    ANDROID_WEBVIEW_STANDALONE_BUNDLE,
420    ANDROID_WEBVIEW_STANDALONE_GOOGLE,
421    ANDROID_WEBVIEW_STANDALONE_GOOGLE_BUNDLE,
422    ANDROID_WEBVIEW_TRICHROME,
423    ANDROID_WEBVIEW_TRICHROME_BUNDLE,
424    ANDROID_WEBVIEW_TRICHROME_GOOGLE,
425    ANDROID_WEBVIEW_TRICHROME_GOOGLE_BUNDLE,
426    ANDROID_CHROMIUM,
427    ANDROID_CHROMIUM_BUNDLE,
428    ANDROID_CHROMIUM_MONOCHROME,
429    REFERENCE_ANDROID_CHROME_BUNDLE,
430    ANDROID_CHROME,
431    ANDROID_CHROME_64_BUNDLE,
432    ANDROID_CHROME_BUNDLE,
433    ANDROID_CHROME_BETA,
434    ANDROID_CHROME_DEV,
435    ANDROID_CHROME_CANARY,
436    ANDROID_SYSTEM_CHROME
437)
438