Index: tools/telemetry/telemetry/core/platform/android_platform_backend.py |
diff --git a/tools/telemetry/telemetry/core/platform/android_platform_backend.py b/tools/telemetry/telemetry/core/platform/android_platform_backend.py |
index 49ee2d1e73b3a52b2ea41eae0049f5ac3f745166..1acbe1675c301a12cda33ddd59d95f6f2c03ae44 100644 |
--- a/tools/telemetry/telemetry/core/platform/android_platform_backend.py |
+++ b/tools/telemetry/telemetry/core/platform/android_platform_backend.py |
@@ -3,7 +3,11 @@ |
# found in the LICENSE file. |
import logging |
+import os |
+import re |
+import subprocess |
import tempfile |
+import time |
from telemetry import decorators |
from telemetry.core import exceptions |
@@ -23,6 +27,7 @@ from telemetry.core.platform.profiler import android_prebuilt_profiler_helper |
# Get build/android scripts into our path. |
util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android') |
from pylib import screenshot # pylint: disable=F0401 |
+from pylib.device import device_errors # pylint: disable=F0401 |
from pylib.perf import cache_control # pylint: disable=F0401 |
from pylib.perf import perf_control # pylint: disable=F0401 |
from pylib.perf import thermal_throttle # pylint: disable=F0401 |
@@ -69,6 +74,8 @@ class AndroidPlatformBackend( |
self._video_recorder = None |
self._installed_applications = None |
+ self._wpr_ca_cert_path = None |
+ |
@classmethod |
def SupportsDevice(cls, device): |
return isinstance(device, android_device.AndroidDevice) |
@@ -184,10 +191,6 @@ class AndroidPlatformBackend( |
raise exceptions.ProcessGoneException() |
return ps[0][1] |
- @decorators.Cache |
- def GetArchName(self): |
- return self._device.GetABI() |
- |
def GetOSName(self): |
return 'android' |
@@ -210,10 +213,24 @@ class AndroidPlatformBackend( |
def StopApplication(self, application): |
"""Stop the given |application|. |
- Args: |
- application: The full package name string of the application to launch. |
+ |
+ Args: |
+ application: The full package name string of the application to stop. |
""" |
- self._adb.device().ForceStop(application) |
+ self._device.ForceStop(application) |
+ |
+ def KillApplication(self, application): |
+ """Kill the given application. |
+ |
+ Args: |
+ application: The full package name string of the application to kill. |
+ """ |
+ # We use KillAll rather than ForceStop for efficiency reasons. |
+ try: |
+ self._adb.device().KillAll(application, retries=0) |
+ time.sleep(3) |
+ except device_errors.CommandFailedError: |
+ pass |
def LaunchApplication( |
self, application, parameters=None, elevate_privilege=False): |
@@ -336,3 +353,181 @@ class AndroidPlatformBackend( |
cstates['C0'] -= int(times[i]) |
sample_stats[cpu] = cstates |
return sample_stats |
+ |
+ def SetRelaxSslCheck(self, value): |
+ old_flag = self._device.GetProp('socket.relaxsslcheck') |
+ self._device.SetProp('socket.relaxsslcheck', value) |
+ return old_flag |
+ |
+ def ForwardHostToDevice(self, host_port, device_port): |
+ self._adb.Forward('tcp:%d' % host_port, device_port) |
+ |
+ def DismissCrashDialogIfNeeded(self): |
+ """Dismiss any error dialogs. |
+ |
+ Limit the number in case we have an error loop or we are failing to dismiss. |
+ """ |
+ for _ in xrange(10): |
+ if not self._device.old_interface.DismissCrashDialogIfNeeded(): |
+ break |
+ |
+ def IsAppRunning(self, process_name): |
+ """Determine if the given process is running. |
+ |
+ Args: |
+ process_name: The full package name string of the process. |
+ """ |
+ pids = self._adb.ExtractPid(process_name) |
+ return len(pids) != 0 |
+ |
+ @property |
+ def wpr_ca_cert_path(self): |
+ """Path to root certificate installed on browser (or None). |
+ |
+ If this is set, web page replay will use it to sign HTTPS responses. |
+ """ |
+ if self._wpr_ca_cert_path: |
+ assert os.path.isfile(self._wpr_ca_cert_path) |
+ return self._wpr_ca_cert_path |
+ |
+ def PushProfile(self, package, new_profile_dir): |
+ """Replace application profile with files found on host machine. |
+ |
+ Pushing the profile is slow, so we don't want to do it every time. |
+ Avoid this by pushing to a safe location using PushChangedFiles, and |
+ then copying into the correct location on each test run. |
+ |
+ Args: |
+ package: The full package name string of the application for which the |
+ profile is to be updated. |
+ new_profile_dir: Location where profile to be pushed is stored on the |
+ host machine. |
+ """ |
+ (profile_parent, profile_base) = os.path.split(new_profile_dir) |
+ # If the path ends with a '/' python split will return an empty string for |
+ # the base name; so we now need to get the base name from the directory. |
+ if not profile_base: |
+ profile_base = os.path.basename(profile_parent) |
+ |
+ saved_profile_location = '/sdcard/profile/%s' % profile_base |
+ self._device.PushChangedFiles(new_profile_dir, saved_profile_location) |
+ |
+ profile_dir = self._GetProfileDir(package) |
+ self._device.old_interface.EfficientDeviceDirectoryCopy( |
+ saved_profile_location, profile_dir) |
+ dumpsys = self._device.RunShellCommand('dumpsys package %s' % package) |
+ id_line = next(line for line in dumpsys if 'userId=' in line) |
+ uid = re.search(r'\d+', id_line).group() |
+ files = self._device.RunShellCommand( |
+ 'ls "%s"' % profile_dir, as_root=True) |
+ files.remove('lib') |
+ paths = ['%s%s' % (profile_dir, f) for f in files] |
+ for path in paths: |
+ extended_path = '%s %s/* %s/*/* %s/*/*/*' % (path, path, path, path) |
+ self._device.RunShellCommand( |
+ 'chown %s.%s %s' % (uid, uid, extended_path)) |
+ |
+ def RemoveProfile(self, package, ignore_list): |
+ """Delete application profile on device. |
+ |
+ Args: |
+ package: The full package name string of the application for which the |
+ profile is to be deleted. |
+ ignore_list: List of files to keep. |
+ """ |
+ profile_dir = self._GetProfileDir(package) |
+ files = self._device.RunShellCommand( |
+ 'ls "%s"' % profile_dir, as_root=True) |
+ paths = ['"%s%s"' % (profile_dir, f) for f in files |
+ if f not in ignore_list] |
+ self._device.RunShellCommand('rm -r %s' % ' '.join(paths), as_root=True) |
+ |
+ def PullProfile(self, package, output_profile_path): |
+ """Copy application profile from device to host machine. |
+ |
+ Args: |
+ package: The full package name string of the application for which the |
+ profile is to be copied. |
+ output_profile_dir: Location where profile to be stored on host machine. |
+ """ |
+ profile_dir = self._GetProfileDir(package) |
+ logging.info("Pulling profile directory from device: '%s'->'%s'.", |
+ self._ProfileDir(package), output_profile_path) |
+ # To minimize bandwidth it might be good to look at whether all the data |
+ # pulled down is really needed e.g. .pak files. |
+ if not os.path.exists(output_profile_path): |
+ os.makedirs(output_profile_path) |
+ files = self._device.RunShellCommand('ls "%s"' % profile_dir) |
+ for f in files: |
+ # Don't pull lib, since it is created by the installer. |
+ if f != 'lib': |
+ source = '%s%s' % (profile_dir, f) |
+ dest = os.path.join(output_profile_path, f) |
+ # self._adb.Pull(source, dest) doesn't work because its timeout |
+ # is fixed in android's adb_interface at 60 seconds, which may |
+ # be too short to pull the cache. |
+ cmd = 'pull %s %s' % (source, dest) |
+ self._device.old_interface.Adb().SendCommand(cmd, timeout_time=240) |
+ |
+ def _GetProfileDir(self, package): |
+ """Returns the on-device location where the application profile is stored |
+ based on Android convention. |
+ |
+ Args: |
+ package: The full package name string of the application. |
+ """ |
+ return '/data/data/%s/' % package |
+ |
+ def SetDebugApp(self, package): |
+ """Set application to debugging. |
+ |
+ Args: |
+ package: The full package name string of the application. |
+ """ |
+ if self._adb.IsUserBuild(): |
+ logging.debug('User build device, setting debug app') |
+ self._device.RunShellCommand('am set-debug-app --persistent %s' % package) |
+ |
+ def GetStandardOutput(self, number_of_lines=500): |
+ """Returns most recent lines of logcat dump. |
+ |
+ Args: |
+ number_of_lines: Number of lines of log to return. |
+ """ |
+ return '\n'.join(self.adb.device().RunShellCommand( |
+ 'logcat -d -t %d' % number_of_lines)) |
+ |
+ def GetStackTrace(self, target_arch): |
+ """Returns stack trace. |
+ |
+ The stack trace consists of raw logcat dump, logcat dump with symbols, |
+ and stack info from tomstone files. |
+ |
+ Args: |
+ target_arch: String specifying device architecture (eg. arm, arm64, mips, |
+ x86, x86_64) |
+ """ |
+ def Decorate(title, content): |
+ return "%s\n%s\n%s\n" % (title, content, '*' * 80) |
+ # Get the last lines of logcat (large enough to contain stacktrace) |
+ logcat = self.GetStandardOutput() |
+ ret = Decorate('Logcat', logcat) |
+ stack = os.path.join(util.GetChromiumSrcDir(), 'third_party', |
+ 'android_platform', 'development', 'scripts', 'stack') |
+ # Try to symbolize logcat. |
+ if os.path.exists(stack): |
+ cmd = [stack] |
+ if target_arch: |
+ cmd.append('--arch=%s' % target_arch) |
+ p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) |
+ ret += Decorate('Stack from Logcat', p.communicate(input=logcat)[0]) |
+ |
+ # Try to get tombstones. |
+ tombstones = os.path.join(util.GetChromiumSrcDir(), 'build', 'android', |
+ 'tombstones.py') |
+ if os.path.exists(tombstones): |
+ ret += Decorate('Tombstones', |
+ subprocess.Popen([tombstones, '-w', '--device', |
+ self._adb.device_serial()], |
+ stdout=subprocess.PIPE).communicate()[0]) |
+ return ret |