| 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
|
|
|