Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(192)

Unified Diff: tools/telemetry/telemetry/internal/backends/mandoline/android_mandoline_backend.py

Issue 1252253003: Support Telemetry tests on Android Mandoline. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@7_android_tele
Patch Set: Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: tools/telemetry/telemetry/internal/backends/mandoline/android_mandoline_backend.py
diff --git a/tools/telemetry/telemetry/internal/backends/chrome/android_browser_backend.py b/tools/telemetry/telemetry/internal/backends/mandoline/android_mandoline_backend.py
similarity index 27%
copy from tools/telemetry/telemetry/internal/backends/chrome/android_browser_backend.py
copy to tools/telemetry/telemetry/internal/backends/mandoline/android_mandoline_backend.py
index 37874d484c44982d6ded459f255c6e9d27b092ab..3aadab34a9284d721dedbf361d605f7d39e7cd2a 100644
--- a/tools/telemetry/telemetry/internal/backends/chrome/android_browser_backend.py
+++ b/tools/telemetry/telemetry/internal/backends/mandoline/android_mandoline_backend.py
@@ -1,48 +1,45 @@
-# Copyright 2013 The Chromium Authors. All rights reserved.
+# Copyright 2015 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.
import logging
+import os
+import random
+import re
import sys
from telemetry.core import exceptions
+from telemetry.core import util
+from telemetry.internal.backends.mandoline import android
+from telemetry.internal.backends.mandoline import config
+from telemetry.internal.backends.mandoline import mandoline_browser_backend
from telemetry.internal.platform import android_platform_backend as \
android_platform_backend_module
-from telemetry.core import util
-from telemetry.internal.backends import android_command_line_backend
-from telemetry.internal.backends import browser_backend
-from telemetry.internal.backends.chrome import chrome_browser_backend
-from telemetry.internal import forwarders
util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android')
-from pylib.device import intent
-
-
-class AndroidBrowserBackend(chrome_browser_backend.ChromeBrowserBackend):
- """The backend for controlling a browser instance running on Android."""
- def __init__(self, android_platform_backend, browser_options,
- backend_settings, output_profile_path, extensions_to_load,
- target_arch):
+try:
+ from pylib import constants
+except ImportError:
+ pass
+
+class AndroidMandolineBackend(
+ mandoline_browser_backend.MandolineBrowserBackend):
+ """The backend for controlling a mandoline browser instance running on
+ Android."""
+
+ def __init__(self, android_platform_backend, browser_options, target_arch,
+ browser_type, build_path, package):
assert isinstance(android_platform_backend,
android_platform_backend_module.AndroidPlatformBackend)
- super(AndroidBrowserBackend, self).__init__(
+ super(AndroidMandolineBackend, self).__init__(
android_platform_backend,
- supports_tab_control=backend_settings.supports_tab_control,
- supports_extensions=False, browser_options=browser_options,
- output_profile_path=output_profile_path,
- extensions_to_load=extensions_to_load)
- if len(extensions_to_load) > 0:
- raise browser_backend.ExtensionsNotSupportedException(
- 'Android browser does not support extensions.')
-
- # Initialize fields so that an explosion during init doesn't break in Close.
- self._backend_settings = backend_settings
- self._target_arch = target_arch
- self._saved_sslflag = ''
+ browser_options=browser_options)
- # TODO(tonyg): This is flaky because it doesn't reserve the port that it
- # allocates. Need to fix this.
- self._port = util.GetUnreservedAvailableLocalPort()
+ self._target_arch = target_arch
+ self._browser_type = browser_type
+ self._build_path = build_path
+ self._package = package
+ self._device_port = None
# TODO(wuhu): Move to network controller backend.
self.platform_backend.InstallTestCa()
@@ -50,98 +47,88 @@ class AndroidBrowserBackend(chrome_browser_backend.ChromeBrowserBackend):
# Kill old browser.
self._KillBrowser()
- if self.device.HasRoot() or self.device.NeedsSU():
- if self.browser_options.profile_dir:
- self.platform_backend.PushProfile(
- self._backend_settings.package,
- self.browser_options.profile_dir)
- elif not self.browser_options.dont_override_profile:
- self.platform_backend.RemoveProfile(
- self._backend_settings.package,
- self._backend_settings.profile_ignore_list)
-
- if self.browser_options.netsim:
- assert self.platform_backend.use_rndis_forwarder, (
- 'Netsim requires RNDIS forwarding.')
- self.wpr_port_pairs = forwarders.PortPairs(
- http=forwarders.PortPair(0, 80),
- https=forwarders.PortPair(0, 443),
- dns=forwarders.PortPair(0, 53))
-
- # Set the debug app if needed.
- self.platform_backend.SetDebugApp(self._backend_settings.package)
-
@property
def device(self):
return self.platform_backend.device
def _KillBrowser(self):
if self.device.IsUserBuild():
- self.platform_backend.StopApplication(self._backend_settings.package)
+ self.platform_backend.StopApplication(self._package)
else:
- self.platform_backend.KillApplication(self._backend_settings.package)
+ self.platform_backend.KillApplication(self._package)
def Start(self):
self.device.RunShellCommand('logcat -c')
- if self.browser_options.startup_url:
- url = self.browser_options.startup_url
- elif self.browser_options.profile_dir:
- url = None
- else:
- # If we have no existing tabs start with a blank page since default
- # startup with the NTP can lead to race conditions with Telemetry
- url = 'about:blank'
self.platform_backend.DismissCrashDialogIfNeeded()
- browser_startup_args = self.GetBrowserStartupArgs()
- with android_command_line_backend.SetUpCommandLineFlags(
- self.device, self._backend_settings, browser_startup_args):
- self.device.StartActivity(
- intent.Intent(package=self._backend_settings.package,
- activity=self._backend_settings.activity,
- action=None, data=url, category=None),
- blocking=True)
-
- remote_devtools_port = self._backend_settings.GetDevtoolsRemotePort(
- self.device)
- self.platform_backend.ForwardHostToDevice(self._port,
- remote_devtools_port)
- try:
- self._WaitForBrowserToComeUp()
- self._InitDevtoolsClientBackend(remote_devtools_port)
- except exceptions.BrowserGoneException:
- logging.critical('Failed to connect to browser.')
- if not (self.device.HasRoot() or self.device.NeedsSU()):
- logging.critical(
- 'Resolve this by either: '
- '(1) Flashing to a userdebug build OR '
- '(2) Manually enabling web debugging in Chrome at '
- 'Settings > Developer tools > Enable USB Web debugging.')
- sys.exit(1)
- except:
- import traceback
- traceback.print_exc()
- self.Close()
- raise
+ self._port = util.GetUnreservedAvailableLocalPort()
+ self._device_port = self._GetAvailableDevicePort()
+
+ self.device.adb.Forward('tcp:%d' % self._port, 'tcp:%d' % self._device_port)
+ logging.info('Forwarded host port %d to device port %d.' %
+ (self._port, self._device_port))
+
+ args = self.GetBrowserStartupArgs()
+
+ if self.browser_options.startup_url:
+ # TODO(yzshen): For now "about:blank" is not supported yet.
+ if self.browser_options.startup_url != "about:blank":
+ args.append(self.browser_options.startup_url)
+ else:
+ logging.warning('Ignoring startup URL about:blank.')
+
+ logging.debug('Starting Mandoline %s', args)
+
+ is_debug = 'debug' in self._browser_type
+ mandoline_config = config.Config(build_dir=self._build_path,
+ target_os=config.Config.OS_ANDROID,
+ target_cpu=self._target_arch,
+ is_debug=is_debug,
+ apk_name='Mandoline.apk')
+ shell = android.AndroidShell(mandoline_config)
+ shell.InitShell(self.device)
+
+ output = sys.stdout
+ if not self.browser_options.show_stdout:
+ output = open(os.devnull, 'w')
+ logging_process = shell.ShowLogs(output)
+
+ # Unlock device screen.
+ self.device.SendKeyEvent(constants.keyevent.KEYCODE_MENU)
+ shell.StartActivity(self.activity, args, output, logging_process.terminate)
+
+ try:
+ self._WaitForBrowserToComeUp()
+ self._InitDevtoolsClientBackend(self._port)
+ except exceptions.BrowserGoneException:
+ logging.critical('Failed to connect to browser.')
+ # TODO(yzshen): It seems wrong to catch exception and then exit, but this
+ # matches the behavior of AndroidBrowserBackend. Consider fixing both.
+ sys.exit(1)
+ except:
+ import traceback
+ traceback.print_exc()
+ self.Close()
+ raise
def GetBrowserStartupArgs(self):
- args = super(AndroidBrowserBackend, self).GetBrowserStartupArgs()
- args.append('--enable-remote-debugging')
- args.append('--disable-fre')
- args.append('--disable-external-intent-requests')
+ args = super(AndroidMandolineBackend, self).GetBrowserStartupArgs()
+ # TODO(yzshen): Mandoline on Android doesn't support excessively long
+ # command line yet. Remove fieldtrial-related flags as a temp fix because
+ # they are long and not processed by Mandoline anyway.
+ # http://crbug.com/514285
+ args = [arg for arg in args if arg.find('--force-fieldtrials=') == -1 and
+ arg.find('--force-fieldtrial-params=') == -1]
+ args.append('--remote-debugging-port=%i' % self._device_port)
return args
@property
def pid(self):
- pids = self.device.GetPids(self._backend_settings.package)
- if not pids or self._backend_settings.package not in pids:
+ pids = self.device.GetPids(self._package)
+ if not pids or self._package not in pids:
raise exceptions.BrowserGoneException(self.browser)
- if len(pids[self._backend_settings.package]) > 1:
- raise Exception(
- 'At most one instance of process %s expected but found pids: '
- '%s' % (self._backend_settings.package, pids))
- return int(pids[self._backend_settings.package][0])
+ return int(pids[self._package])
@property
def browser_directory(self):
@@ -149,32 +136,34 @@ class AndroidBrowserBackend(chrome_browser_backend.ChromeBrowserBackend):
@property
def profile_directory(self):
- return self._backend_settings.profile_dir
+ raise NotImplementedError()
@property
def package(self):
- return self._backend_settings.package
+ return self._package
@property
def activity(self):
- return self._backend_settings.activity
+ return 'MandolineActivity'
def __del__(self):
self.Close()
def Close(self):
- super(AndroidBrowserBackend, self).Close()
+ super(AndroidMandolineBackend, self).Close()
self.platform_backend.RemoveTestCa()
self._KillBrowser()
- if self._output_profile_path:
- self.platform_backend.PullProfile(
- self._backend_settings.package, self._output_profile_path)
+ if self._port:
+ # TODO(yzshen): Consider remove the port forwarding. Currently AdbWrapper
+ # doesn't expose necessary API.
+ self._port = None
+ self._device_port = None
def IsBrowserRunning(self):
- return self.platform_backend.IsAppRunning(self._backend_settings.package)
+ return self.platform_backend.IsAppRunning(self._package)
def GetStandardOutput(self):
return self.platform_backend.GetStandardOutput()
@@ -185,3 +174,31 @@ class AndroidBrowserBackend(chrome_browser_backend.ChromeBrowserBackend):
@property
def should_ignore_certificate_errors(self):
return not self.platform_backend.is_test_ca_installed
+
+ def _GetAvailableDevicePort(self):
+ used_ports = []
+ netstat_results = self.device.RunShellCommand(
+ ['netstat', '-a'], check_return=True, large_output=True)
+ pattern = re.compile(r"^(127\.0\.0\.1:|localhost:)(\d+)$")
+ for single_connect in netstat_results:
+ # Column 3 is the local address which we want to check with.
+ connect_results = single_connect.split()
+ if connect_results[0] != 'tcp':
+ continue
+ if len(connect_results) < 6:
+ raise Exception('Unexpected format while parsing netstat line: ' +
+ single_connect)
+ result = re.match(pattern, connect_results[3])
+ if result:
+ used_ports.append(int(result.group(2)))
+
+ # The range of ephemeral ports on Android.
+ dynamic_port_begin = 32768
+ dynamic_port_end = 61000
+ assert len(used_ports) < dynamic_port_end - dynamic_port_begin + 1
+
+ available_port = random.randint(dynamic_port_begin, dynamic_port_end)
+ while available_port in used_ports:
+ available_port = random.randint(dynamic_port_begin, dynamic_port_end)
+
+ return available_port

Powered by Google App Engine
This is Rietveld 408576698