blob: 3eda4a5e8da2c5a4b1cbc9fb1b98284366d9be0a [file] [log] [blame]
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Finds android browsers that can be controlled by telemetry."""
import logging
import os
import sys
from py_utils import dependency_util
from devil import base_error
from devil.android import apk_helper
from telemetry.core import exceptions
from telemetry.core import platform
from telemetry.core import util
from telemetry import decorators
from telemetry.internal.backends import android_browser_backend_settings
from telemetry.internal.backends.chrome import android_browser_backend
from telemetry.internal.browser import browser
from telemetry.internal.browser import possible_browser
from telemetry.internal.platform import android_device
from telemetry.internal.util import binary_manager
CHROME_PACKAGE_NAMES = {
'android-content-shell':
['org.chromium.content_shell_apk',
android_browser_backend_settings.ContentShellBackendSettings,
'ContentShell.apk'],
'android-webview':
['org.chromium.webview_shell',
android_browser_backend_settings.WebviewBackendSettings,
None],
'android-webview-shell':
['org.chromium.android_webview.shell',
android_browser_backend_settings.WebviewShellBackendSettings,
'AndroidWebView.apk'],
'android-chromium':
['org.chromium.chrome',
android_browser_backend_settings.ChromeBackendSettings,
'ChromePublic.apk'],
'android-chrome':
['com.google.android.apps.chrome',
android_browser_backend_settings.ChromeBackendSettings,
'Chrome.apk'],
'android-chrome-work':
['com.chrome.work',
android_browser_backend_settings.ChromeBackendSettings,
None],
'android-chrome-beta':
['com.chrome.beta',
android_browser_backend_settings.ChromeBackendSettings,
None],
'android-chrome-dev':
['com.chrome.dev',
android_browser_backend_settings.ChromeBackendSettings,
None],
'android-chrome-canary':
['com.chrome.canary',
android_browser_backend_settings.ChromeBackendSettings,
None],
'android-system-chrome':
['com.android.chrome',
android_browser_backend_settings.ChromeBackendSettings,
None],
}
class PossibleAndroidBrowser(possible_browser.PossibleBrowser):
"""A launchable android browser instance."""
def __init__(self, browser_type, finder_options, android_platform,
backend_settings, apk_name):
super(PossibleAndroidBrowser, self).__init__(
browser_type, 'android', backend_settings.supports_tab_control)
assert browser_type in FindAllBrowserTypes(finder_options), (
'Please add %s to android_browser_finder.FindAllBrowserTypes' %
browser_type)
self._platform = android_platform
self._platform_backend = (
android_platform._platform_backend) # pylint: disable=protected-access
self._backend_settings = backend_settings
self._local_apk = None
if browser_type == 'exact':
if not os.path.exists(apk_name):
raise exceptions.PathMissingError(
'Unable to find exact apk %s specified by --browser-executable' %
apk_name)
self._local_apk = apk_name
elif browser_type == 'reference':
if not os.path.exists(apk_name):
raise exceptions.PathMissingError(
'Unable to find reference apk at expected location %s.' % apk_name)
self._local_apk = apk_name
elif apk_name:
assert finder_options.chrome_root, (
'Must specify Chromium source to use apk_name')
chrome_root = finder_options.chrome_root
candidate_apks = []
for build_path in util.GetBuildDirectories(chrome_root):
apk_full_name = os.path.join(build_path, 'apks', apk_name)
if os.path.exists(apk_full_name):
last_changed = os.path.getmtime(apk_full_name)
candidate_apks.append((last_changed, apk_full_name))
if candidate_apks:
# Find the candidate .apk with the latest modification time.
newest_apk_path = sorted(candidate_apks)[-1][1]
self._local_apk = newest_apk_path
def __repr__(self):
return 'PossibleAndroidBrowser(browser_type=%s)' % self.browser_type
def _InitPlatformIfNeeded(self):
pass
def Create(self, finder_options):
self._InitPlatformIfNeeded()
browser_backend = android_browser_backend.AndroidBrowserBackend(
self._platform_backend,
finder_options.browser_options, self._backend_settings)
try:
return browser.Browser(
browser_backend, self._platform_backend, self._credentials_path)
except Exception:
logging.exception('Failure while creating Android browser.')
original_exception = sys.exc_info()
try:
browser_backend.Close()
except Exception:
logging.exception('Secondary failure while closing browser backend.')
raise original_exception[0], original_exception[1], original_exception[2]
def SupportsOptions(self, browser_options):
if len(browser_options.extensions_to_load) != 0:
return False
return True
def HaveLocalAPK(self):
return self._local_apk and os.path.exists(self._local_apk)
@decorators.Cache
def UpdateExecutableIfNeeded(self):
if self.HaveLocalAPK():
logging.warn('Installing %s on device if needed.' % self._local_apk)
self.platform.InstallApplication(self._local_apk)
def last_modification_time(self):
if self.HaveLocalAPK():
return os.path.getmtime(self._local_apk)
return -1
def SelectDefaultBrowser(possible_browsers):
"""Return the newest possible browser."""
if not possible_browsers:
return None
return max(possible_browsers, key=lambda b: b.last_modification_time())
def CanFindAvailableBrowsers():
return android_device.CanDiscoverDevices()
def CanPossiblyHandlePath(target_path):
return os.path.splitext(target_path.lower())[1] == '.apk'
def FindAllBrowserTypes(options):
del options # unused
return CHROME_PACKAGE_NAMES.keys() + ['exact', 'reference']
def _FindAllPossibleBrowsers(finder_options, android_platform):
"""Testable version of FindAllAvailableBrowsers."""
if not android_platform:
return []
possible_browsers = []
# Add the exact APK if given.
if (finder_options.browser_executable and
CanPossiblyHandlePath(finder_options.browser_executable)):
apk_name = os.path.basename(finder_options.browser_executable)
normalized_path = os.path.expanduser(finder_options.browser_executable)
exact_package = apk_helper.GetPackageName(normalized_path)
package_info = next(
(info for info in CHROME_PACKAGE_NAMES.itervalues()
if info[0] == exact_package or info[2] == apk_name), None)
# It is okay if the APK name or package doesn't match any of known chrome
# browser APKs, since it may be of a different browser.
if package_info:
if not exact_package:
raise exceptions.PackageDetectionError(
'Unable to find package for %s specified by --browser-executable' %
normalized_path)
[package, backend_settings, _] = package_info
if package == exact_package:
possible_browsers.append(PossibleAndroidBrowser(
'exact',
finder_options,
android_platform,
backend_settings(package),
normalized_path))
else:
raise exceptions.UnknownPackageError(
'%s specified by --browser-executable has an unknown package: %s' %
(normalized_path, exact_package))
# Add the reference build if found.
os_version = dependency_util.GetChromeApkOsVersion(
android_platform.GetOSVersionName())
arch = android_platform.GetArchName()
try:
reference_build = binary_manager.FetchPath(
'chrome_stable', arch, 'android', os_version)
except (binary_manager.NoPathFoundError,
binary_manager.CloudStorageError):
reference_build = None
if reference_build and os.path.exists(reference_build):
# TODO(aiolos): how do we stably map the android chrome_stable apk to the
# correct package name?
package, backend_settings, _ = CHROME_PACKAGE_NAMES['android-chrome']
possible_browsers.append(PossibleAndroidBrowser(
'reference',
finder_options,
android_platform,
backend_settings(package),
reference_build))
# Add any known local versions.
for name, package_info in CHROME_PACKAGE_NAMES.iteritems():
package, backend_settings, apk_name = package_info
if apk_name and not finder_options.chrome_root:
continue
b = PossibleAndroidBrowser(name,
finder_options,
android_platform,
backend_settings(package),
apk_name)
if b.platform.CanLaunchApplication(package) or b.HaveLocalAPK():
possible_browsers.append(b)
return possible_browsers
def FindAllAvailableBrowsers(finder_options, device):
"""Finds all the possible browsers on one device.
The device is either the only device on the host platform,
or |finder_options| specifies a particular device.
"""
if not isinstance(device, android_device.AndroidDevice):
return []
try:
android_platform = platform.GetPlatformForDevice(device, finder_options)
return _FindAllPossibleBrowsers(finder_options, android_platform)
except base_error.BaseError as e:
logging.error('Unable to find browsers on %s: %s', device.device_id, str(e))
return []