summaryrefslogtreecommitdiff
path: root/systrace/catapult/devil
diff options
context:
space:
mode:
Diffstat (limited to 'systrace/catapult/devil')
-rw-r--r--systrace/catapult/devil/BUILD.gn32
-rw-r--r--systrace/catapult/devil/README.md4
-rwxr-xr-xsystrace/catapult/devil/bin/run_py_tests6
-rw-r--r--systrace/catapult/devil/devil/android/apk_helper.py150
-rwxr-xr-xsystrace/catapult/devil/devil/android/apk_helper_test.py157
-rw-r--r--systrace/catapult/devil/devil/android/battery_utils.py46
-rwxr-xr-xsystrace/catapult/devil/devil/android/battery_utils_test.py51
-rw-r--r--systrace/catapult/devil/devil/android/cpu_temperature.py154
-rw-r--r--systrace/catapult/devil/devil/android/cpu_temperature_test.py132
-rw-r--r--systrace/catapult/devil/devil/android/crash_handler.py3
-rw-r--r--systrace/catapult/devil/devil/android/decorators.py2
-rw-r--r--systrace/catapult/devil/devil/android/device_errors.py34
-rw-r--r--systrace/catapult/devil/devil/android/device_utils.py687
-rwxr-xr-xsystrace/catapult/devil/devil/android/device_utils_devicetest.py33
-rwxr-xr-xsystrace/catapult/devil/devil/android/device_utils_test.py625
-rw-r--r--systrace/catapult/devil/devil/android/fastboot_utils.py2
-rw-r--r--systrace/catapult/devil/devil/android/flag_changer.py51
-rwxr-xr-xsystrace/catapult/devil/devil/android/flag_changer_test.py13
-rw-r--r--systrace/catapult/devil/devil/android/forwarder.py2
-rw-r--r--systrace/catapult/devil/devil/android/logcat_monitor.py13
-rw-r--r--systrace/catapult/devil/devil/android/md5sum.py6
-rw-r--r--systrace/catapult/devil/devil/android/ndk/__init__.py6
-rw-r--r--systrace/catapult/devil/devil/android/ndk/abis.py16
-rw-r--r--systrace/catapult/devil/devil/android/perf/perf_control.py256
-rw-r--r--systrace/catapult/devil/devil/android/perf/perf_control_test.py105
-rw-r--r--systrace/catapult/devil/devil/android/perf/surface_stats_collector.py136
-rw-r--r--systrace/catapult/devil/devil/android/perf/surface_stats_collector_test.py40
-rw-r--r--systrace/catapult/devil/devil/android/perf/thermal_throttle.py1
-rw-r--r--systrace/catapult/devil/devil/android/ports.py2
-rw-r--r--systrace/catapult/devil/devil/android/sdk/adb_wrapper.py107
-rw-r--r--systrace/catapult/devil/devil/android/sdk/fastboot.py4
-rw-r--r--systrace/catapult/devil/devil/android/sdk/shared_prefs.py68
-rwxr-xr-xsystrace/catapult/devil/devil/android/sdk/shared_prefs_test.py45
-rw-r--r--systrace/catapult/devil/devil/android/sdk/version_codes.py5
-rwxr-xr-xsystrace/catapult/devil/devil/android/tools/device_monitor.py7
-rwxr-xr-xsystrace/catapult/devil/devil/android/tools/device_recovery.py66
-rwxr-xr-xsystrace/catapult/devil/devil/android/tools/provision_devices.py31
-rw-r--r--systrace/catapult/devil/devil/android/tools/script_common.py33
-rwxr-xr-xsystrace/catapult/devil/devil/android/tools/script_common_test.py2
-rwxr-xr-xsystrace/catapult/devil/devil/android/tools/system_app.py69
-rwxr-xr-xsystrace/catapult/devil/devil/android/tools/system_app_devicetest.py2
-rw-r--r--systrace/catapult/devil/devil/android/tools/system_app_test.py72
-rw-r--r--systrace/catapult/devil/devil/android/tools/unlock_bootloader.py2
-rwxr-xr-xsystrace/catapult/devil/devil/android/tools/webview_app.py205
-rw-r--r--systrace/catapult/devil/devil/devil_dependencies.json16
-rw-r--r--systrace/catapult/devil/devil/utils/__init__.py23
-rwxr-xr-xsystrace/catapult/devil/devil/utils/battor_device_mapping.py309
-rw-r--r--systrace/catapult/devil/devil/utils/cmd_helper.py36
-rwxr-xr-xsystrace/catapult/devil/devil/utils/find_usb_devices.py4
-rwxr-xr-xsystrace/catapult/devil/devil/utils/find_usb_devices_test.py161
-rw-r--r--systrace/catapult/devil/devil/utils/lazy/weak_constant.py23
-rw-r--r--systrace/catapult/devil/devil/utils/lazy/weak_constant_test.py70
-rw-r--r--systrace/catapult/devil/devil/utils/logging_common.py23
-rwxr-xr-xsystrace/catapult/devil/devil/utils/markdown.py12
-rw-r--r--systrace/catapult/devil/devil/utils/reraiser_thread.py30
-rwxr-xr-xsystrace/catapult/devil/devil/utils/reset_usb.py5
-rw-r--r--systrace/catapult/devil/devil/utils/run_tests_helper.py4
-rw-r--r--systrace/catapult/devil/devil/utils/test/data/test_serial_map.json1
-rw-r--r--systrace/catapult/devil/devil/utils/timeout_retry.py3
-rwxr-xr-xsystrace/catapult/devil/devil/utils/update_mapping.py47
-rw-r--r--systrace/catapult/devil/pylintrc1
61 files changed, 968 insertions, 3283 deletions
diff --git a/systrace/catapult/devil/BUILD.gn b/systrace/catapult/devil/BUILD.gn
deleted file mode 100644
index cf1255d..0000000
--- a/systrace/catapult/devil/BUILD.gn
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright 2017 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.
-
-group("devil") {
- testonly = true
- deps = []
- data_deps = [
- "../third_party/gsutil",
- ]
- data = [
- "devil/",
- ]
-
- if (is_android) {
- deps += [
- ":empty_system_webview_apk",
- "//buildtools/third_party/libc++($host_toolchain)",
- "//tools/android/forwarder2",
- "//tools/android/md5sum",
- ]
- }
-}
-
-if (is_android) {
- import("//testing/android/empty_apk/empty_apk.gni")
-
- empty_apk("empty_system_webview_apk") {
- package_name = "com.android.webview"
- apk_name = "EmptySystemWebView"
- }
-}
diff --git a/systrace/catapult/devil/README.md b/systrace/catapult/devil/README.md
index 9953e6a..852ac37 100644
--- a/systrace/catapult/devil/README.md
+++ b/systrace/catapult/devil/README.md
@@ -6,8 +6,8 @@
😈
-devil (device interaction layer) is a library used by the Chromium developers to
-interact with Android devices. It currently supports SDK level 16 and above.
+devil is a library used by the Chromium developers to interact with Android
+devices. It currently supports SDK level 16 and above.
## Interfaces
diff --git a/systrace/catapult/devil/bin/run_py_tests b/systrace/catapult/devil/bin/run_py_tests
index a74fa83..44ec61e 100755
--- a/systrace/catapult/devil/bin/run_py_tests
+++ b/systrace/catapult/devil/bin/run_py_tests
@@ -16,12 +16,6 @@ from catapult_build import run_with_typ
def main():
- # Tests mock out internal details of methods, and the ANDROID_SERIAL can
- # change which internal methods are called. Since tests don't actually use
- # devices, it should be fine to delete the variable.
- if 'ANDROID_SERIAL' in os.environ:
- del os.environ['ANDROID_SERIAL']
-
return run_with_typ.Run(top_level_dir=_DEVIL_PATH)
if __name__ == '__main__':
diff --git a/systrace/catapult/devil/devil/android/apk_helper.py b/systrace/catapult/devil/devil/android/apk_helper.py
index abdf907..8acb41e 100644
--- a/systrace/catapult/devil/devil/android/apk_helper.py
+++ b/systrace/catapult/devil/devil/android/apk_helper.py
@@ -5,13 +5,9 @@
"""Module containing utilities for apk packages."""
import re
-import xml.etree.ElementTree
-import zipfile
from devil import base_error
-from devil.android.ndk import abis
from devil.android.sdk import aapt
-from devil.utils import cmd_helper
_MANIFEST_ATTRIBUTE_RE = re.compile(
@@ -48,8 +44,9 @@ def ToHelper(path_or_helper):
# matches the height of the stack). Each line parsed (either an attribute or an
# element) is added to the node at the top of the stack (after the stack has
# been popped/pushed due to indentation).
-def _ParseManifestFromApk(apk):
- aapt_output = aapt.Dump('xmltree', apk.path, 'AndroidManifest.xml')
+def _ParseManifestFromApk(apk_path):
+ aapt_output = aapt.Dump('xmltree', apk_path, 'AndroidManifest.xml')
+
parsed_manifest = {}
node_stack = [parsed_manifest]
indent = ' '
@@ -98,8 +95,7 @@ def _ParseManifestFromApk(apk):
manifest_key = m.group(1)
if manifest_key in node:
raise base_error.BaseError(
- "A single attribute should have one key and one value: {}"
- .format(line))
+ "A single attribute should have one key and one value")
else:
node[manifest_key] = m.group(2) or m.group(3)
continue
@@ -107,47 +103,6 @@ def _ParseManifestFromApk(apk):
return parsed_manifest
-def _ParseManifestFromBundle(bundle):
- cmd = [bundle.path, 'dump-manifest']
- status, stdout, stderr = cmd_helper.GetCmdStatusOutputAndError(cmd)
- if status != 0:
- raise Exception('Failed running {} with output\n{}\n{}'.format(
- ' '.join(cmd), stdout, stderr))
- return ParseManifestFromXml(stdout)
-
-
-def ParseManifestFromXml(xml_str):
- """Parse an android bundle manifest.
-
- As ParseManifestFromAapt, but uses the xml output from bundletool. Each
- element is a dict, mapping attribute or children by name. Attributes map to
- a dict (as they are unique), children map to a list of dicts (as there may
- be multiple children with the same name).
-
- Args:
- xml_str (str) An xml string that is an android manifest.
-
- Returns:
- A dict holding the parsed manifest, as with ParseManifestFromAapt.
- """
- root = xml.etree.ElementTree.fromstring(xml_str)
- return {root.tag: [_ParseManifestXMLNode(root)]}
-
-
-def _ParseManifestXMLNode(node):
- out = {}
- for name, value in node.attrib.items():
- cleaned_name = name.replace(
- '{http://schemas.android.com/apk/res/android}',
- 'android:').replace(
- '{http://schemas.android.com/tools}',
- 'tools:')
- out[cleaned_name] = value
- for child in node:
- out.setdefault(child.tag, []).append(_ParseManifestXMLNode(child))
- return out
-
-
def _ParseNumericKey(obj, key, default=0):
val = obj.get(key)
if val is None:
@@ -196,10 +151,6 @@ class ApkHelper(object):
def path(self):
return self._apk_path
- @property
- def is_bundle(self):
- return self._apk_path.endswith('_bundle')
-
def GetActivityName(self):
"""Returns the name of the first launcher Activity in the apk."""
manifest_info = self._GetManifest()
@@ -281,73 +232,9 @@ class ApkHelper(object):
except KeyError:
return []
- def GetVersionCode(self):
- """Returns the versionCode as an integer, or None if not available."""
- manifest_info = self._GetManifest()
- try:
- version_code = manifest_info['manifest'][0]['android:versionCode']
- return int(version_code, 16)
- except KeyError:
- return None
-
- def GetVersionName(self):
- """Returns the versionName as a string."""
- manifest_info = self._GetManifest()
- try:
- version_name = manifest_info['manifest'][0]['android:versionName']
- return version_name
- except KeyError:
- return ''
-
- def GetMinSdkVersion(self):
- """Returns the minSdkVersion as a string, or None if not available.
-
- Note: this cannot always be cast to an integer."""
- manifest_info = self._GetManifest()
- try:
- uses_sdk = manifest_info['manifest'][0]['uses-sdk'][0]
- min_sdk_version = uses_sdk['android:minSdkVersion']
- try:
- # The common case is for this to be an integer. Convert to decimal
- # notation (rather than hexadecimal) for readability, but convert back
- # to a string for type consistency with the general case.
- return str(int(min_sdk_version, 16))
- except ValueError:
- # In general (ex. apps with minSdkVersion set to pre-release Android
- # versions), minSdkVersion can be a string (usually, the OS codename
- # letter). For simplicity, don't do any validation on the value.
- return min_sdk_version
- except KeyError:
- return None
-
- def GetTargetSdkVersion(self):
- """Returns the targetSdkVersion as a string, or None if not available.
-
- Note: this cannot always be cast to an integer."""
- manifest_info = self._GetManifest()
- try:
- uses_sdk = manifest_info['manifest'][0]['uses-sdk'][0]
- target_sdk_version = uses_sdk['android:targetSdkVersion']
- try:
- # The common case is for this to be an integer. Convert to decimal
- # notation (rather than hexadecimal) for readability, but convert back
- # to a string for type consistency with the general case.
- return str(int(target_sdk_version, 16))
- except ValueError:
- # In general (ex. apps targeting pre-release Android versions),
- # targetSdkVersion can be a string (usually, the OS codename letter).
- # For simplicity, don't do any validation on the value.
- return target_sdk_version
- except KeyError:
- return None
-
def _GetManifest(self):
if not self._manifest:
- app = ToHelper(self._apk_path)
- if app.is_bundle:
- self._manifest = _ParseManifestFromBundle(app)
- else:
- self._manifest = _ParseManifestFromApk(app)
+ self._manifest = _ParseManifestFromApk(self._apk_path)
return self._manifest
def _ResolveName(self, name):
@@ -355,30 +242,3 @@ class ApkHelper(object):
if '.' not in name:
return '%s.%s' % (self.GetPackageName(), name)
return name
-
- def _ListApkPaths(self):
- with zipfile.ZipFile(self._apk_path) as z:
- return z.namelist()
-
- def GetAbis(self):
- """Returns a list of ABIs in the apk (empty list if no native code)."""
- # Use lib/* to determine the compatible ABIs.
- libs = set()
- for path in self._ListApkPaths():
- path_tokens = path.split('/')
- if len(path_tokens) >= 2 and path_tokens[0] == 'lib':
- libs.add(path_tokens[1])
- lib_to_abi = {
- abis.ARM: [abis.ARM, abis.ARM_64],
- abis.ARM_64: [abis.ARM_64],
- abis.X86: [abis.X86, abis.X86_64],
- abis.X86_64: [abis.X86_64]
- }
- try:
- output = set()
- for lib in libs:
- for abi in lib_to_abi[lib]:
- output.add(abi)
- return sorted(output)
- except KeyError:
- raise base_error.BaseError('Unexpected ABI in lib/* folder.')
diff --git a/systrace/catapult/devil/devil/android/apk_helper_test.py b/systrace/catapult/devil/devil/android/apk_helper_test.py
index 3258bb0..12137db 100755
--- a/systrace/catapult/devil/devil/android/apk_helper_test.py
+++ b/systrace/catapult/devil/devil/android/apk_helper_test.py
@@ -3,30 +3,21 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import collections
-import os
import unittest
from devil import base_error
from devil import devil_env
from devil.android import apk_helper
-from devil.android.ndk import abis
from devil.utils import mock_calls
with devil_env.SysPath(devil_env.PYMOCK_PATH):
import mock # pylint: disable=import-error
-# pylint: disable=line-too-long
_MANIFEST_DUMP = """N: android=http://schemas.android.com/apk/res/android
E: manifest (line=1)
- A: android:versionCode(0x0101021b)=(type 0x10)0x166de1ea
- A: android:versionName(0x0101021c)="75.0.3763.0" (Raw: "75.0.3763.0")
A: package="org.chromium.abc" (Raw: "org.chromium.abc")
A: split="random_split" (Raw: "random_split")
- E: uses-sdk (line=2)
- A: android:minSdkVersion(0x0101020c)=(type 0x10)0x15
- A: android:targetSdkVersion(0x01010270)=(type 0x10)0x1c
E: uses-permission (line=2)
A: android:name(0x01010003)="android.permission.INTERNET" (Raw: "android.permission.INTERNET")
E: uses-permission (line=3)
@@ -120,14 +111,6 @@ _SINGLE_J4_INSTRUMENTATION_MANIFEST_DUMP = """N: android=http://schemas.android.
A: junit4=(type 0x12)0xffffffff (Raw: "true")
"""
-_TARGETING_PRE_RELEASE_Q_MANIFEST_DUMP = """N: android=http://schemas.android.com/apk/res/android
- E: manifest (line=1)
- A: package="org.chromium.xyz" (Raw: "org.chromium.xyz")
- E: uses-sdk (line=2)
- A: android:minSdkVersion(0x0101020c)="Q" (Raw: "Q")
- A: android:targetSdkVersion(0x01010270)="Q" (Raw: "Q")
-"""
-
_NO_NAMESPACE_MANIFEST_DUMP = """E: manifest (line=1)
A: package="org.chromium.xyz" (Raw: "org.chromium.xyz")
E: instrumentation (line=8)
@@ -135,7 +118,6 @@ _NO_NAMESPACE_MANIFEST_DUMP = """E: manifest (line=1)
A: http://schemas.android.com/apk/res/android:name(0x01010003)="org.chromium.RandomTestRunner" (Raw: "org.chromium.RandomTestRunner")
A: http://schemas.android.com/apk/res/android:targetPackage(0x01010021)="org.chromium.random_package" (Raw:"org.chromium.random_pacakge")
"""
-# pylint: enable=line-too-long
def _MockAaptDump(manifest_dump):
@@ -143,11 +125,6 @@ def _MockAaptDump(manifest_dump):
'devil.android.sdk.aapt.Dump',
mock.Mock(side_effect=None, return_value=manifest_dump.split('\n')))
-def _MockListApkPaths(files):
- return mock.patch(
- 'devil.android.apk_helper.ApkHelper._ListApkPaths',
- mock.Mock(side_effect=None, return_value=files))
-
class ApkHelperTest(mock_calls.TestCase):
def testGetInstrumentationName(self):
@@ -237,146 +214,12 @@ class ApkHelperTest(mock_calls.TestCase):
self.assertEquals([('name1', 'value1'), ('name2', 'value2')],
helper.GetAllMetadata())
- def testGetVersionCode(self):
- with _MockAaptDump(_MANIFEST_DUMP):
- helper = apk_helper.ApkHelper('')
- self.assertEquals(376300010, helper.GetVersionCode())
-
- def testGetVersionName(self):
- with _MockAaptDump(_MANIFEST_DUMP):
- helper = apk_helper.ApkHelper('')
- self.assertEquals('75.0.3763.0', helper.GetVersionName())
-
- def testGetMinSdkVersion_integerValue(self):
- with _MockAaptDump(_MANIFEST_DUMP):
- helper = apk_helper.ApkHelper('')
- self.assertEquals('21', helper.GetMinSdkVersion())
-
- def testGetMinSdkVersion_stringValue(self):
- with _MockAaptDump(_TARGETING_PRE_RELEASE_Q_MANIFEST_DUMP):
- helper = apk_helper.ApkHelper('')
- self.assertEquals('Q', helper.GetMinSdkVersion())
-
- def testGetTargetSdkVersion_integerValue(self):
- with _MockAaptDump(_MANIFEST_DUMP):
- helper = apk_helper.ApkHelper('')
- self.assertEquals('28', helper.GetTargetSdkVersion())
-
- def testGetTargetSdkVersion_stringValue(self):
- with _MockAaptDump(_TARGETING_PRE_RELEASE_Q_MANIFEST_DUMP):
- helper = apk_helper.ApkHelper('')
- self.assertEquals('Q', helper.GetTargetSdkVersion())
-
def testGetSingleInstrumentationName_strippedNamespaces(self):
with _MockAaptDump(_NO_NAMESPACE_MANIFEST_DUMP):
helper = apk_helper.ApkHelper('')
self.assertEquals('org.chromium.RandomTestRunner',
helper.GetInstrumentationName())
- def testGetArchitectures(self):
- AbiPair = collections.namedtuple('AbiPair', ['abi32bit', 'abi64bit'])
- for abi_pair in [AbiPair('lib/' + abis.ARM, 'lib/' + abis.ARM_64),
- AbiPair('lib/' + abis.X86, 'lib/' + abis.X86_64)]:
- with _MockListApkPaths([abi_pair.abi32bit]):
- helper = apk_helper.ApkHelper('')
- self.assertEquals(set([os.path.basename(abi_pair.abi32bit),
- os.path.basename(abi_pair.abi64bit)]),
- set(helper.GetAbis()))
- with _MockListApkPaths([abi_pair.abi32bit, abi_pair.abi64bit]):
- helper = apk_helper.ApkHelper('')
- self.assertEquals(set([os.path.basename(abi_pair.abi32bit),
- os.path.basename(abi_pair.abi64bit)]),
- set(helper.GetAbis()))
- with _MockListApkPaths([abi_pair.abi64bit]):
- helper = apk_helper.ApkHelper('')
- self.assertEquals(set([os.path.basename(abi_pair.abi64bit)]),
- set(helper.GetAbis()))
-
- def testParseXmlManifest(self):
- self.assertEquals({
- 'manifest': [
- {'android:compileSdkVersion': '28',
- 'android:versionCode': '2',
- 'uses-sdk': [
- {'android:minSdkVersion': '24',
- 'android:targetSdkVersion': '28'}],
- 'uses-permission': [
- {'android:name':
- 'android.permission.ACCESS_COARSE_LOCATION'},
- {'android:name':
- 'android.permission.ACCESS_NETWORK_STATE'}],
- 'application': [
- {'android:allowBackup': 'true',
- 'android:extractNativeLibs': 'false',
- 'android:fullBackupOnly': 'false',
- 'meta-data': [
- {'android:name': 'android.allow_multiple',
- 'android:value': 'true'},
- {'android:name': 'multiwindow',
- 'android:value': 'true'}],
- 'activity': [
- {'android:configChanges': '0x00001fb3',
- 'android:excludeFromRecents': 'true',
- 'android:name': 'ChromeLauncherActivity',
- 'intent-filter': [
- {'action': [
- {'android:name': 'dummy.action'}],
- 'category': [
- {'android:name': 'DAYDREAM'},
- {'android:name': 'CARDBOARD'}]}]},
- {'android:enabled': 'false',
- 'android:name': 'MediaLauncherActivity',
- 'intent-filter': [
- {'tools:ignore': 'AppLinkUrlError',
- 'action': [{'android:name': 'VIEW'}],
- 'category': [{'android:name': 'DEFAULT'}],
- 'data': [
- {'android:mimeType': 'audio/*'},
- {'android:mimeType': 'image/*'},
- {'android:mimeType': 'video/*'},
- {'android:scheme': 'file'},
- {'android:scheme': 'content'}]}]}]}]}]},
- apk_helper.ParseManifestFromXml("""
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:compileSdkVersion="28" android:versionCode="2">
- <uses-sdk android:minSdkVersion="24" android:targetSdkVersion="28"/>
- <uses-permission
- android:name="android.permission.ACCESS_COARSE_LOCATION"/>
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
- <application android:allowBackup="true"
- android:extractNativeLibs="false"
- android:fullBackupOnly="false">
- <meta-data android:name="android.allow_multiple"
- android:value="true"/>
- <meta-data android:name="multiwindow"
- android:value="true"/>
- <activity android:configChanges="0x00001fb3"
- android:excludeFromRecents="true"
- android:name="ChromeLauncherActivity">
- <intent-filter>
- <action android:name="dummy.action"/>
- <category android:name="DAYDREAM"/>
- <category android:name="CARDBOARD"/>
- </intent-filter>
- </activity>
- <activity android:enabled="false"
- android:name="MediaLauncherActivity">
- <intent-filter tools:ignore="AppLinkUrlError">
- <action android:name="VIEW"/>
-
- <category android:name="DEFAULT"/>
-
- <data android:mimeType="audio/*"/>
- <data android:mimeType="image/*"/>
- <data android:mimeType="video/*"/>
- <data android:scheme="file"/>
- <data android:scheme="content"/>
- </intent-filter>
- </activity>
- </application>
- </manifest>"""))
-
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/systrace/catapult/devil/devil/android/battery_utils.py b/systrace/catapult/devil/devil/android/battery_utils.py
index c41c19a..a8a08a9 100644
--- a/systrace/catapult/devil/devil/android/battery_utils.py
+++ b/systrace/catapult/devil/devil/android/battery_utils.py
@@ -11,7 +11,6 @@ import contextlib
import csv
import logging
-from devil.android import crash_handler
from devil.android import decorators
from devil.android import device_errors
from devil.android import device_utils
@@ -241,6 +240,44 @@ class BatteryUtils(object):
'Unable to find fuel gauge.')
@decorators.WithTimeoutAndRetriesFromInstance()
+ def GetNetworkData(self, package, timeout=None, retries=None):
+ """Get network data for specific package.
+
+ Args:
+ package: package name you want network data for.
+ timeout: timeout in seconds
+ retries: number of retries
+
+ Returns:
+ Tuple of (sent_data, recieved_data)
+ None if no network data found
+ """
+ # If device_utils clears cache, cache['uids'] doesn't exist
+ if 'uids' not in self._cache:
+ self._cache['uids'] = {}
+ if package not in self._cache['uids']:
+ self.GetPowerData()
+ if package not in self._cache['uids']:
+ logger.warning('No UID found for %s. Can\'t get network data.',
+ package)
+ return None
+
+ network_data_path = '/proc/uid_stat/%s/' % self._cache['uids'][package]
+ try:
+ send_data = int(self._device.ReadFile(network_data_path + 'tcp_snd'))
+ # If ReadFile throws exception, it means no network data usage file for
+ # package has been recorded. Return 0 sent and 0 received.
+ except device_errors.AdbShellCommandFailedError:
+ logger.warning('No sent data found for package %s', package)
+ send_data = 0
+ try:
+ recv_data = int(self._device.ReadFile(network_data_path + 'tcp_rcv'))
+ except device_errors.AdbShellCommandFailedError:
+ logger.warning('No received data found for package %s', package)
+ recv_data = 0
+ return (send_data, recv_data)
+
+ @decorators.WithTimeoutAndRetriesFromInstance()
def GetPowerData(self, timeout=None, retries=None):
"""Get power data for device.
@@ -337,12 +374,7 @@ class BatteryUtils(object):
Returns:
True if the device is charging, false otherwise.
"""
- # Wrapper function so that we can use `RetryOnSystemCrash`.
- def GetBatteryInfoHelper(device):
- return self.GetBatteryInfo()
-
- battery_info = crash_handler.RetryOnSystemCrash(
- GetBatteryInfoHelper, self._device)
+ battery_info = self.GetBatteryInfo()
for k in ('AC powered', 'USB powered', 'Wireless powered'):
if (k in battery_info and
battery_info[k].lower() in ('true', '1', 'yes')):
diff --git a/systrace/catapult/devil/devil/android/battery_utils_test.py b/systrace/catapult/devil/devil/android/battery_utils_test.py
index 07c7496..feccf79 100755
--- a/systrace/catapult/devil/devil/android/battery_utils_test.py
+++ b/systrace/catapult/devil/devil/android/battery_utils_test.py
@@ -401,6 +401,57 @@ class BatteryUtilsGetChargingTest(BatteryUtilsTest):
self.assertFalse(self.battery.GetCharging())
+class BatteryUtilsGetNetworkDataTest(BatteryUtilsTest):
+
+ def testGetNetworkData_noDataUsage(self):
+ with self.assertCalls(
+ (self.call.device.RunShellCommand(
+ ['dumpsys', 'batterystats', '-c'],
+ check_return=True, large_output=True),
+ _DUMPSYS_OUTPUT),
+ (self.call.device.ReadFile('/proc/uid_stat/1000/tcp_snd'),
+ self.ShellError()),
+ (self.call.device.ReadFile('/proc/uid_stat/1000/tcp_rcv'),
+ self.ShellError())):
+ self.assertEquals(self.battery.GetNetworkData('test_package1'), (0, 0))
+
+ def testGetNetworkData_badPackage(self):
+ with self.assertCall(
+ self.call.device.RunShellCommand(
+ ['dumpsys', 'batterystats', '-c'],
+ check_return=True, large_output=True),
+ _DUMPSYS_OUTPUT):
+ self.assertEqual(self.battery.GetNetworkData('asdf'), None)
+
+ def testGetNetworkData_packageNotCached(self):
+ with self.assertCalls(
+ (self.call.device.RunShellCommand(
+ ['dumpsys', 'batterystats', '-c'],
+ check_return=True, large_output=True),
+ _DUMPSYS_OUTPUT),
+ (self.call.device.ReadFile('/proc/uid_stat/1000/tcp_snd'), 1),
+ (self.call.device.ReadFile('/proc/uid_stat/1000/tcp_rcv'), 2)):
+ self.assertEqual(self.battery.GetNetworkData('test_package1'), (1, 2))
+
+ def testGetNetworkData_packageCached(self):
+ self.battery._cache['uids'] = {'test_package1': '1000'}
+ with self.assertCalls(
+ (self.call.device.ReadFile('/proc/uid_stat/1000/tcp_snd'), 1),
+ (self.call.device.ReadFile('/proc/uid_stat/1000/tcp_rcv'), 2)):
+ self.assertEqual(self.battery.GetNetworkData('test_package1'), (1, 2))
+
+ def testGetNetworkData_clearedCache(self):
+ with self.assertCalls(
+ (self.call.device.RunShellCommand(
+ ['dumpsys', 'batterystats', '-c'],
+ check_return=True, large_output=True),
+ _DUMPSYS_OUTPUT),
+ (self.call.device.ReadFile('/proc/uid_stat/1000/tcp_snd'), 1),
+ (self.call.device.ReadFile('/proc/uid_stat/1000/tcp_rcv'), 2)):
+ self.battery._cache.clear()
+ self.assertEqual(self.battery.GetNetworkData('test_package1'), (1, 2))
+
+
class BatteryUtilsLetBatteryCoolToTemperatureTest(BatteryUtilsTest):
@mock.patch('time.sleep', mock.Mock())
diff --git a/systrace/catapult/devil/devil/android/cpu_temperature.py b/systrace/catapult/devil/devil/android/cpu_temperature.py
deleted file mode 100644
index 58ce87a..0000000
--- a/systrace/catapult/devil/devil/android/cpu_temperature.py
+++ /dev/null
@@ -1,154 +0,0 @@
-# Copyright 2019 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.
-"""Provides device interactions for CPU temperature monitoring."""
-# pylint: disable=unused-argument
-
-import logging
-
-from devil.android import device_utils
-from devil.android.perf import perf_control
-from devil.utils import timeout_retry
-
-logger = logging.getLogger(__name__)
-
-# NB: when adding devices to this structure, be aware of the impact it may
-# have on the chromium.perf waterfall, as it may increase testing time.
-# Please contact a person responsible for the waterfall to see if the
-# device you're adding is currently being tested.
-_DEVICE_THERMAL_INFORMATION = {
- # Pixel 3
- 'blueline': {
- 'cpu_temps': {
- # See /sys/class/thermal/thermal_zone<number>/type for description
- # Types:
- # cpu0: cpu0-silver-step
- # cpu1: cpu1-silver-step
- # cpu2: cpu2-silver-step
- # cpu3: cpu3-silver-step
- # cpu4: cpu0-gold-step
- # cpu5: cpu1-gold-step
- # cpu6: cpu2-gold-step
- # cpu7: cpu3-gold-step
- 'cpu0': '/sys/class/thermal/thermal_zone11/temp',
- 'cpu1': '/sys/class/thermal/thermal_zone12/temp',
- 'cpu2': '/sys/class/thermal/thermal_zone13/temp',
- 'cpu3': '/sys/class/thermal/thermal_zone14/temp',
- 'cpu4': '/sys/class/thermal/thermal_zone15/temp',
- 'cpu5': '/sys/class/thermal/thermal_zone16/temp',
- 'cpu6': '/sys/class/thermal/thermal_zone17/temp',
- 'cpu7': '/sys/class/thermal/thermal_zone18/temp'
- },
- # Different device sensors use different multipliers
- # e.g. Pixel 3 35 degrees c is 35000
- 'temp_multiplier': 1000
- },
- # Pixel
- 'sailfish': {
- 'cpu_temps': {
- # The following thermal zones tend to produce the most accurate
- # readings
- # Types:
- # cpu0: tsens_tz_sensor0
- # cpu1: tsens_tz_sensor1
- # cpu2: tsens_tz_sensor2
- # cpu3: tsens_tz_sensor3
- 'cpu0': '/sys/class/thermal/thermal_zone1/temp',
- 'cpu1': '/sys/class/thermal/thermal_zone2/temp',
- 'cpu2': '/sys/class/thermal/thermal_zone3/temp',
- 'cpu3': '/sys/class/thermal/thermal_zone4/temp'
- },
- 'temp_multiplier': 10
- }
-}
-
-
-class CpuTemperature(object):
-
- def __init__(self, device):
- """CpuTemperature constructor.
-
- Args:
- device: A DeviceUtils instance.
- Raises:
- TypeError: If it is not passed a DeviceUtils instance.
- """
- if not isinstance(device, device_utils.DeviceUtils):
- raise TypeError('Must be initialized with DeviceUtils object.')
- self._device = device
- self._perf_control = perf_control.PerfControl(self._device)
- self._device_info = None
-
- def InitThermalDeviceInformation(self):
- """Init the current devices thermal information.
- """
- self._device_info = _DEVICE_THERMAL_INFORMATION.get(
- self._device.build_product)
-
- def IsSupported(self):
- """Check if the current device is supported.
-
- Returns:
- True if the device is in _DEVICE_THERMAL_INFORMATION and the temp
- files exist. False otherwise.
- """
- # Init device info if it hasnt been manually initialised already
- if self._device_info is None:
- self.InitThermalDeviceInformation()
-
- if self._device_info is not None:
- return all(
- self._device.FileExists(f)
- for f in self._device_info['cpu_temps'].values())
- return False
-
- def LetCpuCoolToTemperature(self, target_temp, wait_period=30):
- """Lets device sit to give CPU time to cool down.
-
- Implements a similar mechanism to
- battery_utils.LetBatteryCoolToTemperature
-
- Args:
- temp: A float containing the maximum temperature to allow
- in degrees c.
- wait_period: An integer indicating time in seconds to wait
- between checking.
- """
- target_temp = int(target_temp * self._device_info['temp_multiplier'])
-
- def cool_cpu():
- # Get the temperatures
- cpu_temp_paths = self._device_info['cpu_temps']
- temps = []
- for temp_path in cpu_temp_paths.values():
- temp_return = self._device.ReadFile(temp_path)
- # Output is an array of strings, only need the first line.
- temps.append(int(temp_return))
-
- if not temps:
- logger.warning('Unable to read temperature files provided.')
- return True
-
- logger.info('Current CPU temperatures: %s', str(temps)[1:-1])
-
- return all(t <= target_temp for t in temps)
-
- logger.info('Waiting for the CPU to cool down to %s',
- target_temp / self._device_info['temp_multiplier'])
-
- # Set the governor to powersave to aid the cooling down of the CPU
- self._perf_control.SetScalingGovernor('powersave')
-
- # Retry 3 times, each time waiting 30 seconds.
- # This negates most (if not all) of the noise in recorded results without
- # taking too long
- timeout_retry.WaitFor(cool_cpu, wait_period=wait_period, max_tries=3)
-
- # Set the performance mode
- self._perf_control.SetHighPerfMode()
-
- def GetDeviceForTesting(self):
- return self._device
-
- def GetDeviceInfoForTesting(self):
- return self._device_info
diff --git a/systrace/catapult/devil/devil/android/cpu_temperature_test.py b/systrace/catapult/devil/devil/android/cpu_temperature_test.py
deleted file mode 100644
index f0f99de..0000000
--- a/systrace/catapult/devil/devil/android/cpu_temperature_test.py
+++ /dev/null
@@ -1,132 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2019 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.
-"""
-Unit tests for the contents of cpu_temperature.py
-"""
-
-# pylint: disable=unused-argument
-
-import logging
-import unittest
-
-from devil import devil_env
-from devil.android import cpu_temperature
-from devil.android import device_utils
-from devil.utils import mock_calls
-from devil.android.sdk import adb_wrapper
-
-with devil_env.SysPath(devil_env.PYMOCK_PATH):
- import mock # pylint: disable=import-error
-
-
-class CpuTemperatureTest(mock_calls.TestCase):
-
- @mock.patch('devil.android.perf.perf_control.PerfControl', mock.Mock())
- def setUp(self):
- # Mock the device
- self.mock_device = mock.Mock(spec=device_utils.DeviceUtils)
- self.mock_device.build_product = 'blueline'
- self.mock_device.adb = mock.Mock(spec=adb_wrapper.AdbWrapper)
- self.mock_device.FileExists.return_value = True
-
- self.cpu_temp = cpu_temperature.CpuTemperature(self.mock_device)
- self.cpu_temp.InitThermalDeviceInformation()
-
-
-class CpuTemperatureInitTest(unittest.TestCase):
-
- @mock.patch('devil.android.perf.perf_control.PerfControl', mock.Mock())
- def testInitWithDeviceUtil(self):
- d = mock.Mock(spec=device_utils.DeviceUtils)
- d.build_product = 'blueline'
- c = cpu_temperature.CpuTemperature(d)
- self.assertEqual(d, c.GetDeviceForTesting())
-
- def testInitWithMissing_fails(self):
- with self.assertRaises(TypeError):
- cpu_temperature.CpuTemperature(None)
- with self.assertRaises(TypeError):
- cpu_temperature.CpuTemperature('')
-
-
-class CpuTemperatureGetThermalDeviceInformationTest(CpuTemperatureTest):
-
- @mock.patch('devil.android.perf.perf_control.PerfControl', mock.Mock())
- def testGetThermalDeviceInformation_noneWhenIncorrectLabel(self):
- invalid_device = mock.Mock(spec=device_utils.DeviceUtils)
- invalid_device.build_product = 'invalid_name'
- c = cpu_temperature.CpuTemperature(invalid_device)
- c.InitThermalDeviceInformation()
- self.assertEqual(c.GetDeviceInfoForTesting(), None)
-
- def testGetThermalDeviceInformation_getsCorrectInformation(self):
- correct_information = {
- 'cpu0': '/sys/class/thermal/thermal_zone11/temp',
- 'cpu1': '/sys/class/thermal/thermal_zone12/temp',
- 'cpu2': '/sys/class/thermal/thermal_zone13/temp',
- 'cpu3': '/sys/class/thermal/thermal_zone14/temp',
- 'cpu4': '/sys/class/thermal/thermal_zone15/temp',
- 'cpu5': '/sys/class/thermal/thermal_zone16/temp',
- 'cpu6': '/sys/class/thermal/thermal_zone17/temp',
- 'cpu7': '/sys/class/thermal/thermal_zone18/temp'
- }
- self.assertEqual(
- cmp(correct_information,
- self.cpu_temp.GetDeviceInfoForTesting().get('cpu_temps')), 0)
-
-
-class CpuTemperatureIsSupportedTest(CpuTemperatureTest):
-
- @mock.patch('devil.android.perf.perf_control.PerfControl', mock.Mock())
- def testIsSupported_returnsTrue(self):
- d = mock.Mock(spec=device_utils.DeviceUtils)
- d.build_product = 'blueline'
- d.FileExists.return_value = True
- c = cpu_temperature.CpuTemperature(d)
- self.assertTrue(c.IsSupported())
-
- @mock.patch('devil.android.perf.perf_control.PerfControl', mock.Mock())
- def testIsSupported_returnsFalse(self):
- d = mock.Mock(spec=device_utils.DeviceUtils)
- d.build_product = 'blueline'
- d.FileExists.return_value = False
- c = cpu_temperature.CpuTemperature(d)
- self.assertFalse(c.IsSupported())
-
-
-class CpuTemperatureLetCpuCoolToTemperatureTest(CpuTemperatureTest):
- # Return values for the mock side effect
- cooling_down0 = ([45000 for _ in range(8)] + [43000 for _ in range(8)] +
- [41000 for _ in range(8)])
-
- @mock.patch('time.sleep', mock.Mock())
- def testLetBatteryCoolToTemperature_coolWithin24Calls(self):
- self.mock_device.ReadFile = mock.Mock(side_effect=self.cooling_down0)
- self.cpu_temp.LetCpuCoolToTemperature(42)
- self.mock_device.ReadFile.assert_called()
- self.assertEquals(self.mock_device.ReadFile.call_count, 24)
-
- cooling_down1 = [45000 for _ in range(8)] + [41000 for _ in range(16)]
-
- @mock.patch('time.sleep', mock.Mock())
- def testLetBatteryCoolToTemperature_coolWithin16Calls(self):
- self.mock_device.ReadFile = mock.Mock(side_effect=self.cooling_down1)
- self.cpu_temp.LetCpuCoolToTemperature(42)
- self.mock_device.ReadFile.assert_called()
- self.assertEquals(self.mock_device.ReadFile.call_count, 16)
-
- constant_temp = [45000 for _ in range(40)]
-
- @mock.patch('time.sleep', mock.Mock())
- def testLetBatteryCoolToTemperature_timeoutAfterThree(self):
- self.mock_device.ReadFile = mock.Mock(side_effect=self.constant_temp)
- self.cpu_temp.LetCpuCoolToTemperature(42)
- self.mock_device.ReadFile.assert_called()
- self.assertEquals(self.mock_device.ReadFile.call_count, 24)
-
-
-if __name__ == '__main__':
- logging.getLogger().setLevel(logging.DEBUG)
- unittest.main(verbosity=2)
diff --git a/systrace/catapult/devil/devil/android/crash_handler.py b/systrace/catapult/devil/devil/android/crash_handler.py
index 028e787..7cfabcf 100644
--- a/systrace/catapult/devil/devil/android/crash_handler.py
+++ b/systrace/catapult/devil/devil/android/crash_handler.py
@@ -37,9 +37,6 @@ def RetryOnSystemCrash(f, device, retries=3):
raise
try:
logger.warning('Device is unreachable. Waiting for recovery...')
- # Treat the device being unreachable as an unexpected reboot and clear
- # any cached state.
- device.ClearCache()
device.WaitUntilFullyBooted()
except base_error.BaseError:
logger.exception('Device never recovered. X(')
diff --git a/systrace/catapult/devil/devil/android/decorators.py b/systrace/catapult/devil/devil/android/decorators.py
index 93e1054..3844b49 100644
--- a/systrace/catapult/devil/devil/android/decorators.py
+++ b/systrace/catapult/devil/devil/android/decorators.py
@@ -59,7 +59,7 @@ def _TimeoutRetryWrapper(
raise device_errors.CommandTimeoutError(str(e)), None, (
sys.exc_info()[2])
except cmd_helper.TimeoutError as e:
- raise device_errors.CommandTimeoutError(str(e), output=e.output), None, (
+ raise device_errors.CommandTimeoutError(str(e)), None, (
sys.exc_info()[2])
return timeout_retry_wrapper
diff --git a/systrace/catapult/devil/devil/android/device_errors.py b/systrace/catapult/devil/devil/android/device_errors.py
index e6893a4..57f3615 100644
--- a/systrace/catapult/devil/devil/android/device_errors.py
+++ b/systrace/catapult/devil/devil/android/device_errors.py
@@ -55,15 +55,15 @@ class _BaseCommandFailedError(CommandFailedError):
self.status = status
if not message:
adb_cmd = ' '.join(cmd_helper.SingleQuote(arg) for arg in self.args)
- segments = ['adb %s: failed ' % adb_cmd]
+ message = ['adb %s: failed ' % adb_cmd]
if status:
- segments.append('with exit status %s ' % self.status)
+ message.append('with exit status %s ' % self.status)
if output:
- segments.append('and output:\n')
- segments.extend('- %s\n' % line for line in output.splitlines())
+ message.append('and output:\n')
+ message.extend('- %s\n' % line for line in output.splitlines())
else:
- segments.append('and no output.')
- message = ''.join(segments)
+ message.append('and no output.')
+ message = ''.join(message)
super(_BaseCommandFailedError, self).__init__(message, device_serial)
def __eq__(self, other):
@@ -79,7 +79,8 @@ class _BaseCommandFailedError(CommandFailedError):
"""Support pickling."""
result = [None, None, None, None, None]
super_result = super(_BaseCommandFailedError, self).__reduce__()
- result[:len(super_result)] = super_result
+ for i in range(len(super_result)):
+ result[i] = super_result[i]
# Update the args used to reconstruct this exception.
result[1] = (
@@ -119,19 +120,19 @@ class AdbShellCommandFailedError(AdbCommandFailedError):
def __init__(self, command, output, status, device_serial=None):
self.command = command
- segments = ['shell command run via adb failed on the device:\n',
+ message = ['shell command run via adb failed on the device:\n',
' command: %s\n' % command]
- segments.append(' exit status: %s\n' % status)
+ message.append(' exit status: %s\n' % status)
if output:
- segments.append(' output:\n')
+ message.append(' output:\n')
if isinstance(output, basestring):
output_lines = output.splitlines()
else:
output_lines = output
- segments.extend(' - %s\n' % line for line in output_lines)
+ message.extend(' - %s\n' % line for line in output_lines)
else:
- segments.append(" output: ''\n")
- message = ''.join(segments)
+ message.append(" output: ''\n")
+ message = ''.join(message)
super(AdbShellCommandFailedError, self).__init__(
['shell', command], output, status, device_serial, message)
@@ -139,7 +140,8 @@ class AdbShellCommandFailedError(AdbCommandFailedError):
"""Support pickling."""
result = [None, None, None, None, None]
super_result = super(AdbShellCommandFailedError, self).__reduce__()
- result[:len(super_result)] = super_result
+ for i in range(len(super_result)):
+ result[i] = super_result[i]
# Update the args used to reconstruct this exception.
result[1] = (self.command, self.output, self.status, self.device_serial)
@@ -148,9 +150,7 @@ class AdbShellCommandFailedError(AdbCommandFailedError):
class CommandTimeoutError(base_error.BaseError):
"""Exception for command timeouts."""
- def __init__(self, message, is_infra_error=False, output=None):
- super(CommandTimeoutError, self).__init__(message, is_infra_error)
- self.output = output
+ pass
class DeviceUnreachableError(base_error.BaseError):
diff --git a/systrace/catapult/devil/devil/android/device_utils.py b/systrace/catapult/devil/devil/android/device_utils.py
index 6182a52..5a3db41 100644
--- a/systrace/catapult/devil/devil/android/device_utils.py
+++ b/systrace/catapult/devil/devil/android/device_utils.py
@@ -10,11 +10,9 @@ Eventually, this will be based on adb_wrapper.
import calendar
import collections
-import contextlib
import fnmatch
import json
import logging
-import math
import os
import posixpath
import pprint
@@ -22,7 +20,6 @@ import random
import re
import shutil
import stat
-import sys
import tempfile
import time
import threading
@@ -52,12 +49,6 @@ from devil.utils import zip_utils
from py_utils import tempfile_ext
-try:
- from devil.utils import reset_usb
-except ImportError:
- # Fail silently if we can't import reset_usb. We're likely on windows.
- reset_usb = None
-
logger = logging.getLogger(__name__)
_DEFAULT_TIMEOUT = 30
@@ -68,29 +59,6 @@ _DEFAULT_RETRIES = 3
# the timeout_retry decorators.
DEFAULT = object()
-# A sentinel object to require that calls to RunShellCommand force running the
-# command with su even if the device has been rooted. To use, pass into the
-# as_root param.
-_FORCE_SU = object()
-
-_RECURSIVE_DIRECTORY_LIST_SCRIPT = """
- function list_subdirs() {
- for f in "$1"/* ;
- do
- if [ -d "$f" ] ;
- then
- if [ "$f" == "." ] || [ "$f" == ".." ] ;
- then
- continue ;
- fi ;
- echo "$f" ;
- list_subdirs "$f" ;
- fi ;
- done ;
- } ;
- list_subdirs %s
-"""
-
_RESTART_ADBD_SCRIPT = """
trap '' HUP
trap '' TERM
@@ -120,7 +88,6 @@ _PERMISSIONS_BLACKLIST_RE = re.compile('|'.join(fnmatch.translate(p) for p in [
'android.permission.DISABLE_KEYGUARD',
'android.permission.DOWNLOAD_WITHOUT_NOTIFICATION',
'android.permission.EXPAND_STATUS_BAR',
- 'android.permission.FOREGROUND_SERVICE',
'android.permission.GET_PACKAGE_SIZE',
'android.permission.INSTALL_SHORTCUT',
'android.permission.INJECT_EVENTS',
@@ -218,14 +185,8 @@ _SPECIAL_ROOT_DEVICE_LIST = [
'marlin', # Pixel XL
'sailfish', # Pixel
'taimen', # Pixel 2 XL
- 'vega', # Lenovo Mirage Solo
'walleye', # Pixel 2
- 'crosshatch', # Pixel 3 XL
- 'blueline', # Pixel 3
]
-_SPECIAL_ROOT_DEVICE_LIST += ['aosp_%s' % _d for _d in
- _SPECIAL_ROOT_DEVICE_LIST]
-
_IMEI_RE = re.compile(r' Device ID = (.+)$')
# The following regex is used to match result parcels like:
"""
@@ -239,25 +200,6 @@ _PARCEL_RESULT_RE = re.compile(
_EBUSY_RE = re.compile(
r'mkdir failed for ([^,]*), Device or resource busy')
-# http://bit.ly/2WLZhUF added a timeout to adb wait-for-device. We sometimes
-# want to wait longer than the implicit call within adb root allows.
-_WAIT_FOR_DEVICE_TIMEOUT_STR = 'timeout expired while waiting for device'
-
-_WEBVIEW_SYSUPDATE_CURRENT_PKG_RE = re.compile(
- r'Current WebView package.*:.*\(([a-z.]*),')
-_WEBVIEW_SYSUPDATE_NULL_PKG_RE = re.compile(
- r'Current WebView package is null')
-_WEBVIEW_SYSUPDATE_FALLBACK_LOGIC_RE = re.compile(
- r'Fallback logic enabled: (true|false)')
-_WEBVIEW_SYSUPDATE_PACKAGE_INSTALLED_RE = re.compile(
- r'(?:Valid|Invalid) package\s+(\S+)\s+\(.*\),?\s+(.*)$')
-_WEBVIEW_SYSUPDATE_PACKAGE_NOT_INSTALLED_RE = re.compile(
- r'(\S+)\s+(is NOT installed\.)')
-_WEBVIEW_SYSUPDATE_MIN_VERSION_CODE = re.compile(
- r'Minimum WebView version code: (\d+)')
-
-_GOOGLE_FEATURES_RE = re.compile(r'^\s*com\.google\.')
-
PS_COLUMNS = ('name', 'pid', 'ppid')
ProcessInfo = collections.namedtuple('ProcessInfo', PS_COLUMNS)
@@ -401,7 +343,7 @@ class DeviceUtils(object):
assert hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR)
assert hasattr(self, decorators.DEFAULT_RETRIES_ATTR)
- self.ClearCache()
+ self._ClearCache()
@property
def serial(self):
@@ -459,20 +401,6 @@ class DeviceUtils(object):
def HasRoot(self, timeout=None, retries=None):
"""Checks whether or not adbd has root privileges.
- A device is considered to have root if all commands are implicitly run
- with elevated privileges, i.e. without having to use "su" to run them.
-
- Note that some devices do not allow this implicit privilige elevation,
- but _can_ run commands as root just fine when done explicitly with "su".
- To check if your device can run commands with elevated privileges at all
- use:
-
- device.HasRoot() or device.NeedsSU()
-
- Luckily, for the most part you don't need to worry about this and using
- RunShellCommand(cmd, as_root=True) will figure out for you the right
- command incantation to run with elevated privileges.
-
Args:
timeout: timeout in seconds
retries: number of retries
@@ -485,10 +413,6 @@ class DeviceUtils(object):
DeviceUnreachableError on missing device.
"""
try:
- if self.build_type == 'eng':
- # 'eng' builds have root enabled by default and the adb session cannot
- # be unrooted.
- return True
if self.product_name in _SPECIAL_ROOT_DEVICE_LIST:
return self.GetProp('service.adb.root') == '1'
self.RunShellCommand(['ls', '/root'], check_return=True)
@@ -552,22 +476,17 @@ class DeviceUtils(object):
try:
self.adb.Root()
- except device_errors.AdbCommandFailedError as e:
+ except device_errors.AdbCommandFailedError:
if self.IsUserBuild():
raise device_errors.CommandFailedError(
'Unable to root device with user build.', str(self))
- elif e.output and _WAIT_FOR_DEVICE_TIMEOUT_STR in e.output:
- # adb 1.0.41 added a call to wait-for-device *inside* root
- # with a timeout that can be too short in some cases.
- # If we hit that timeout, ignore it & do our own wait below.
- pass
else:
raise # Failed probably due to some other reason.
def device_online_with_root():
try:
self.adb.WaitForDevice()
- return self.HasRoot()
+ return self.GetProp('service.adb.root', cache=False) == '1'
except (device_errors.AdbCommandFailedError,
device_errors.DeviceUnreachableError):
return False
@@ -730,22 +649,6 @@ class DeviceUtils(object):
'Version name for %s not found on dumpsys output' % package, str(self))
@decorators.WithTimeoutAndRetriesFromInstance()
- def GetPackageArchitecture(self, package, timeout=None, retries=None):
- """Get the architecture of a package installed on the device.
-
- Args:
- package: Name of the package.
-
- Returns:
- A string with the architecture, or None if the package is missing.
- """
- lines = self._GetDumpsysOutput(['package', package], 'primaryCpuAbi')
- if lines:
- _, _, package_arch = lines[-1].partition('=')
- return package_arch.strip()
- return None
-
- @decorators.WithTimeoutAndRetriesFromInstance()
def GetApplicationDataDirectory(self, package, timeout=None, retries=None):
"""Get the data directory on the device for the given package.
@@ -767,31 +670,6 @@ class DeviceUtils(object):
raise device_errors.CommandFailedError(
'Could not find data directory for %s', package)
- @decorators.WithTimeoutAndRetriesFromInstance()
- def GetSecurityContextForPackage(self, package, encrypted=False, timeout=None,
- retries=None):
- """Gets the SELinux security context for the given package.
-
- Args:
- package: Name of the package.
- encrypted: Whether to check in the encrypted data directory
- (/data/user_de/0/) or the unencrypted data directory (/data/data/).
-
- Returns:
- The package's security context as a string, or None if not found.
- """
- directory = '/data/user_de/0/' if encrypted else '/data/data/'
- for line in self.RunShellCommand(['ls', '-Z', directory],
- as_root=True, check_return=True):
- split_line = line.split()
- # ls -Z output differs between Android versions, but the package is
- # always last and the context always starts with "u:object"
- if split_line[-1] == package:
- for column in split_line:
- if column.startswith('u:object'):
- return column
- return None
-
def TakeBugReport(self, path, timeout=60*5, retries=None):
"""Takes a bug report and dumps it to the specified path.
@@ -882,35 +760,29 @@ class DeviceUtils(object):
return not self.IsOnline()
self.adb.Reboot()
- self.ClearCache()
+ self._ClearCache()
timeout_retry.WaitFor(device_offline, wait_period=1)
if block:
self.WaitUntilFullyBooted(wifi=wifi)
- INSTALL_DEFAULT_TIMEOUT = 8 * _DEFAULT_TIMEOUT
+ INSTALL_DEFAULT_TIMEOUT = 4 * _DEFAULT_TIMEOUT
@decorators.WithTimeoutAndRetriesFromInstance(
min_default_timeout=INSTALL_DEFAULT_TIMEOUT)
def Install(self, apk, allow_downgrade=False, reinstall=False,
- permissions=None, timeout=None, retries=None, modules=None):
- """Install an APK or app bundle.
+ permissions=None, timeout=None, retries=None):
+ """Install an APK.
- Noop if an identical APK is already installed. If installing a bundle, the
- bundletools helper script (bin/*_bundle) should be used rather than the .aab
- file.
+ Noop if an identical APK is already installed.
Args:
- apk: An ApkHelper instance or string containing the path to the APK or
- bundle.
+ apk: An ApkHelper instance or string containing the path to the APK.
allow_downgrade: A boolean indicating if we should allow downgrades.
reinstall: A boolean indicating if we should keep any existing app data.
- Ignored if |apk| is a bundle.
permissions: Set of permissions to set. If not set, finds permissions with
apk helper. To set no permissions, pass [].
timeout: timeout in seconds
retries: number of retries
- modules: An iterable containing specific bundle modules to install.
- Error if set and |apk| points to an APK instead of a bundle.
Raises:
CommandFailedError if the installation fails.
@@ -918,8 +790,7 @@ class DeviceUtils(object):
DeviceUnreachableError on missing device.
"""
self._InstallInternal(apk, None, allow_downgrade=allow_downgrade,
- reinstall=reinstall, permissions=permissions,
- modules=modules)
+ reinstall=reinstall, permissions=permissions)
@decorators.WithTimeoutAndRetriesFromInstance(
min_default_timeout=INSTALL_DEFAULT_TIMEOUT)
@@ -955,29 +826,12 @@ class DeviceUtils(object):
def _InstallInternal(self, base_apk, split_apks, allow_downgrade=False,
reinstall=False, allow_cached_props=False,
- permissions=None, modules=None):
- base_apk = apk_helper.ToHelper(base_apk)
- if base_apk.is_bundle:
- if split_apks:
- raise device_errors.CommandFailedError(
- 'Attempted to install a bundle {} while specifying split apks'
- .format(base_apk))
- if allow_downgrade:
- logging.warning('Installation of a bundle requested with '
- 'allow_downgrade=False. This is not possible with '
- 'bundletools, no downgrading is possible. This '
- 'flag will be ignored and installation will proceed.')
- # |allow_cached_props| is unused and ignored for bundles.
- self._InstallBundleInternal(base_apk, permissions, modules)
- return
-
- if modules:
- raise device_errors.CommandFailedError(
- 'Attempted to specify modules to install when providing an APK')
-
+ permissions=None):
if split_apks:
self._CheckSdkLevel(version_codes.LOLLIPOP)
+ base_apk = apk_helper.ToHelper(base_apk)
+
all_apks = [base_apk.path]
if split_apks:
all_apks += split_select.SelectSplits(
@@ -1014,11 +868,9 @@ class DeviceUtils(object):
logger.warning('Error calculating md5: %s', e)
apks_to_install, host_checksums = all_apks, None
if apks_to_install and not reinstall:
+ self.Uninstall(package_name)
apks_to_install = all_apks
- if device_apk_paths and apks_to_install and not reinstall:
- self.Uninstall(package_name)
-
if apks_to_install:
# Assume that we won't know the resulting device state.
self._cache['package_apk_paths'].pop(package_name, 0)
@@ -1044,20 +896,6 @@ class DeviceUtils(object):
if host_checksums is not None:
self._cache['package_apk_checksums'][package_name] = host_checksums
- def _InstallBundleInternal(self, bundle, permissions, modules):
- cmd = [bundle.path, 'install', '--device', self.serial]
- if modules:
- for m in modules:
- cmd.extend(['-m', m])
- status = cmd_helper.RunCmd(cmd)
- if status != 0:
- raise device_errors.CommandFailedError('Cound not install {}'.format(
- bundle.path))
- if (permissions is None
- and self.build_version_sdk >= version_codes.MARSHMALLOW):
- permissions = bundle.GetPermissions()
- self.GrantPermissions(bundle.GetPackageName(), permissions)
-
@decorators.WithTimeoutAndRetriesFromInstance()
def Uninstall(self, package_name, keep_data=False, timeout=None,
retries=None):
@@ -1079,11 +917,15 @@ class DeviceUtils(object):
installed = self._GetApplicationPathsInternal(package_name)
if not installed:
return
- # cached package paths are indeterminate due to system apps taking over
- # user apps after uninstall, so clear it
- self._cache['package_apk_paths'].pop(package_name, 0)
- self._cache['package_apk_checksums'].pop(package_name, 0)
- self.adb.Uninstall(package_name, keep_data)
+ try:
+ self.adb.Uninstall(package_name, keep_data)
+ self._cache['package_apk_paths'][package_name] = []
+ self._cache['package_apk_checksums'][package_name] = set()
+ except:
+ # Clear cache since we can't be sure of the state.
+ self._cache['package_apk_paths'].pop(package_name, 0)
+ self._cache['package_apk_checksums'].pop(package_name, 0)
+ raise
def _CheckSdkLevel(self, required_sdk_level):
"""Raises an exception if the device does not have the required SDK level.
@@ -1188,16 +1030,11 @@ class DeviceUtils(object):
def handle_large_output(cmd, large_output_mode):
if large_output_mode:
with device_temp_file.DeviceTempFile(self.adb) as large_output_file:
- large_output_cmd = '( %s )>%s 2>&1' % (cmd, large_output_file.name)
+ cmd = '( %s )>%s 2>&1' % (cmd, large_output_file.name)
logger.debug('Large output mode enabled. Will write output to '
'device and read results from file.')
- try:
- handle_large_command(large_output_cmd)
- return self.ReadFile(large_output_file.name, force_pull=True)
- except device_errors.AdbShellCommandFailedError as exc:
- output = self.ReadFile(large_output_file.name, force_pull=True)
- raise device_errors.AdbShellCommandFailedError(
- cmd, output, exc.status, exc.device_serial)
+ handle_large_command(cmd)
+ return self.ReadFile(large_output_file.name, force_pull=True)
else:
try:
return handle_large_command(cmd)
@@ -1227,7 +1064,7 @@ class DeviceUtils(object):
if run_as:
cmd = 'run-as %s sh -c %s' % (cmd_helper.SingleQuote(run_as),
cmd_helper.SingleQuote(cmd))
- if (as_root is _FORCE_SU) or (as_root and self.NeedsSU()):
+ if as_root and self.NeedsSU():
# "su -c sh -c" allows using shell features in |cmd|
cmd = self._Su('sh -c %s' % cmd_helper.SingleQuote(cmd))
@@ -1365,33 +1202,6 @@ class DeviceUtils(object):
raise device_errors.CommandFailedError(line, str(self))
@decorators.WithTimeoutAndRetriesFromInstance()
- def StartService(self, intent_obj, user_id=None, timeout=None, retries=None):
- """Start a service on the device.
-
- Args:
- intent_obj: An Intent object to send describing the service to start.
- user_id: A specific user to start the service as, defaults to current.
- timeout: Timeout in seconds.
- retries: Number of retries
-
- Raises:
- CommandFailedError if the service could not be started.
- CommandTimeoutError on timeout.
- DeviceUnreachableError on missing device.
- """
- # For whatever reason, startservice was changed to start-service on O and
- # above.
- cmd = ['am', 'startservice']
- if self.build_version_sdk >= version_codes.OREO:
- cmd[1] = 'start-service'
- if user_id:
- cmd.extend(['--user', str(user_id)])
- cmd.extend(intent_obj.am_args)
- for line in self.RunShellCommand(cmd, check_return=True):
- if line.startswith('Error:'):
- raise device_errors.CommandFailedError(line, str(self))
-
- @decorators.WithTimeoutAndRetriesFromInstance()
def StartInstrumentation(self, component, finish=True, raw=False,
extras=None, timeout=None, retries=None):
if extras is None:
@@ -1573,7 +1383,7 @@ class DeviceUtils(object):
missing_dirs.add(posixpath.dirname(d))
if delete_device_stale and all_stale_files:
- self.RemovePath(all_stale_files, force=True, recursive=True)
+ self.RunShellCommand(['rm', '-f'] + all_stale_files, check_return=True)
if all_changed_files:
if missing_dirs:
@@ -1673,66 +1483,15 @@ class DeviceUtils(object):
else:
to_push.append((host_abs_path, device_abs_path))
to_delete = device_checksums.keys()
- # We can't rely solely on the checksum approach since it does not catch
- # stale directories, which can result in empty directories that cause issues
- # during copying in efficient_android_directory_copy.sh. So, find any stale
- # directories here so they can be removed in addition to stale files.
- if track_stale:
- to_delete.extend(self._GetStaleDirectories(host_path, device_path))
def cache_commit_func():
- # When host_path is a not a directory, the path.join() call below would
- # have an '' as the second argument, causing an unwanted / to be appended.
- if os.path.isfile(host_path):
- assert len(host_checksums) == 1
- new_sums = {device_path: host_checksums[host_path]}
- else:
- new_sums = {posixpath.join(device_path, path[len(host_path) + 1:]): val
- for path, val in host_checksums.iteritems()}
+ new_sums = {posixpath.join(device_path, path[len(host_path) + 1:]): val
+ for path, val in host_checksums.iteritems()}
cache_entry = [ignore_other_files, new_sums]
self._cache['device_path_checksums'][device_path] = cache_entry
return (to_push, up_to_date, to_delete, cache_commit_func)
- def _GetStaleDirectories(self, host_path, device_path):
- """Gets a list of stale directories on the device.
-
- Args:
- host_path: an absolute path of a directory on the host
- device_path: an absolute path of a directory on the device
-
- Returns:
- A list containing absolute paths to directories on the device that are
- considered stale.
- """
- def get_device_dirs(path):
- directories = set()
- command = _RECURSIVE_DIRECTORY_LIST_SCRIPT % cmd_helper.SingleQuote(path)
- # We use shell=True to evaluate the command as a script through the shell,
- # otherwise RunShellCommand tries to interpret it as the name of a (non
- # existent) command to run.
- for line in self.RunShellCommand(
- command, shell=True, check_return=True):
- directories.add(posixpath.relpath(posixpath.normpath(line), path))
- return directories
-
- def get_host_dirs(path):
- directories = set()
- if not os.path.isdir(path):
- return directories
- for root, _, _ in os.walk(path):
- if root != path:
- # Strip off the top level directory so we can compare the device and
- # host.
- directories.add(
- os.path.relpath(root, path).replace(os.sep, posixpath.sep))
- return directories
-
- host_dirs = get_host_dirs(host_path)
- device_dirs = get_device_dirs(device_path)
- stale_dirs = device_dirs - host_dirs
- return [posixpath.join(device_path, d) for d in stale_dirs]
-
def _ComputeDeviceChecksumsForApks(self, package_name):
ret = self._cache['package_apk_checksums'].get(package_name)
if ret is None:
@@ -1849,8 +1608,6 @@ class DeviceUtils(object):
except zip_utils.ZipFailedError:
return False
- logger.info('Pushing %d files via .zip of size %d', len(files),
- os.path.getsize(zip_path))
self.NeedsSU()
with device_temp_file.DeviceTempFile(
self.adb, suffix='.zip') as device_temp:
@@ -1947,29 +1704,8 @@ class DeviceUtils(object):
device_path if not rename else [_RenamePath(p) for p in device_path])
self.RunShellCommand(args, as_root=as_root, check_return=True)
- @contextlib.contextmanager
- def _CopyToReadableLocation(self, device_path):
- """Context manager to copy a file to a globally readable temp file.
-
- This uses root permission to copy a file to a globally readable named
- temporary file. The temp file is removed when this contextmanager is closed.
-
- Args:
- device_path: A string containing the absolute path of the file (on the
- device) to copy.
- Yields:
- The globally readable file object.
- """
- with device_temp_file.DeviceTempFile(self.adb) as device_temp:
- cmd = 'SRC=%s DEST=%s;cp "$SRC" "$DEST" && chmod 666 "$DEST"' % (
- cmd_helper.SingleQuote(device_path),
- cmd_helper.SingleQuote(device_temp.name))
- self.RunShellCommand(cmd, shell=True, as_root=True, check_return=True)
- yield device_temp
-
@decorators.WithTimeoutAndRetriesFromInstance()
- def PullFile(self, device_path, host_path, as_root=False, timeout=None,
- retries=None):
+ def PullFile(self, device_path, host_path, timeout=None, retries=None):
"""Pull a file from the device.
Args:
@@ -1977,7 +1713,6 @@ class DeviceUtils(object):
from the device.
host_path: A string containing the absolute path of the destination on
the host.
- as_root: Whether root permissions should be used to pull the file.
timeout: timeout in seconds
retries: number of retries
@@ -1989,14 +1724,7 @@ class DeviceUtils(object):
dirname = os.path.dirname(host_path)
if dirname and not os.path.exists(dirname):
os.makedirs(dirname)
- if as_root and self.NeedsSU():
- if not self.PathExists(device_path, as_root=True):
- raise device_errors.CommandFailedError(
- '%r: No such file or directory' % device_path, str(self))
- with self._CopyToReadableLocation(device_path) as readable_temp_file:
- self.adb.Pull(readable_temp_file.name, host_path)
- else:
- self.adb.Pull(device_path, host_path)
+ self.adb.Pull(device_path, host_path)
def _ReadFileWithPull(self, device_path):
try:
@@ -2043,8 +1771,12 @@ class DeviceUtils(object):
return _JoinLines(self.RunShellCommand(
['cat', device_path], as_root=as_root, check_return=True))
elif as_root and self.NeedsSU():
- with self._CopyToReadableLocation(device_path) as readable_temp_file:
- return self._ReadFileWithPull(readable_temp_file.name)
+ with device_temp_file.DeviceTempFile(self.adb) as device_temp:
+ cmd = 'SRC=%s DEST=%s;cp "$SRC" "$DEST" && chmod 666 "$DEST"' % (
+ cmd_helper.SingleQuote(device_path),
+ cmd_helper.SingleQuote(device_temp.name))
+ self.RunShellCommand(cmd, shell=True, as_root=True, check_return=True)
+ return self._ReadFileWithPull(device_temp.name)
else:
return self._ReadFileWithPull(device_path)
@@ -2319,42 +2051,20 @@ class DeviceUtils(object):
else:
return False
- def GetLocale(self, cache=False):
- """Returns the locale setting on the device.
-
- Args:
- cache: Whether to use cached properties when available.
- Returns:
- A pair (language, country).
- """
- locale = self.GetProp('persist.sys.locale', cache=cache)
- if locale:
- if '-' not in locale:
- logging.error('Unparsable locale: %s', locale)
- return ('', '') # Behave as if persist.sys.locale is undefined.
- return tuple(locale.split('-', 1))
- return (self.GetProp('persist.sys.language', cache=cache),
- self.GetProp('persist.sys.country', cache=cache))
-
def GetLanguage(self, cache=False):
"""Returns the language setting on the device.
-
- DEPRECATED: Prefer GetLocale() instead.
-
Args:
cache: Whether to use cached properties when available.
"""
- return self.GetLocale(cache=cache)[0]
+ return self.GetProp('persist.sys.language', cache=cache)
def GetCountry(self, cache=False):
"""Returns the country setting on the device.
- DEPRECATED: Prefer GetLocale() instead.
-
Args:
cache: Whether to use cached properties when available.
"""
- return self.GetLocale(cache=cache)[1]
+ return self.GetProp('persist.sys.country', cache=cache)
@property
def screen_density(self):
@@ -2427,11 +2137,7 @@ class DeviceUtils(object):
@property
def product_cpu_abi(self):
- """Returns the product cpu abi of the device (e.g. 'armeabi-v7a').
-
- For supported ABIs, the return value will be one of the values defined in
- devil.android.ndk.abis.
- """
+ """Returns the product cpu abi of the device (e.g. 'armeabi-v7a')."""
return self.GetProp('ro.product.cpu.abi', cache=True)
@property
@@ -2557,8 +2263,7 @@ class DeviceUtils(object):
retries: number of retries
Returns:
- The device's main ABI name. For supported ABIs, the return value will be
- one of the values defined in devil.android.ndk.abis.
+ The device's main ABI name.
Raises:
CommandTimeoutError on timeout.
@@ -2574,8 +2279,9 @@ class DeviceUtils(object):
"""
try:
ps_cmd = 'ps'
- # ps behavior was changed in Android O and above, http://crbug.com/686716
- if self.build_version_sdk >= version_codes.OREO:
+ # ps behavior was changed in Android above N, http://crbug.com/686716
+ if (self.build_version_sdk >= version_codes.NOUGAT_MR1
+ and self.build_id[0] > 'N'):
ps_cmd = 'ps -e'
if pattern:
return self._RunPipedShellCommand(
@@ -2625,29 +2331,6 @@ class DeviceUtils(object):
processes.append(ProcessInfo(**row))
return processes
- def _GetDumpsysOutput(self, extra_args, pattern=None):
- """Runs |dumpsys| command on the device and returns its output.
-
- This private method implements support for filtering the output by a given
- |pattern|, but does not do any output parsing.
- """
- try:
- cmd = ['dumpsys'] + extra_args
- if pattern:
- cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd)
- return self._RunPipedShellCommand(
- '%s | grep -F %s' % (cmd, cmd_helper.SingleQuote(pattern)))
- else:
- cmd = ['dumpsys'] + extra_args
- return self.RunShellCommand(cmd, check_return=True, large_output=True)
- except device_errors.AdbShellCommandFailedError as e:
- if e.status and isinstance(e.status, list) and not e.status[0]:
- # If dumpsys succeeded but grep failed, there were no lines matching
- # the given pattern.
- return []
- else:
- raise
-
# TODO(#4103): Remove after migrating clients to ListProcesses.
@decorators.WithTimeoutAndRetriesFromInstance()
def GetPids(self, process_name=None, timeout=None, retries=None):
@@ -2755,181 +2438,6 @@ class DeviceUtils(object):
check_return=True)
@decorators.WithTimeoutAndRetriesFromInstance()
- def GetWebViewUpdateServiceDump(self, timeout=None, retries=None):
- """Get the WebView update command sysdump on the device.
-
- Returns:
- A dictionary with these possible entries:
- FallbackLogicEnabled: True|False
- CurrentWebViewPackage: "package name" or None
- MinimumWebViewVersionCode: int
- WebViewPackages: Dict of installed WebView providers, mapping "package
- name" to "reason it's valid/invalid."
-
- It may return an empty dictionary if device does not
- support the "dumpsys webviewupdate" command.
-
- Raises:
- CommandFailedError on failure.
- CommandTimeoutError on timeout.
- DeviceUnreachableError on missing device.
- """
- result = {}
-
- # Command was implemented starting in Oreo
- if self.build_version_sdk < version_codes.OREO:
- return result
-
- output = self.RunShellCommand(
- ['dumpsys', 'webviewupdate'], check_return=True)
- webview_packages = {}
- for line in output:
- match = re.search(_WEBVIEW_SYSUPDATE_CURRENT_PKG_RE, line)
- if match:
- result['CurrentWebViewPackage'] = match.group(1)
- match = re.search(_WEBVIEW_SYSUPDATE_NULL_PKG_RE, line)
- if match:
- result['CurrentWebViewPackage'] = None
- match = re.search(_WEBVIEW_SYSUPDATE_FALLBACK_LOGIC_RE, line)
- if match:
- result['FallbackLogicEnabled'] = \
- True if match.group(1) == 'true' else False
- match = re.search(_WEBVIEW_SYSUPDATE_PACKAGE_INSTALLED_RE, line)
- if match:
- package_name = match.group(1)
- reason = match.group(2)
- webview_packages[package_name] = reason
- match = re.search(_WEBVIEW_SYSUPDATE_PACKAGE_NOT_INSTALLED_RE, line)
- if match:
- package_name = match.group(1)
- reason = match.group(2)
- webview_packages[package_name] = reason
- match = re.search(_WEBVIEW_SYSUPDATE_MIN_VERSION_CODE, line)
- if match:
- result['MinimumWebViewVersionCode'] = int(match.group(1))
- if webview_packages:
- result['WebViewPackages'] = webview_packages
-
- missing_fields = set(['CurrentWebViewPackage', 'FallbackLogicEnabled']) - \
- set(result.keys())
- if len(missing_fields) > 0:
- raise device_errors.CommandFailedError(
- '%s not found in dumpsys webviewupdate' % str(list(missing_fields)))
- return result
-
- @decorators.WithTimeoutAndRetriesFromInstance()
- def SetWebViewImplementation(self, package_name, timeout=None, retries=None):
- """Select the WebView implementation to the specified package.
-
- Args:
- package_name: The package name of a WebView implementation. The package
- must be already installed on the device.
- timeout: timeout in seconds
- retries: number of retries
-
- Raises:
- CommandFailedError on failure.
- CommandTimeoutError on timeout.
- DeviceUnreachableError on missing device.
- """
- installed = self.GetApplicationPaths(package_name)
- if not installed:
- raise device_errors.CommandFailedError(
- '%s is not installed' % package_name, str(self))
- output = self.RunShellCommand(
- ['cmd', 'webviewupdate', 'set-webview-implementation', package_name],
- single_line=True,
- check_return=False)
- if output == 'Success':
- logging.info('WebView provider set to: %s', package_name)
- else:
- dumpsys_output = self.GetWebViewUpdateServiceDump()
- webview_packages = dumpsys_output.get('WebViewPackages')
- if webview_packages:
- reason = webview_packages.get(package_name)
- if not reason:
- all_provider_package_names = webview_packages.keys()
- raise device_errors.CommandFailedError(
- '%s is not in the system WebView provider list. Must choose one '
- 'of %r.' % (package_name, all_provider_package_names), str(self))
- if re.search(r'is\s+NOT\s+installed/enabled for all users', reason):
- raise device_errors.CommandFailedError(
- '%s is disabled, make sure to disable WebView fallback logic' %
- package_name, str(self))
- if re.search(r'No WebView-library manifest flag', reason):
- raise device_errors.CommandFailedError(
- '%s does not declare a WebView native library, so it cannot '
- 'be a WebView provider' % package_name, str(self))
- if re.search(r'SDK version too low', reason):
- raise device_errors.CommandFailedError(
- '%s needs a higher targetSdkVersion (must be >= %d)' %
- (package_name, self.build_version_sdk), str(self))
- if re.search(r'Version code too low', reason):
- raise device_errors.CommandFailedError(
- '%s needs a higher versionCode (must be >= %d)' %
- (package_name, dumpsys_output.get('MinimumWebViewVersionCode')),
- str(self))
- if re.search(r'Incorrect signature', reason):
- raise device_errors.CommandFailedError(
- '%s is not signed with release keys (but user builds require '
- 'this for WebView providers)' % package_name, str(self))
- raise device_errors.CommandFailedError(
- 'Error setting WebView provider: %s' % output, str(self))
-
- @decorators.WithTimeoutAndRetriesFromInstance()
- def SetWebViewFallbackLogic(self, enabled, timeout=None, retries=None):
- """Set whether WebViewUpdateService's "fallback logic" should be enabled.
-
- WebViewUpdateService has nonintuitive "fallback logic" for devices where
- Monochrome (Chrome Stable) is preinstalled as the WebView provider, with a
- "stub" (little-to-no code) implementation of standalone WebView.
-
- "Fallback logic" (enabled by default) is designed, in the case where the
- user has disabled Chrome, to fall back to the stub standalone WebView by
- enabling the package. The implementation plumbs through the Chrome APK until
- Play Store installs an update with the full implementation.
-
- A surprising side-effect of "fallback logic" is that, immediately after
- sideloading WebView, WebViewUpdateService re-disables the package and
- uninstalls the update. This can prevent successfully using standalone
- WebView for development, although "fallback logic" can be disabled on
- userdebug/eng devices.
-
- Because this is only relevant for devices with the standalone WebView stub,
- this command is only relevant on N-P (inclusive).
-
- You can determine if "fallback logic" is currently enabled by checking
- FallbackLogicEnabled in the dictionary returned by
- GetWebViewUpdateServiceDump.
-
- Args:
- enabled: bool - True for enabled, False for disabled
- timeout: timeout in seconds
- retries: number of retries
-
- Raises:
- CommandFailedError on failure.
- CommandTimeoutError on timeout.
- DeviceUnreachableError on missing device.
- """
-
- # Command is only available on devices which preinstall stub WebView.
- if not version_codes.NOUGAT <= self.build_version_sdk <= version_codes.PIE:
- return
-
- # redundant-packages is the opposite of fallback logic
- enable_string = 'disable' if enabled else 'enable'
- output = self.RunShellCommand(
- ['cmd', 'webviewupdate', '%s-redundant-packages' % enable_string],
- single_line=True, check_return=True)
- if output == 'Success':
- logging.info('WebView Fallback Logic is %s',
- 'enabled' if enabled else 'disabled')
- else:
- raise device_errors.CommandFailedError(
- 'Error setting WebView Fallback Logic: %s' % output, str(self))
-
- @decorators.WithTimeoutAndRetriesFromInstance()
def TakeScreenshot(self, host_path=None, timeout=None, retries=None):
"""Takes a screenshot of the device.
@@ -3003,7 +2511,7 @@ class DeviceUtils(object):
self._client_caches[client_name] = {}
return self._client_caches[client_name]
- def ClearCache(self):
+ def _ClearCache(self):
"""Clears all caches."""
for client in self._client_caches:
self._client_caches[client].clear()
@@ -3106,8 +2614,8 @@ class DeviceUtils(object):
return parallelizer.SyncParallelizer(devices)
@classmethod
- def HealthyDevices(cls, blacklist=None, device_arg='default', retries=1,
- enable_usb_resets=False, abis=None, **kwargs):
+ def HealthyDevices(cls, blacklist=None, device_arg='default', retry=True,
+ **kwargs):
"""Returns a list of DeviceUtils instances.
Returns a list of DeviceUtils instances that are attached, not blacklisted,
@@ -3129,14 +2637,8 @@ class DeviceUtils(object):
blacklisted.
['A', 'B', ...] -> Returns instances for the subset that is not
blacklisted.
- retries: Number of times to restart adb server and query it again if no
- devices are found on the previous attempts, with exponential backoffs
- up to 60s between each retry.
- enable_usb_resets: If true, will attempt to trigger a USB reset prior to
- the last attempt if there are no available devices. It will only reset
- those that appear to be android devices.
- abis: A list of ABIs for which the device needs to support at least one of
- (optional). See devil.android.ndk.abis for valid values.
+ retry: If true, will attempt to restart adb server and query it again if
+ no devices are found.
A device serial, or a list of device serials (optional).
Returns:
@@ -3172,23 +2674,14 @@ class DeviceUtils(object):
return True
return False
- def supports_abi(abi, serial):
- if abis and abi not in abis:
- logger.warning("Device %s doesn't support required ABIs.", serial)
- return False
- return True
-
def _get_devices():
if device_arg:
devices = [cls(x, **kwargs) for x in device_arg if not blacklisted(x)]
else:
devices = []
for adb in adb_wrapper.AdbWrapper.Devices():
- serial = adb.GetDeviceSerial()
- if not blacklisted(serial):
- device = cls(_CreateAdbWrapper(adb), **kwargs)
- if supports_abi(device.GetABI(), serial):
- devices.append(device)
+ if not blacklisted(adb.GetDeviceSerial()):
+ devices.append(cls(_CreateAdbWrapper(adb), **kwargs))
if len(devices) == 0 and not allow_no_devices:
raise device_errors.NoDevicesError()
@@ -3196,37 +2689,15 @@ class DeviceUtils(object):
raise device_errors.MultipleDevicesError(devices)
return sorted(devices)
- def _reset_devices():
- if not reset_usb:
- logging.error(
- 'reset_usb.py not supported on this platform (%s). Skipping usb '
- 'resets.', sys.platform)
- return
- if device_arg:
- for serial in device_arg:
- reset_usb.reset_android_usb(serial)
- else:
- reset_usb.reset_all_android_devices()
-
- for attempt in xrange(retries+1):
- try:
- return _get_devices()
- except device_errors.NoDevicesError:
- if attempt == retries:
- logging.error('No devices found after exhausting all retries.')
- raise
- elif attempt == retries - 1 and enable_usb_resets:
- logging.warning(
- 'Attempting to reset relevant USB devices prior to the last '
- 'attempt.')
- _reset_devices()
- # math.pow returns floats, so cast to int for easier testing
- sleep_s = min(int(math.pow(2, attempt + 1)), 60)
- logger.warning(
- 'No devices found. Will try again after restarting adb server '
- 'and a short nap of %d s.', sleep_s)
- time.sleep(sleep_s)
- RestartServer()
+ try:
+ return _get_devices()
+ except device_errors.NoDevicesError:
+ if not retry:
+ raise
+ logger.warning(
+ 'No devices found. Will try again after restarting adb server.')
+ RestartServer()
+ return _get_devices()
@decorators.WithTimeoutAndRetriesFromInstance()
def RestartAdbd(self, timeout=None, retries=None):
@@ -3335,39 +2806,3 @@ class DeviceUtils(object):
return
self.SendKeyEvent(keyevent.KEYCODE_POWER)
timeout_retry.WaitFor(screen_test, wait_period=1)
-
- @decorators.WithTimeoutAndRetriesFromInstance()
- def ChangeOwner(self, owner_group, paths, timeout=None, retries=None):
- """Changes file system ownership for permissions.
-
- Args:
- owner_group: New owner and group to assign. Note that this should be a
- string in the form user[.group] where the group is option.
- paths: Paths to change ownership of.
-
- Note that the -R recursive option is not supported by all Android
- versions.
- """
- if not paths:
- return
- self.RunShellCommand(['chown', owner_group] + paths, check_return=True)
-
- @decorators.WithTimeoutAndRetriesFromInstance()
- def ChangeSecurityContext(self, security_context, paths, timeout=None,
- retries=None):
- """Changes the SELinux security context for files.
-
- Args:
- security_context: The new security context as a string
- paths: Paths to change the security context of.
-
- Note that the -R recursive option is not supported by all Android
- versions.
- """
- if not paths:
- return
- command = ['chcon', security_context] + paths
-
- # Note, need to force su because chcon can fail with permission errors even
- # if the device is rooted.
- self.RunShellCommand(command, as_root=_FORCE_SU, check_return=True)
diff --git a/systrace/catapult/devil/devil/android/device_utils_devicetest.py b/systrace/catapult/devil/devil/android/device_utils_devicetest.py
index 0836f3e..173094b 100755
--- a/systrace/catapult/devil/devil/android/device_utils_devicetest.py
+++ b/systrace/catapult/devil/devil/android/device_utils_devicetest.py
@@ -210,39 +210,6 @@ class DeviceUtilsPushDeleteFilesTest(device_test_case.DeviceTestCase):
cmd_helper.RunCmd(['rm', '-rf', host_tmp_dir])
self.device.RemovePath(_DEVICE_DIR, recursive=True, force=True)
- def testPushWithStaleDirectories(self):
- # Make a few files and directories to push.
- host_tmp_dir = tempfile.mkdtemp()
- host_sub_dir1 = '%s/%s' % (host_tmp_dir, _SUB_DIR1)
- host_sub_dir2 = "%s/%s/%s" % (host_tmp_dir, _SUB_DIR, _SUB_DIR2)
- os.makedirs(host_sub_dir1)
- os.makedirs(host_sub_dir2)
-
- self._MakeTempFileGivenDir(host_sub_dir1, _OLD_CONTENTS)
- self._MakeTempFileGivenDir(host_sub_dir2, _OLD_CONTENTS)
-
- # Push all our created files/directories and verify they're on the device.
- self.device.PushChangedFiles([(host_tmp_dir, _DEVICE_DIR)],
- delete_device_stale=True)
- top_level_dirs = self.device.ListDirectory(_DEVICE_DIR)
- self.assertIn(_SUB_DIR1, top_level_dirs)
- self.assertIn(_SUB_DIR, top_level_dirs)
- sub_dir = self.device.ListDirectory('%s/%s' % (_DEVICE_DIR, _SUB_DIR))
- self.assertIn(_SUB_DIR2, sub_dir)
-
- # Remove one of the directories on the host and push again.
- cmd_helper.RunCmd(['rm', '-rf', host_sub_dir2])
- self.device.PushChangedFiles([(host_tmp_dir, _DEVICE_DIR)],
- delete_device_stale=True)
-
- # Verify that the directory we removed is no longer on the device, but the
- # other directories still are.
- top_level_dirs = self.device.ListDirectory(_DEVICE_DIR)
- self.assertIn(_SUB_DIR1, top_level_dirs)
- self.assertIn(_SUB_DIR, top_level_dirs)
- sub_dir = self.device.ListDirectory('%s/%s' % (_DEVICE_DIR, _SUB_DIR))
- self.assertEqual([], sub_dir)
-
def testRestartAdbd(self):
def get_adbd_pid():
try:
diff --git a/systrace/catapult/devil/devil/android/device_utils_test.py b/systrace/catapult/devil/devil/android/device_utils_test.py
index 5799c7b..b5660ac 100755
--- a/systrace/catapult/devil/devil/android/device_utils_test.py
+++ b/systrace/catapult/devil/devil/android/device_utils_test.py
@@ -15,14 +15,12 @@ import json
import logging
import os
import stat
-import sys
import unittest
from devil import devil_env
from devil.android import device_errors
from devil.android import device_signal
from devil.android import device_utils
-from devil.android.ndk import abis
from devil.android.sdk import adb_wrapper
from devil.android.sdk import intent
from devil.android.sdk import keyevent
@@ -33,6 +31,7 @@ from devil.utils import mock_calls
with devil_env.SysPath(devil_env.PYMOCK_PATH):
import mock # pylint: disable=import-error
+
def Process(name, pid, ppid='1'):
return device_utils.ProcessInfo(name=name, pid=pid, ppid=ppid)
@@ -56,10 +55,8 @@ class _MockApkHelper(object):
def __init__(self, path, package_name, perms=None):
self.path = path
- self.is_bundle = path.endswith('_bundle')
self.package_name = package_name
self.perms = perms
- self.abis = [abis.ARM]
def GetPackageName(self):
return self.package_name
@@ -67,9 +64,6 @@ class _MockApkHelper(object):
def GetPermissions(self):
return self.perms
- def GetAbis(self):
- return self.abis
-
class _MockMultipleDevicesError(Exception):
pass
@@ -314,62 +308,32 @@ class DeviceUtilsIsOnlineTest(DeviceUtilsTest):
class DeviceUtilsHasRootTest(DeviceUtilsTest):
def testHasRoot_true(self):
- with self.patch_call(self.call.device.build_type,
- return_value='userdebug'), (
- self.patch_call(self.call.device.product_name,
- return_value='notasailfish')), (
+ with self.patch_call(self.call.device.product_name,
+ return_value='notasailfish'), (
self.assertCall(self.call.adb.Shell('ls /root'), 'foo\n')):
self.assertTrue(self.device.HasRoot())
def testhasRootSpecial_true(self):
- with self.patch_call(self.call.device.build_type,
- return_value='userdebug'), (
- self.patch_call(self.call.device.product_name,
- return_value='sailfish')), (
+ with self.patch_call(self.call.device.product_name,
+ return_value='sailfish'), (
self.assertCall(self.call.adb.Shell('getprop service.adb.root'),
'1\n')):
self.assertTrue(self.device.HasRoot())
- def testhasRootSpecialAosp_true(self):
- with self.patch_call(self.call.device.build_type,
- return_value='userdebug'), (
- self.patch_call(self.call.device.product_name,
- return_value='aosp_sailfish')), (
- self.assertCall(self.call.adb.Shell('getprop service.adb.root'),
- '1\n')):
- self.assertTrue(self.device.HasRoot())
-
- def testhasRootEngBuild_true(self):
- with self.patch_call(self.call.device.build_type,
- return_value='eng'):
- self.assertTrue(self.device.HasRoot())
-
def testHasRoot_false(self):
- with self.patch_call(self.call.device.build_type,
- return_value='userdebug'), (
- self.patch_call(self.call.device.product_name,
- return_value='notasailfish')), (
+ with self.patch_call(self.call.device.product_name,
+ return_value='notasailfish'), (
self.assertCall(self.call.adb.Shell('ls /root'),
self.ShellError())):
self.assertFalse(self.device.HasRoot())
def testHasRootSpecial_false(self):
- with self.patch_call(self.call.device.build_type,
- return_value='userdebug'), (
- self.patch_call(self.call.device.product_name,
- return_value='sailfish')), (
+ with self.patch_call(self.call.device.product_name,
+ return_value='sailfish'), (
self.assertCall(self.call.adb.Shell('getprop service.adb.root'),
'\n')):
self.assertFalse(self.device.HasRoot())
- def testHasRootSpecialAosp_false(self):
- with self.patch_call(self.call.device.build_type,
- return_value='userdebug'), (
- self.patch_call(self.call.device.product_name,
- return_value='aosp_sailfish')), (
- self.assertCall(self.call.adb.Shell('getprop service.adb.root'),
- '\n')):
- self.assertFalse(self.device.HasRoot())
class DeviceUtilsEnableRootTest(DeviceUtilsTest):
@@ -377,7 +341,7 @@ class DeviceUtilsEnableRootTest(DeviceUtilsTest):
with self.assertCalls(
self.call.adb.Root(),
self.call.adb.WaitForDevice(),
- (self.call.device.HasRoot(), True)):
+ (self.call.device.GetProp('service.adb.root', cache=False), '1')):
self.device.EnableRoot()
def testEnableRoot_userBuild(self):
@@ -394,16 +358,6 @@ class DeviceUtilsEnableRootTest(DeviceUtilsTest):
with self.assertRaises(device_errors.AdbCommandFailedError):
self.device.EnableRoot()
- def testEnableRoot_timeoutInWaitForDevice(self):
- with self.assertCalls(
- (self.call.adb.Root(),
- self.AdbCommandError(
- output='timeout expired while waiting for device')),
- (self.call.device.IsUserBuild(), False),
- self.call.adb.WaitForDevice(),
- (self.call.device.HasRoot(), True)):
- self.device.EnableRoot()
-
class DeviceUtilsIsUserBuildTest(DeviceUtilsTest):
@@ -511,27 +465,6 @@ class DeviceUtils_GetApplicationVersionTest(DeviceUtilsTest):
self.device.GetApplicationVersion('com.android.chrome')
-class DeviceUtils_GetPackageArchitectureTest(DeviceUtilsTest):
-
- def test_GetPackageArchitecture_exists(self):
- with self.assertCall(
- self.call.device._RunPipedShellCommand(
- 'dumpsys package com.android.chrome | grep -F primaryCpuAbi'),
- [' primaryCpuAbi=armeabi-v7a']):
- self.assertEquals(
- abis.ARM,
- self.device.GetPackageArchitecture('com.android.chrome'))
-
- def test_GetPackageArchitecture_notExists(self):
- with self.assertCall(
- self.call.device._RunPipedShellCommand(
- 'dumpsys package com.android.chrome | grep -F primaryCpuAbi'),
- []):
- self.assertEquals(
- None,
- self.device.GetPackageArchitecture('com.android.chrome'))
-
-
class DeviceUtilsGetApplicationDataDirectoryTest(DeviceUtilsTest):
def testGetApplicationDataDirectory_exists(self):
@@ -782,18 +715,6 @@ class DeviceUtilsInstallTest(DeviceUtilsTest):
self.device.Install(DeviceUtilsInstallTest.mock_apk, retries=0,
permissions=['p1', 'p2'])
- def testInstall_identicalPriorInstall(self):
- with self.assertCalls(
- (mock.call.os.path.exists('/fake/test/app.apk'), True),
- (self.call.device._GetApplicationPathsInternal('test.package'),
- ['/fake/data/app/test.package.apk']),
- (self.call.device._ComputeStaleApks('test.package',
- ['/fake/test/app.apk']),
- ([], None)),
- (self.call.device.ForceStop('test.package'))):
- self.device.Install(DeviceUtilsInstallTest.mock_apk, retries=0,
- permissions=[])
-
def testInstall_differentPriorInstall(self):
with self.assertCalls(
(mock.call.os.path.exists('/fake/test/app.apk'), True),
@@ -808,18 +729,6 @@ class DeviceUtilsInstallTest(DeviceUtilsTest):
self.device.Install(DeviceUtilsInstallTest.mock_apk, retries=0,
permissions=[])
- def testInstall_differentPriorInstallSplitApk(self):
- with self.assertCalls(
- (mock.call.os.path.exists('/fake/test/app.apk'), True),
- (self.call.device._GetApplicationPathsInternal('test.package'),
- ['/fake/data/app/test.package.apk',
- '/fake/data/app/test.package2.apk']),
- self.call.device.Uninstall('test.package'),
- self.call.adb.Install('/fake/test/app.apk', reinstall=False,
- allow_downgrade=False)):
- self.device.Install(DeviceUtilsInstallTest.mock_apk, retries=0,
- permissions=[])
-
def testInstall_differentPriorInstall_reinstall(self):
with self.assertCalls(
(mock.call.os.path.exists('/fake/test/app.apk'), True),
@@ -874,11 +783,6 @@ class DeviceUtilsInstallTest(DeviceUtilsTest):
self.device.Install(DeviceUtilsInstallTest.mock_apk,
reinstall=True, retries=0, permissions=[], allow_downgrade=True)
- def testInstall_modulesSpecified(self):
- with self.assertRaises(device_errors.CommandFailedError):
- self.device.Install(DeviceUtilsInstallTest.mock_apk,
- modules=['base'])
-
class DeviceUtilsInstallSplitApkTest(DeviceUtilsTest):
@@ -961,61 +865,6 @@ class DeviceUtilsInstallSplitApkTest(DeviceUtilsTest):
['split1.apk', 'split2.apk', 'split3.apk'], permissions=[],
retries=0)
- def testInstallSplitApk_previouslyNonSplit(self):
- with self.assertCalls(
- (self.call.device._CheckSdkLevel(21)),
- (mock.call.devil.android.sdk.split_select.SelectSplits(
- self.device, 'base.apk',
- ['split1.apk', 'split2.apk', 'split3.apk'],
- allow_cached_props=False),
- ['split2.apk']),
- (mock.call.os.path.exists('base.apk'), True),
- (mock.call.os.path.exists('split2.apk'), True),
- (self.call.device._GetApplicationPathsInternal(
- 'test.package'), ['/fake/data/app/test.package.apk']),
- self.call.device.Uninstall('test.package'),
- (self.call.adb.InstallMultiple(
- ['base.apk', 'split2.apk'], partial=None, reinstall=False,
- allow_downgrade=False))):
- self.device.InstallSplitApk(DeviceUtilsInstallSplitApkTest.mock_apk,
- ['split1.apk', 'split2.apk', 'split3.apk'], permissions=[], retries=0)
-
-
-class DeviceUtilsInstallBundleTest(DeviceUtilsTest):
- mock_apk = _MockApkHelper('/fake/test/app_bundle', 'test.package', ['p1'])
-
- def testInstallBundle_noPriorInstall(self):
- with self.patch_call(self.call.device.build_version_sdk, return_value=23):
- with self.assertCalls(
- (mock.call.devil.utils.cmd_helper.RunCmd(
- ['/fake/test/app_bundle', 'install', '--device',
- self.device.serial]), 0),
- (self.call.device.GrantPermissions('test.package', ['p1']), [])):
- self.device.Install(DeviceUtilsInstallBundleTest.mock_apk)
-
- def testInstallBundle_modulesSpecified(self):
- with self.patch_call(self.call.device.build_version_sdk, return_value=23):
- with self.assertCalls(
- (mock.call.devil.utils.cmd_helper.RunCmd(
- ['/fake/test/app_bundle', 'install', '--device',
- self.device.serial, '-m', 'base']), 0),
- (self.call.device.GrantPermissions('test.package', ['p1']), [])):
- self.device.Install(
- DeviceUtilsInstallBundleTest.mock_apk, modules=['base'])
-
- def testInstallBundle_permissionsPreM(self):
- with self.patch_call(self.call.device.build_version_sdk, return_value=20):
- with self.assertCalls(
- (mock.call.devil.utils.cmd_helper.RunCmd(
- ['/fake/test/app_bundle', 'install', '--device',
- self.device.serial]), 0)):
- self.device.Install(DeviceUtilsInstallBundleTest.mock_apk)
-
- def testInstallBundle_splitApks(self):
- with self.assertRaises(device_errors.CommandFailedError):
- self.device.InstallSplitApk(
- DeviceUtilsInstallBundleTest.mock_apk, ['apk1', 'apk2'])
-
class DeviceUtilsUninstallTest(DeviceUtilsTest):
@@ -1566,62 +1415,6 @@ class DeviceUtilsStartActivityTest(DeviceUtilsTest):
self.device.StartActivity(test_intent)
-class DeviceUtilsStartServiceTest(DeviceUtilsTest):
- def testStartService_success(self):
- test_intent = intent.Intent(action='android.intent.action.START',
- package='test.package',
- activity='.Main')
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.NOUGAT):
- with self.assertCall(
- self.call.adb.Shell('am startservice '
- '-a android.intent.action.START '
- '-n test.package/.Main'),
- 'Starting service: Intent { act=android.intent.action.START }'):
- self.device.StartService(test_intent)
-
- def testStartService_failure(self):
- test_intent = intent.Intent(action='android.intent.action.START',
- package='test.package',
- activity='.Main')
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.NOUGAT):
- with self.assertCall(
- self.call.adb.Shell('am startservice '
- '-a android.intent.action.START '
- '-n test.package/.Main'),
- 'Error: Failed to start test service'):
- with self.assertRaises(device_errors.CommandFailedError):
- self.device.StartService(test_intent)
-
- def testStartService_withUser(self):
- test_intent = intent.Intent(action='android.intent.action.START',
- package='test.package',
- activity='.Main')
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.NOUGAT):
- with self.assertCall(
- self.call.adb.Shell('am startservice '
- '--user TestUser '
- '-a android.intent.action.START '
- '-n test.package/.Main'),
- 'Starting service: Intent { act=android.intent.action.START }'):
- self.device.StartService(test_intent, user_id='TestUser')
-
- def testStartService_onOreo(self):
- test_intent = intent.Intent(action='android.intent.action.START',
- package='test.package',
- activity='.Main')
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.OREO):
- with self.assertCall(
- self.call.adb.Shell('am start-service '
- '-a android.intent.action.START '
- '-n test.package/.Main'),
- 'Starting service: Intent { act=android.intent.action.START }'):
- self.device.StartService(test_intent)
-
-
class DeviceUtilsStartInstrumentationTest(DeviceUtilsTest):
def testStartInstrumentation_nothing(self):
@@ -1898,8 +1691,6 @@ class DeviceUtilsPushChangedFilesZippedTest(DeviceUtilsTest):
mock_zip_temp_dir),
(mock.call.devil.utils.zip_utils.WriteZipFile(
'/test/temp/dir/tmp.zip', test_files)),
- (mock.call.os.path.getsize(
- '/test/temp/dir/tmp.zip'), 123),
(self.call.device.NeedsSU(), True),
(mock.call.devil.android.device_temp_file.DeviceTempFile(self.adb,
suffix='.zip'),
@@ -2027,33 +1818,6 @@ class DeviceUtilsPullFileTest(DeviceUtilsTest):
self.device.PullFile('/data/app/test.file.does.not.exist',
'/test/file/host/path')
- def testPullFile_asRoot(self):
- with mock.patch('os.path.exists', return_value=True):
- with self.assertCalls(
- (self.call.device.NeedsSU(), True),
- (self.call.device.PathExists('/this/file/can.be.read.with.su',
- as_root=True), True),
- (mock.call.devil.android.device_temp_file.DeviceTempFile(self.adb),
- MockTempFile('/sdcard/tmp/on.device')),
- self.call.device.RunShellCommand(
- 'SRC=/this/file/can.be.read.with.su DEST=/sdcard/tmp/on.device;'
- 'cp "$SRC" "$DEST" && chmod 666 "$DEST"',
- shell=True, as_root=True, check_return=True),
- (self.call.adb.Pull('/sdcard/tmp/on.device',
- '/test/file/host/path'))):
- self.device.PullFile('/this/file/can.be.read.with.su',
- '/test/file/host/path', as_root=True)
-
- def testPullFile_asRootDoesntExistOnDevice(self):
- with mock.patch('os.path.exists', return_value=True):
- with self.assertCalls(
- (self.call.device.NeedsSU(), True),
- (self.call.device.PathExists('/data/app/test.file.does.not.exist',
- as_root=True), False)):
- with self.assertRaises(device_errors.CommandFailedError):
- self.device.PullFile('/data/app/test.file.does.not.exist',
- '/test/file/host/path', as_root=True)
-
class DeviceUtilsReadFileTest(DeviceUtilsTest):
@@ -2761,205 +2525,6 @@ class DeviceUtilsGetSetEnforce(DeviceUtilsTest):
self.device.SetEnforce(enabled='0') # Not recommended but it works!
-class DeviceUtilsGetWebViewUpdateServiceDumpTest(DeviceUtilsTest):
-
- def testGetWebViewUpdateServiceDump_success(self):
- # Some of the lines of adb shell dumpsys webviewupdate:
- dumpsys_lines = [
- 'Fallback logic enabled: true',
- ('Current WebView package (name, version): '
- '(com.android.chrome, 61.0.3163.98)'),
- 'Minimum WebView version code: 12345',
- 'WebView packages:',
- ('Valid package com.android.chrome (versionName: '
- '61.0.3163.98, versionCode: 1, targetSdkVersion: 26) is '
- 'installed/enabled for all users'),
- ('Valid package com.google.android.webview (versionName: '
- '58.0.3029.125, versionCode: 1, targetSdkVersion: 26) is NOT '
- 'installed/enabled for all users'),
- ('Invalid package com.google.android.apps.chrome (versionName: '
- '56.0.2924.122, versionCode: 2, targetSdkVersion: 25), reason: SDK '
- 'version too low'),
- ('com.chrome.canary is NOT installed.'),
- ]
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.OREO):
- with self.assertCall(
- self.call.adb.Shell('dumpsys webviewupdate'),
- '\n'.join(dumpsys_lines)):
- update = self.device.GetWebViewUpdateServiceDump()
- self.assertTrue(update['FallbackLogicEnabled'])
- self.assertEqual('com.android.chrome',
- update['CurrentWebViewPackage'])
- self.assertEqual(12345, update['MinimumWebViewVersionCode'])
- # Order isn't really important, and we shouldn't have duplicates, so we
- # convert to sets.
- expected = {
- 'com.android.chrome', 'com.google.android.webview',
- 'com.google.android.apps.chrome', 'com.chrome.canary'
- }
- self.assertSetEqual(expected, set(update['WebViewPackages'].keys()))
- self.assertEquals(
- 'is installed/enabled for all users',
- update['WebViewPackages']['com.android.chrome'])
- self.assertEquals(
- 'is NOT installed/enabled for all users',
- update['WebViewPackages']['com.google.android.webview'])
- self.assertEquals(
- 'reason: SDK version too low',
- update['WebViewPackages']['com.google.android.apps.chrome'])
- self.assertEquals(
- 'is NOT installed.',
- update['WebViewPackages']['com.chrome.canary'])
-
- def testGetWebViewUpdateServiceDump_missingkey(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.OREO):
- with self.assertCall(self.call.adb.Shell('dumpsys webviewupdate'),
- 'Fallback logic enabled: true'):
- with self.assertRaises(device_errors.CommandFailedError):
- self.device.GetWebViewUpdateServiceDump()
-
- def testGetWebViewUpdateServiceDump_noop(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.NOUGAT_MR1):
- with self.assertCalls():
- self.device.GetWebViewUpdateServiceDump()
-
- def testGetWebViewUpdateServiceDump_noPackage(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.OREO):
- with self.assertCall(self.call.adb.Shell('dumpsys webviewupdate'),
- 'Fallback logic enabled: true\n'
- 'Current WebView package is null'):
- update = self.device.GetWebViewUpdateServiceDump()
- self.assertEqual(True, update['FallbackLogicEnabled'])
- self.assertEqual(None, update['CurrentWebViewPackage'])
-
-
-class DeviceUtilsSetWebViewImplementationTest(DeviceUtilsTest):
-
- def testSetWebViewImplementation_success(self):
- with self.patch_call(
- self.call.device.GetApplicationPaths, return_value=['/any/path']):
- with self.assertCall(
- self.call.adb.Shell(
- 'cmd webviewupdate set-webview-implementation foo.org'),
- 'Success'):
- self.device.SetWebViewImplementation('foo.org')
-
- def testSetWebViewImplementation_uninstalled(self):
- with self.patch_call(self.call.device.GetApplicationPaths, return_value=[]):
- with self.assertRaises(device_errors.CommandFailedError) as cfe:
- self.device.SetWebViewImplementation('foo.org')
- self.assertIn('is not installed', cfe.exception.message)
-
- def _testSetWebViewImplementationHelper(self, mock_dump_sys,
- exception_message_substr):
- with self.patch_call(
- self.call.device.GetApplicationPaths, return_value=['/any/path']):
- with self.assertCall(
- self.call.adb.Shell(
- 'cmd webviewupdate set-webview-implementation foo.org'), 'Oops!'):
- with self.patch_call(
- self.call.device.GetWebViewUpdateServiceDump,
- return_value=mock_dump_sys):
- with self.assertRaises(device_errors.CommandFailedError) as cfe:
- self.device.SetWebViewImplementation('foo.org')
- self.assertIn(exception_message_substr, cfe.exception.message)
-
- def testSetWebViewImplementation_notInProviderList(self):
- mock_dump_sys = {
- 'WebViewPackages': {
- 'some.package': 'any reason',
- 'other.package': 'any reason',
- }
- }
- self._testSetWebViewImplementationHelper(mock_dump_sys, 'provider list')
-
- def testSetWebViewImplementation_notEnabled(self):
- mock_dump_sys = {
- 'WebViewPackages': {
- 'foo.org': 'is NOT installed/enabled for all users',
- }
- }
- self._testSetWebViewImplementationHelper(mock_dump_sys, 'is disabled')
-
- def testSetWebViewImplementation_missingManifestTag(self):
- mock_dump_sys = {
- 'WebViewPackages': {
- 'foo.org': 'No WebView-library manifest flag',
- }
- }
- self._testSetWebViewImplementationHelper(mock_dump_sys,
- 'WebView native library')
-
- def testSetWebViewImplementation_lowTargetSdkVersion(self):
- mock_dump_sys = {'WebViewPackages': {'foo.org': 'SDK version too low',}}
- with self.patch_call(self.call.device.build_version_sdk, return_value=26):
- self._testSetWebViewImplementationHelper(mock_dump_sys,
- 'higher targetSdkVersion')
-
- def testSetWebViewImplementation_lowVersionCode(self):
- mock_dump_sys = {
- 'MinimumWebViewVersionCode': 12345,
- 'WebViewPackages': {
- 'foo.org': 'Version code too low',
- }
- }
- self._testSetWebViewImplementationHelper(mock_dump_sys,
- 'higher versionCode')
-
- def testSetWebViewImplementation_invalidSignature(self):
- mock_dump_sys = {
- 'WebViewPackages': {
- 'foo.org': 'Incorrect signature',
- }
- }
- self._testSetWebViewImplementationHelper(mock_dump_sys,
- 'signed with release keys')
-
-
-class DeviceUtilsSetWebViewFallbackLogicTest(DeviceUtilsTest):
-
- def testSetWebViewFallbackLogic_False_success(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.NOUGAT):
- with self.assertCall(self.call.adb.Shell(
- 'cmd webviewupdate enable-redundant-packages'), 'Success'):
- self.device.SetWebViewFallbackLogic(False)
-
- def testSetWebViewFallbackLogic_True_success(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.NOUGAT):
- with self.assertCall(self.call.adb.Shell(
- 'cmd webviewupdate disable-redundant-packages'), 'Success'):
- self.device.SetWebViewFallbackLogic(True)
-
- def testSetWebViewFallbackLogic_failure(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.NOUGAT):
- with self.assertCall(self.call.adb.Shell(
- 'cmd webviewupdate enable-redundant-packages'), 'Oops!'):
- with self.assertRaises(device_errors.CommandFailedError):
- self.device.SetWebViewFallbackLogic(False)
-
- def testSetWebViewFallbackLogic_beforeNougat(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.MARSHMALLOW):
- with self.assertCalls():
- self.device.SetWebViewFallbackLogic(False)
-
- def testSetWebViewFallbackLogic_afterPie(self):
- # TODO(ntfschr): replace this with the Q constant when the SDK is public and
- # the codename is finalized.
- q_version_code = version_codes.PIE + 1
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=q_version_code):
- with self.assertCalls():
- self.device.SetWebViewFallbackLogic(False)
-
-
class DeviceUtilsTakeScreenshotTest(DeviceUtilsTest):
def testTakeScreenshot_fileNameProvided(self):
@@ -3034,7 +2599,7 @@ class DeviceUtilsClientCache(DeviceUtilsTest):
self.assertEqual(self.device._cache['test'], 0)
self.assertEqual(client_cache_one, {'test': 1})
self.assertEqual(client_cache_two, {'test': 2})
- self.device.ClearCache()
+ self.device._ClearCache()
self.assertTrue('test' not in self.device._cache)
self.assertEqual(client_cache_one, {})
self.assertEqual(client_cache_two, {})
@@ -3045,7 +2610,7 @@ class DeviceUtilsClientCache(DeviceUtilsTest):
client_cache_two = self.device.GetClientCache('ClientOne')
self.assertEqual(client_cache_one, {'test': 1})
self.assertEqual(client_cache_two, {'test': 1})
- self.device.ClearCache()
+ self.device._ClearCache()
self.assertEqual(client_cache_one, {})
self.assertEqual(client_cache_two, {})
@@ -3056,11 +2621,7 @@ class DeviceUtilsHealthyDevicesTest(mock_calls.TestCase):
test_serials = ['0123456789abcdef', 'fedcba9876543210']
with self.assertCalls(
(mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(),
- [_AdbWrapperMock(s) for s in test_serials]),
- (mock.call.devil.android.device_utils.DeviceUtils.GetABI(),
- abis.ARM),
- (mock.call.devil.android.device_utils.DeviceUtils.GetABI(),
- abis.ARM)):
+ [_AdbWrapperMock(s) for s in test_serials])):
blacklist = mock.NonCallableMock(**{'Read.return_value': []})
devices = device_utils.DeviceUtils.HealthyDevices(blacklist)
for serial, device in zip(test_serials, devices):
@@ -3071,9 +2632,7 @@ class DeviceUtilsHealthyDevicesTest(mock_calls.TestCase):
test_serials = ['0123456789abcdef', 'fedcba9876543210']
with self.assertCalls(
(mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(),
- [_AdbWrapperMock(s) for s in test_serials]),
- (mock.call.devil.android.device_utils.DeviceUtils.GetABI(),
- abis.ARM)):
+ [_AdbWrapperMock(s) for s in test_serials])):
blacklist = mock.NonCallableMock(
**{'Read.return_value': ['fedcba9876543210']})
devices = device_utils.DeviceUtils.HealthyDevices(blacklist)
@@ -3086,10 +2645,6 @@ class DeviceUtilsHealthyDevicesTest(mock_calls.TestCase):
with self.assertCalls(
(mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(),
[_AdbWrapperMock(s) for s in test_serials]),
- (mock.call.devil.android.device_utils.DeviceUtils.GetABI(),
- abis.ARM),
- (mock.call.devil.android.device_utils.DeviceUtils.GetABI(),
- abis.ARM),
(mock.call.devil.android.device_errors.MultipleDevicesError(mock.ANY),
_MockMultipleDevicesError())):
with self.assertRaises(_MockMultipleDevicesError):
@@ -3099,9 +2654,7 @@ class DeviceUtilsHealthyDevicesTest(mock_calls.TestCase):
test_serials = ['0123456789abcdef']
with self.assertCalls(
(mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(),
- [_AdbWrapperMock(s) for s in test_serials]),
- (mock.call.devil.android.device_utils.DeviceUtils.GetABI(),
- abis.ARM)):
+ [_AdbWrapperMock(s) for s in test_serials])):
devices = device_utils.DeviceUtils.HealthyDevices(device_arg=None)
self.assertEquals(1, len(devices))
@@ -3111,7 +2664,7 @@ class DeviceUtilsHealthyDevicesTest(mock_calls.TestCase):
(mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(),
[_AdbWrapperMock(s) for s in test_serials])):
with self.assertRaises(device_errors.NoDevicesError):
- device_utils.DeviceUtils.HealthyDevices(device_arg=None, retries=0)
+ device_utils.DeviceUtils.HealthyDevices(device_arg=None, retry=False)
def testHealthyDevices_noneDeviceArg_multiple_attached_ANDROID_SERIAL(self):
try:
@@ -3131,11 +2684,7 @@ class DeviceUtilsHealthyDevicesTest(mock_calls.TestCase):
test_serials = ['0123456789abcdef', 'fedcba9876543210']
with self.assertCalls(
(mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(),
- [_AdbWrapperMock(s) for s in test_serials]),
- (mock.call.devil.android.device_utils.DeviceUtils.GetABI(),
- abis.ARM),
- (mock.call.devil.android.device_utils.DeviceUtils.GetABI(),
- abis.ARM)):
+ [_AdbWrapperMock(s) for s in test_serials])):
devices = device_utils.DeviceUtils.HealthyDevices(device_arg=())
self.assertEquals(2, len(devices))
@@ -3154,47 +2703,17 @@ class DeviceUtilsHealthyDevicesTest(mock_calls.TestCase):
(mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(),
[_AdbWrapperMock(s) for s in test_serials])):
with self.assertRaises(device_errors.NoDevicesError):
- device_utils.DeviceUtils.HealthyDevices(device_arg=[], retries=0)
-
- @mock.patch('time.sleep')
- @mock.patch('devil.android.device_utils.RestartServer')
- def testHealthyDevices_EmptyListDeviceArg_no_attached_with_retry(
- self, mock_restart, mock_sleep):
- with self.assertCalls(
- (mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(), []),
- (mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(), []),
- (mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(), []),
- (mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(), []),
- (mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(), [])):
- with self.assertRaises(device_errors.NoDevicesError):
- device_utils.DeviceUtils.HealthyDevices(device_arg=[], retries=4)
- self.assertEquals(mock_restart.call_count, 4)
- self.assertEquals(mock_sleep.call_args_list, [
- mock.call(2), mock.call(4), mock.call(8), mock.call(16)])
-
- @mock.patch('time.sleep')
- @mock.patch('devil.android.device_utils.RestartServer')
- def testHealthyDevices_EmptyListDeviceArg_no_attached_with_resets(
- self, mock_restart, mock_sleep):
- # The reset_usb import fails on windows. Mock the full import here so it can
- # succeed like it would on linux.
- mock_reset_import = mock.MagicMock()
- sys.modules['devil.utils.reset_usb'] = mock_reset_import
- with self.assertCalls(
- (mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(), []),
- (mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(), []),
- (mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(), []),
- (mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(), []),
- (mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(), [])):
+ device_utils.DeviceUtils.HealthyDevices(device_arg=[], retry=False)
+
+ def testHealthyDevices_EmptyListDeviceArg_no_attached_with_retry(self):
+ test_serials = []
+ with self.assertCalls(
+ (mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(),
+ [_AdbWrapperMock(s) for s in test_serials]),
+ (mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(),
+ [_AdbWrapperMock(s) for s in test_serials])):
with self.assertRaises(device_errors.NoDevicesError):
- with mock.patch.object(
- mock_reset_import, 'reset_all_android_devices') as mock_reset:
- device_utils.DeviceUtils.HealthyDevices(device_arg=[], retries=4,
- enable_usb_resets=True)
- self.assertEquals(mock_reset.call_count, 1)
- self.assertEquals(mock_restart.call_count, 4)
- self.assertEquals(mock_sleep.call_args_list, [
- mock.call(2), mock.call(4), mock.call(8), mock.call(16)])
+ device_utils.DeviceUtils.HealthyDevices(device_arg=[], retry=True)
def testHealthyDevices_ListDeviceArg(self):
device_arg = ['0123456789abcdef', 'fedcba9876543210']
@@ -3206,33 +2725,6 @@ class DeviceUtilsHealthyDevicesTest(mock_calls.TestCase):
del os.environ['ANDROID_SERIAL']
self.assertEquals(2, len(devices))
- def testHealthyDevices_abisArg_no_matching_abi(self):
- test_serials = ['0123456789abcdef', 'fedcba9876543210']
- with self.assertCalls(
- (mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(),
- [_AdbWrapperMock(s) for s in test_serials]),
- (mock.call.devil.android.device_utils.DeviceUtils.GetABI(),
- abis.ARM),
- (mock.call.devil.android.device_utils.DeviceUtils.GetABI(),
- abis.ARM)):
- with self.assertRaises(device_errors.NoDevicesError):
- device_utils.DeviceUtils.HealthyDevices(device_arg=[], retries=0,
- abis=[abis.ARM_64])
-
- def testHealthyDevices_abisArg_filter_on_abi(self):
- test_serials = ['0123456789abcdef', 'fedcba9876543210']
- with self.assertCalls(
- (mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(),
- [_AdbWrapperMock(s) for s in test_serials]),
- (mock.call.devil.android.device_utils.DeviceUtils.GetABI(),
- abis.ARM_64),
- (mock.call.devil.android.device_utils.DeviceUtils.GetABI(),
- abis.ARM)):
- devices = device_utils.DeviceUtils.HealthyDevices(device_arg=[],
- retries=0,
- abis=[abis.ARM_64])
- self.assertEquals(1, len(devices))
-
class DeviceUtilsRestartAdbdTest(DeviceUtilsTest):
@@ -3475,69 +2967,6 @@ class DeviceUtilsGetIMEITest(DeviceUtilsTest):
self.device.GetIMEI()
-class DeviceUtilsChangeOwner(DeviceUtilsTest):
-
- def testChangeOwner(self):
- with self.assertCalls(
- (self.call.device.RunShellCommand(
- ['chown', 'user.group', '/path/to/file1', 'file2'],
- check_return=True))):
- self.device.ChangeOwner('user.group', ['/path/to/file1', 'file2'])
-
-
-class DeviceUtilsChangeSecurityContext(DeviceUtilsTest):
-
- def testChangeSecurityContext(self):
- with self.assertCalls(
- (self.call.device.RunShellCommand(
- ['chcon', 'u:object_r:system_data_file:s0', '/path', '/path2'],
- as_root=device_utils._FORCE_SU, check_return=True))):
- self.device.ChangeSecurityContext('u:object_r:system_data_file:s0',
- ['/path', '/path2'])
-
-
-class DeviceUtilsLocale(DeviceUtilsTest):
-
- def testLocaleLegacy(self):
- with self.assertCalls(
- (self.call.device.GetProp('persist.sys.locale', cache=False), ''),
- (self.call.device.GetProp('persist.sys.language', cache=False), 'en'),
- (self.call.device.GetProp('persist.sys.country', cache=False), 'US')):
- self.assertEquals(self.device.GetLocale(), ('en', 'US'))
-
- def testLocale(self):
- with self.assertCalls(
- (self.call.device.GetProp('persist.sys.locale', cache=False), 'en-US'),
- (self.call.device.GetProp('persist.sys.locale', cache=False),
- 'en-US-sw')):
- self.assertEquals(self.device.GetLocale(), ('en', 'US'))
- self.assertEquals(self.device.GetLocale(), ('en', 'US-sw'))
-
- def testBadLocale(self):
- with self.assertCalls(
- (self.call.device.GetProp('persist.sys.locale', cache=False), 'en')):
- self.assertEquals(self.device.GetLocale(), ('', ''))
-
-
- def testLanguageAndCountryLegacy(self):
- with self.assertCalls(
- (self.call.device.GetProp('persist.sys.locale', cache=False), ''),
- (self.call.device.GetProp('persist.sys.language', cache=False), 'en'),
- (self.call.device.GetProp('persist.sys.country', cache=False), 'US'),
- (self.call.device.GetProp('persist.sys.locale', cache=False), ''),
- (self.call.device.GetProp('persist.sys.language', cache=False), 'en'),
- (self.call.device.GetProp('persist.sys.country', cache=False), 'US')):
- self.assertEquals(self.device.GetLanguage(), 'en')
- self.assertEquals(self.device.GetCountry(), 'US')
-
- def testLanguageAndCountry(self):
- with self.assertCalls(
- (self.call.device.GetProp('persist.sys.locale', cache=False), 'en-US'),
- (self.call.device.GetProp('persist.sys.locale', cache=False), 'en-US')):
- self.assertEquals(self.device.GetLanguage(), 'en')
- self.assertEquals(self.device.GetCountry(), 'US')
-
-
if __name__ == '__main__':
logging.getLogger().setLevel(logging.DEBUG)
unittest.main(verbosity=2)
diff --git a/systrace/catapult/devil/devil/android/fastboot_utils.py b/systrace/catapult/devil/devil/android/fastboot_utils.py
index 3621d7f..3bd3ee8 100644
--- a/systrace/catapult/devil/devil/android/fastboot_utils.py
+++ b/systrace/catapult/devil/devil/android/fastboot_utils.py
@@ -108,7 +108,7 @@ class FastbootUtils(object):
This waits for the device serial to show up in fastboot devices output.
"""
def fastboot_mode():
- return any(self._serial == str(d) for d in self.fastboot.Devices())
+ return self._serial in self.fastboot.Devices()
timeout_retry.WaitFor(fastboot_mode, wait_period=self._FASTBOOT_WAIT_TIME)
diff --git a/systrace/catapult/devil/devil/android/flag_changer.py b/systrace/catapult/devil/devil/android/flag_changer.py
index 110cf82..0055e23 100644
--- a/systrace/catapult/devil/devil/android/flag_changer.py
+++ b/systrace/catapult/devil/devil/android/flag_changer.py
@@ -52,14 +52,12 @@ class FlagChanger(object):
once the tests have completed.
"""
- def __init__(self, device, cmdline_file, use_legacy_path=False):
+ def __init__(self, device, cmdline_file):
"""Initializes the FlagChanger and records the original arguments.
Args:
device: A DeviceUtils instance.
cmdline_file: Name of the command line file where to store flags.
- use_legacy_path: Whether to use the legacy commandline path (needed for
- M54 and earlier)
"""
self._device = device
self._should_reset_enforce = False
@@ -68,20 +66,13 @@ class FlagChanger(object):
raise ValueError(
'cmdline_file should be a file name only, do not include path'
' separators in: %s' % cmdline_file)
- cmdline_path = posixpath.join(_CMDLINE_DIR, cmdline_file)
- alternate_cmdline_path = posixpath.join(_CMDLINE_DIR_LEGACY, cmdline_file)
+ self._cmdline_path = posixpath.join(_CMDLINE_DIR, cmdline_file)
- if use_legacy_path:
- cmdline_path, alternate_cmdline_path = (
- alternate_cmdline_path, cmdline_path)
- if not self._device.HasRoot():
- raise ValueError('use_legacy_path requires a rooted device')
- self._cmdline_path = cmdline_path
-
- if self._device.PathExists(alternate_cmdline_path):
+ cmdline_path_legacy = posixpath.join(_CMDLINE_DIR_LEGACY, cmdline_file)
+ if self._device.PathExists(cmdline_path_legacy):
logger.warning(
- 'Removing alternate command line file %r.', alternate_cmdline_path)
- self._device.RemovePath(alternate_cmdline_path, as_root=True)
+ 'Removing legacy command line file %r.', cmdline_path_legacy)
+ self._device.RemovePath(cmdline_path_legacy, as_root=True)
self._state_stack = [None] # Actual state is set by GetCurrentFlags().
self.GetCurrentFlags()
@@ -95,8 +86,7 @@ class FlagChanger(object):
A list of flags.
"""
if self._device.PathExists(self._cmdline_path):
- command_line = self._device.ReadFile(
- self._cmdline_path, as_root=True).strip()
+ command_line = self._device.ReadFile(self._cmdline_path).strip()
else:
command_line = ''
flags = _ParseFlags(command_line)
@@ -105,7 +95,7 @@ class FlagChanger(object):
self._state_stack[-1] = set(flags)
return flags
- def ReplaceFlags(self, flags, log_flags=True):
+ def ReplaceFlags(self, flags):
"""Replaces the flags in the command line with the ones provided.
Saves the current flags state on the stack, so a call to Restore will
change the state back to the one preceeding the call to ReplaceFlags.
@@ -121,7 +111,7 @@ class FlagChanger(object):
new_flags = set(flags)
self._state_stack.append(new_flags)
self._SetPermissive()
- return self._UpdateCommandLineFile(log_flags=log_flags)
+ return self._UpdateCommandLineFile()
def AddFlags(self, flags):
"""Appends flags to the command line if they aren't already there.
@@ -181,14 +171,10 @@ class FlagChanger(object):
"""Set SELinux to permissive, if needed.
On Android N and above this is needed in order to allow Chrome to read the
- legacy command line file.
+ command line file.
TODO(crbug.com/699082): Remove when a better solution exists.
"""
- # TODO(crbug.com/948578): figure out the exact scenarios where the lowered
- # permissions are needed, and document them in the code.
- if not self._device.HasRoot():
- return
if (self._device.build_version_sdk >= version_codes.NOUGAT and
self._device.GetEnforce()):
self._device.SetEnforce(enabled=False)
@@ -209,13 +195,13 @@ class FlagChanger(object):
"""
# The initial state must always remain on the stack.
assert len(self._state_stack) > 1, (
- 'Mismatch between calls to Add/RemoveFlags and Restore')
+ "Mismatch between calls to Add/RemoveFlags and Restore")
self._state_stack.pop()
if len(self._state_stack) == 1:
self._ResetEnforce()
return self._UpdateCommandLineFile()
- def _UpdateCommandLineFile(self, log_flags=True):
+ def _UpdateCommandLineFile(self):
"""Writes out the command line to the file, or removes it if empty.
Returns:
@@ -223,15 +209,13 @@ class FlagChanger(object):
"""
command_line = _SerializeFlags(self._state_stack[-1])
if command_line is not None:
- self._device.WriteFile(self._cmdline_path, command_line, as_root=True)
+ self._device.WriteFile(self._cmdline_path, command_line)
else:
- self._device.RemovePath(self._cmdline_path, force=True, as_root=True)
+ self._device.RemovePath(self._cmdline_path, force=True)
- flags = self.GetCurrentFlags()
- logging.info('Flags now written on the device to %s', self._cmdline_path)
- if log_flags:
- logging.info('Flags: %s', flags)
- return flags
+ current_flags = self.GetCurrentFlags()
+ logger.info('Flags now set on the device: %s', current_flags)
+ return current_flags
def _ParseFlags(line):
@@ -253,7 +237,6 @@ def _ParseFlags(line):
current_quote = None
current_flag = None
- # pylint: disable=unsubscriptable-object
for c in line:
# Detect start or end of quote block.
if (current_quote is None and c in _QUOTES) or c == current_quote:
diff --git a/systrace/catapult/devil/devil/android/flag_changer_test.py b/systrace/catapult/devil/devil/android/flag_changer_test.py
index dbe6fac..5342cf4 100755
--- a/systrace/catapult/devil/devil/android/flag_changer_test.py
+++ b/systrace/catapult/devil/devil/android/flag_changer_test.py
@@ -42,7 +42,7 @@ class FlagChangerTest(unittest.TestCase):
self.cmdline_path_legacy = posixpath.join(
flag_changer._CMDLINE_DIR_LEGACY, _CMDLINE_FILE)
- def testFlagChanger_removeAlternateCmdLine(self):
+ def testFlagChanger_removeLegacyCmdLine(self):
self.device.WriteFile(self.cmdline_path_legacy, 'chrome --old --stuff')
self.assertTrue(self.device.PathExists(self.cmdline_path_legacy))
@@ -52,17 +52,6 @@ class FlagChangerTest(unittest.TestCase):
self.cmdline_path)
self.assertFalse(self.device.PathExists(self.cmdline_path_legacy))
- def testFlagChanger_removeAlternateCmdLineLegacyPath(self):
- self.device.WriteFile(self.cmdline_path, 'chrome --old --stuff')
- self.assertTrue(self.device.PathExists(self.cmdline_path))
-
- changer = flag_changer.FlagChanger(self.device, 'chrome-command-line',
- use_legacy_path=True)
- self.assertEquals(
- changer._cmdline_path, # pylint: disable=protected-access
- self.cmdline_path_legacy)
- self.assertFalse(self.device.PathExists(self.cmdline_path))
-
def testFlagChanger_mustBeFileName(self):
with self.assertRaises(ValueError):
flag_changer.FlagChanger(self.device, '/data/local/chrome-command-line')
diff --git a/systrace/catapult/devil/devil/android/forwarder.py b/systrace/catapult/devil/devil/android/forwarder.py
index 6be4651..cf1fbe1 100644
--- a/systrace/catapult/devil/devil/android/forwarder.py
+++ b/systrace/catapult/devil/devil/android/forwarder.py
@@ -306,7 +306,7 @@ class Forwarder(object):
instance = Forwarder._GetInstanceLocked(None)
serial = str(device)
serial_with_port = (serial, device_port)
- if serial_with_port not in instance._device_to_host_port_map:
+ if not serial_with_port in instance._device_to_host_port_map:
logger.error('Trying to unmap non-forwarded port %d', device_port)
return
diff --git a/systrace/catapult/devil/devil/android/logcat_monitor.py b/systrace/catapult/devil/devil/android/logcat_monitor.py
index b5f796b..249320b 100644
--- a/systrace/catapult/devil/devil/android/logcat_monitor.py
+++ b/systrace/catapult/devil/devil/android/logcat_monitor.py
@@ -31,7 +31,7 @@ class LogcatMonitor(object):
r'(?P<log_level>%s) +(?P<component>%s) *: +(?P<message>%s)$')
def __init__(self, adb, clear=True, filter_specs=None, output_file=None,
- transform_func=None, check_error=True):
+ transform_func=None):
"""Create a LogcatMonitor instance.
Args:
@@ -41,14 +41,11 @@ class LogcatMonitor(object):
output_file: File path to save recorded logcat.
transform_func: An optional unary callable that takes and returns
a list of lines, possibly transforming them in the process.
- check_error: Check for and raise an exception on nonzero exit codes
- from the underlying logcat command.
"""
if isinstance(adb, adb_wrapper.AdbWrapper):
self._adb = adb
else:
raise ValueError('Unsupported type passed for argument "device"')
- self._check_error = check_error
self._clear = clear
self._filter_specs = filter_specs
self._output_file = output_file
@@ -171,11 +168,9 @@ class LogcatMonitor(object):
def record_to_file():
# Write the log with line buffering so the consumer sees each individual
# line.
- for data in self._adb.Logcat(
- filter_specs=self._filter_specs,
- logcat_format='threadtime',
- iter_timeout=self._RECORD_ITER_TIMEOUT,
- check_error=self._check_error):
+ for data in self._adb.Logcat(filter_specs=self._filter_specs,
+ logcat_format='threadtime',
+ iter_timeout=self._RECORD_ITER_TIMEOUT):
if self._stop_recording_event.isSet():
return
diff --git a/systrace/catapult/devil/devil/android/md5sum.py b/systrace/catapult/devil/devil/android/md5sum.py
index f5b6f3c..6dece9e 100644
--- a/systrace/catapult/devil/devil/android/md5sum.py
+++ b/systrace/catapult/devil/devil/android/md5sum.py
@@ -89,8 +89,7 @@ def CalculateDeviceMd5Sums(paths, device):
# Note: ":" is equivalent to "true".
md5sum_script += ';:'
try:
- out = device.RunShellCommand(
- md5sum_script, shell=True, check_return=True, large_output=True)
+ out = device.RunShellCommand(md5sum_script, shell=True, check_return=True)
except device_errors.AdbShellCommandFailedError as e:
# Push the binary only if it is found to not exist
# (faster than checking up-front).
@@ -107,8 +106,7 @@ def CalculateDeviceMd5Sums(paths, device):
device.RunShellCommand(mkdir_cmd, shell=True, check_return=True)
device.adb.Push(md5sum_dist_bin_path, MD5SUM_DEVICE_BIN_PATH)
- out = device.RunShellCommand(
- md5sum_script, shell=True, check_return=True, large_output=True)
+ out = device.RunShellCommand(md5sum_script, shell=True, check_return=True)
else:
raise
diff --git a/systrace/catapult/devil/devil/android/ndk/__init__.py b/systrace/catapult/devil/devil/android/ndk/__init__.py
deleted file mode 100644
index edd8dbc..0000000
--- a/systrace/catapult/devil/devil/android/ndk/__init__.py
+++ /dev/null
@@ -1,6 +0,0 @@
-# Copyright 2019 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.
-
-# This package is intended for modules that are very tightly coupled to
-# tools or APIs from the Android NDK.
diff --git a/systrace/catapult/devil/devil/android/ndk/abis.py b/systrace/catapult/devil/devil/android/ndk/abis.py
deleted file mode 100644
index dd32f7c..0000000
--- a/systrace/catapult/devil/devil/android/ndk/abis.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright 2019 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.
-
-"""Android NDK ABIs.
-
-https://developer.android.com/ndk/guides/abis
-
-These constants can be compared against the value of
-devil.android.DeviceUtils.product_cpu_abi.
-"""
-
-ARM = 'armeabi-v7a'
-ARM_64 = 'arm64-v8a'
-X86 = 'x86'
-X86_64 = 'x86_64'
diff --git a/systrace/catapult/devil/devil/android/perf/perf_control.py b/systrace/catapult/devil/devil/android/perf/perf_control.py
index 1226be8..06a5db6 100644
--- a/systrace/catapult/devil/devil/android/perf/perf_control.py
+++ b/systrace/catapult/devil/devil/android/perf/perf_control.py
@@ -9,116 +9,6 @@ import re
from devil.android import device_errors
logger = logging.getLogger(__name__)
-_atexit_messages = set()
-
-
-# Defines how to switch between the default performance configuration
-# ('default_mode') and the mode for use when benchmarking ('high_perf_mode').
-# For devices not in the list the defaults are to set up the scaling governor to
-# 'performance' and reset it back to 'ondemand' when benchmarking is finished.
-#
-# The 'default_mode_governor' is mandatory to define, while
-# 'high_perf_mode_governor' is not taken into account. The latter is because the
-# governor 'performance' is currently used for all benchmarking on all devices.
-#
-# TODO(crbug.com/383566): Add definitions for all devices used in the perf
-# waterfall.
-_PERFORMANCE_MODE_DEFINITIONS = {
- # Fire TV Edition - 4K
- 'AFTKMST12': {
- 'default_mode_governor': 'interactive',
- },
- # Pixel 3
- 'blueline': {
- 'high_perf_mode': {
- 'bring_cpu_cores_online': True,
- # The SoC is Arm big.LITTLE. The cores 0..3 are LITTLE, the 4..7 are big.
- 'cpu_max_freq': {'0..3': 1228800, '4..7': 1536000},
- 'gpu_max_freq': 520000000,
- },
- 'default_mode': {
- 'cpu_max_freq': {'0..3': 1766400, '4..7': 2649600},
- 'gpu_max_freq': 710000000,
- },
- 'default_mode_governor': 'schedutil',
- },
- 'GT-I9300': {
- 'default_mode_governor': 'pegasusq',
- },
- 'Galaxy Nexus': {
- 'default_mode_governor': 'interactive',
- },
- # Pixel
- 'msm8996': {
- 'high_perf_mode': {
- 'bring_cpu_cores_online': True,
- 'cpu_max_freq': 1209600,
- 'gpu_max_freq': 315000000,
- },
- 'default_mode': {
- # The SoC is Arm big.LITTLE. The cores 0..1 are LITTLE, the 2..3 are big.
- 'cpu_max_freq': {'0..1': 1593600, '2..3': 2150400},
- 'gpu_max_freq': 624000000,
- },
- 'default_mode_governor': 'sched',
- },
- 'Nexus 7': {
- 'default_mode_governor': 'interactive',
- },
- 'Nexus 10': {
- 'default_mode_governor': 'interactive',
- },
- 'Nexus 4': {
- 'high_perf_mode': {
- 'bring_cpu_cores_online': True,
- },
- 'default_mode_governor': 'ondemand',
- },
- 'Nexus 5': {
- # The list of possible GPU frequency values can be found in:
- # /sys/class/kgsl/kgsl-3d0/gpu_available_frequencies.
- # For CPU cores the possible frequency values are at:
- # /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies
- 'high_perf_mode': {
- 'bring_cpu_cores_online': True,
- 'cpu_max_freq': 1190400,
- 'gpu_max_freq': 200000000,
- },
- 'default_mode': {
- 'cpu_max_freq': 2265600,
- 'gpu_max_freq': 450000000,
- },
- 'default_mode_governor': 'ondemand',
- },
- 'Nexus 5X': {
- 'high_perf_mode': {
- 'bring_cpu_cores_online': True,
- 'cpu_max_freq': 1248000,
- 'gpu_max_freq': 300000000,
- },
- 'default_mode': {
- 'governor': 'ondemand',
- # The SoC is ARM big.LITTLE. The cores 4..5 are big, the 0..3 are LITTLE.
- 'cpu_max_freq': {'0..3': 1440000, '4..5': 1824000},
- 'gpu_max_freq': 600000000,
- },
- 'default_mode_governor': 'ondemand',
- },
-}
-
-def _GetPerfModeDefinitions(product_model):
- if product_model.startswith('AOSP on '):
- product_model = product_model.replace('AOSP on ', '')
- return _PERFORMANCE_MODE_DEFINITIONS.get(product_model)
-
-def _NoisyWarning(message):
- message += ' Results may be NOISY!!'
- logger.warning(message)
- # Add an additional warning at exit, such that it's clear that any results
- # may be different/noisy (due to the lack of intended performance mode).
- if message not in _atexit_messages:
- _atexit_messages.add(message)
- atexit.register(logger.warning, message)
class PerfControl(object):
@@ -131,10 +21,10 @@ class PerfControl(object):
def __init__(self, device):
self._device = device
- self._cpu_files = []
- for file_name in self._device.ListDirectory(self._CPU_PATH, as_root=True):
- if self._CPU_FILE_PATTERN.match(file_name):
- self._cpu_files.append(file_name)
+ self._cpu_files = [
+ filename
+ for filename in self._device.ListDirectory(self._CPU_PATH, as_root=True)
+ if self._CPU_FILE_PATTERN.match(filename)]
assert self._cpu_files, 'Failed to detect CPUs.'
self._cpu_file_list = ' '.join(self._cpu_files)
logger.info('CPUs found: %s', self._cpu_file_list)
@@ -146,73 +36,34 @@ class PerfControl(object):
(cpu, raw_governors.strip().split() if not exit_code else None)
for cpu, raw_governors, exit_code in raw]
- def _SetMaxFrequenciesFromMode(self, mode):
- """Set maximum frequencies for GPU and CPU cores.
-
- Args:
- mode: A dictionary mapping optional keys 'cpu_max_freq' and 'gpu_max_freq'
- to integer values of frequency supported by the device.
- """
- cpu_max_freq = mode.get('cpu_max_freq')
- if cpu_max_freq:
- if not isinstance(cpu_max_freq, dict):
- self._SetScalingMaxFreqForCpus(cpu_max_freq, self._cpu_file_list)
- else:
- for key, max_frequency in cpu_max_freq.iteritems():
- # Convert 'X' to 'cpuX' and 'X..Y' to 'cpuX cpu<X+1> .. cpuY'.
- if '..' in key:
- range_min, range_max = key.split('..')
- range_min, range_max = int(range_min), int(range_max)
- else:
- range_min = range_max = int(key)
- cpu_files = ['cpu%d' % number
- for number in xrange(range_min, range_max + 1)]
- # Set the |max_frequency| on requested subset of the cores.
- self._SetScalingMaxFreqForCpus(max_frequency, ' '.join(cpu_files))
- gpu_max_freq = mode.get('gpu_max_freq')
- if gpu_max_freq:
- self._SetMaxGpuClock(gpu_max_freq)
-
def SetHighPerfMode(self):
"""Sets the highest stable performance mode for the device."""
try:
self._device.EnableRoot()
except device_errors.CommandFailedError:
- _NoisyWarning('Need root for performance mode.')
+ message = 'Need root for performance mode. Results may be NOISY!!'
+ logger.warning(message)
+ # Add an additional warning at exit, such that it's clear that any results
+ # may be different/noisy (due to the lack of intended performance mode).
+ atexit.register(logger.warning, message)
return
- mode_definitions = _GetPerfModeDefinitions(self._device.product_model)
- if not mode_definitions:
- self.SetScalingGovernor('performance')
- return
- high_perf_mode = mode_definitions.get('high_perf_mode')
- if not high_perf_mode:
+
+ product_model = self._device.product_model
+ # TODO(epenner): Enable on all devices (http://crbug.com/383566)
+ if 'Nexus 4' == product_model:
+ self._ForceAllCpusOnline(True)
+ if not self._AllCpusAreOnline():
+ logger.warning('Failed to force CPUs online. Results may be NOISY!')
self.SetScalingGovernor('performance')
- return
- if high_perf_mode.get('bring_cpu_cores_online', False):
+ elif 'Nexus 5' == product_model:
self._ForceAllCpusOnline(True)
if not self._AllCpusAreOnline():
- _NoisyWarning('Failed to force CPUs online.')
- # Scaling governor must be set _after_ bringing all CPU cores online,
- # otherwise it would not affect the cores that are currently offline.
- self.SetScalingGovernor('performance')
- self._SetMaxFrequenciesFromMode(high_perf_mode)
-
- def SetDefaultPerfMode(self):
- """Sets the performance mode for the device to its default mode."""
- if not self._device.HasRoot():
- return
- mode_definitions = _GetPerfModeDefinitions(self._device.product_model)
- if not mode_definitions:
- self.SetScalingGovernor('ondemand')
+ logger.warning('Failed to force CPUs online. Results may be NOISY!')
+ self.SetScalingGovernor('performance')
+ self._SetScalingMaxFreq(1190400)
+ self._SetMaxGpuClock(200000000)
else:
- default_mode_governor = mode_definitions.get('default_mode_governor')
- assert default_mode_governor, ('Default mode governor must be provided '
- 'for all perf mode definitions.')
- self.SetScalingGovernor(default_mode_governor)
- default_mode = mode_definitions.get('default_mode')
- if default_mode:
- self._SetMaxFrequenciesFromMode(default_mode)
- self._ForceAllCpusOnline(False)
+ self.SetScalingGovernor('performance')
def SetPerfProfilingMode(self):
"""Enables all cores for reliable perf profiling."""
@@ -223,6 +74,27 @@ class PerfControl(object):
raise RuntimeError('Need root to force CPUs online.')
raise RuntimeError('Failed to force CPUs online.')
+ def SetDefaultPerfMode(self):
+ """Sets the performance mode for the device to its default mode."""
+ if not self._device.HasRoot():
+ return
+ product_model = self._device.product_model
+ if 'Nexus 5' == product_model:
+ if self._AllCpusAreOnline():
+ self._SetScalingMaxFreq(2265600)
+ self._SetMaxGpuClock(450000000)
+
+ governor_mode = {
+ 'GT-I9300': 'pegasusq',
+ 'Galaxy Nexus': 'interactive',
+ 'Nexus 4': 'ondemand',
+ 'Nexus 5': 'ondemand',
+ 'Nexus 7': 'interactive',
+ 'Nexus 10': 'interactive'
+ }.get(product_model, 'ondemand')
+ self.SetScalingGovernor(governor_mode)
+ self._ForceAllCpusOnline(False)
+
def GetCpuInfo(self):
online = (output.rstrip() == '1' and status == 0
for (_, output, status) in self._ForEachCpu('cat "$CPU/online"'))
@@ -231,24 +103,9 @@ class PerfControl(object):
in self._ForEachCpu('cat "$CPU/cpufreq/scaling_governor"'))
return zip(self._cpu_files, online, governor)
- def _ForEachCpu(self, cmd, cpu_list=None):
- """Runs a command on the device for each of the CPUs.
-
- Args:
- cmd: A string with a shell command, may may use shell expansion: "$CPU" to
- refer to the current CPU in the string form (e.g. "cpu0", "cpu1",
- and so on).
- cpu_list: A space-separated string of CPU core names, like in the example
- above
- Returns:
- A list of tuples in the form (cpu_string, command_output, exit_code), one
- tuple per each command invocation. As usual, all lines of the output
- command are joined into one line with spaces.
- """
- if cpu_list is None:
- cpu_list = self._cpu_file_list
+ def _ForEachCpu(self, cmd):
script = '; '.join([
- 'for CPU in %s' % cpu_list,
+ 'for CPU in %s' % self._cpu_file_list,
'do %s' % cmd,
'echo -n "%~%$?%~%"',
'done'
@@ -258,20 +115,20 @@ class PerfControl(object):
output = '\n'.join(output).split('%~%')
return zip(self._cpu_files, output[0::2], (int(c) for c in output[1::2]))
- def _ConditionallyWriteCpuFiles(self, path, value, cpu_files, condition):
+ def _WriteEachCpuFile(self, path, value):
+ self._ConditionallyWriteEachCpuFile(path, value, condition='true')
+
+ def _ConditionallyWriteEachCpuFile(self, path, value, condition):
template = (
'{condition} && test -e "$CPU/{path}" && echo {value} > "$CPU/{path}"')
results = self._ForEachCpu(
- template.format(path=path, value=value, condition=condition), cpu_files)
+ template.format(path=path, value=value, condition=condition))
cpus = ' '.join(cpu for (cpu, _, status) in results if status == 0)
if cpus:
logger.info('Successfully set %s to %r on: %s', path, value, cpus)
else:
logger.warning('Failed to set %s to %r on any cpus', path, value)
- def _WriteCpuFiles(self, path, value, cpu_files):
- self._ConditionallyWriteCpuFiles(path, value, cpu_files, condition='true')
-
def _ReadEachCpuFile(self, path):
return self._ForEachCpu(
'cat "$CPU/{path}"'.format(path=path))
@@ -288,8 +145,8 @@ class PerfControl(object):
condition = 'test -e "{path}" && grep -q {value} {path}'.format(
path=('${CPU}/%s' % self._AVAILABLE_GOVERNORS_REL_PATH),
value=value)
- self._ConditionallyWriteCpuFiles(
- 'cpufreq/scaling_governor', value, self._cpu_file_list, condition)
+ self._ConditionallyWriteEachCpuFile(
+ 'cpufreq/scaling_governor', value, condition)
def GetScalingGovernor(self):
"""Gets the currently set governor for each CPU.
@@ -312,8 +169,8 @@ class PerfControl(object):
"""
return self._available_governors
- def _SetScalingMaxFreqForCpus(self, value, cpu_files):
- self._WriteCpuFiles('cpufreq/scaling_max_freq', '%d' % value, cpu_files)
+ def _SetScalingMaxFreq(self, value):
+ self._WriteEachCpuFile('cpufreq/scaling_max_freq', '%d' % value)
def _SetMaxGpuClock(self, value):
self._device.WriteFile('/sys/class/kgsl/kgsl-3d0/max_gpuclk',
@@ -322,9 +179,8 @@ class PerfControl(object):
def _AllCpusAreOnline(self):
results = self._ForEachCpu('cat "$CPU/online"')
- # The file 'cpu0/online' is missing on some devices (example: Nexus 9). This
- # is likely because on these devices it is impossible to bring the cpu0
- # offline. Assuming the same for all devices until proven otherwise.
+ # TODO(epenner): Investigate why file may be missing
+ # (http://crbug.com/397118)
return all(output.rstrip() == '1' and status == 0
for (cpu, output, status) in results
if cpu != 'cpu0')
diff --git a/systrace/catapult/devil/devil/android/perf/perf_control_test.py b/systrace/catapult/devil/devil/android/perf/perf_control_test.py
deleted file mode 100644
index 3832424..0000000
--- a/systrace/catapult/devil/devil/android/perf/perf_control_test.py
+++ /dev/null
@@ -1,105 +0,0 @@
-# Copyright 2018 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 unittest
-
-from devil import devil_env
-from devil.android import device_utils
-from devil.android.perf import perf_control
-from devil.android.sdk import adb_wrapper
-
-with devil_env.SysPath(devil_env.PYMOCK_PATH):
- import mock
-
-
-# pylint: disable=unused-argument
-def _ShellCommandHandler(cmd, shell=False, check_return=False,
- cwd=None, env=None, run_as=None, as_root=False, single_line=False,
- large_output=False, raw_output=False, timeout=None, retries=None):
- if cmd.startswith('for CPU in '):
- if 'scaling_available_governors' in cmd:
- contents = 'interactive ondemand userspace powersave performance'
- return [contents + '\n%~%0%~%'] * 4
- if 'cat "$CPU/online"' in cmd:
- return ['1\n%~%0%~%'] * 4
- assert False, 'Should not be called with cmd: {}'.format(cmd)
-
-
-class PerfControlTest(unittest.TestCase):
- @staticmethod
- def _MockOutLowLevelPerfControlMethods(perf_control_object):
- # pylint: disable=protected-access
- perf_control_object.SetScalingGovernor = mock.Mock()
- perf_control_object._ForceAllCpusOnline = mock.Mock()
- perf_control_object._SetScalingMaxFreqForCpus = mock.Mock()
- perf_control_object._SetMaxGpuClock = mock.Mock()
-
- # pylint: disable=no-self-use
- def testNexus5HighPerfMode(self):
- # Mock out the device state for PerfControl.
- cpu_list = ['cpu%d' % cpu for cpu in xrange(4)]
- mock_device = mock.Mock(spec=device_utils.DeviceUtils)
- mock_device.product_model = 'Nexus 5'
- mock_device.adb = mock.Mock(spec=adb_wrapper.AdbWrapper)
- mock_device.ListDirectory.return_value = cpu_list + ['cpufreq']
- mock_device.FileExists.return_value = True
- mock_device.RunShellCommand = mock.Mock(side_effect=_ShellCommandHandler)
- pc = perf_control.PerfControl(mock_device)
- self._MockOutLowLevelPerfControlMethods(pc)
-
- # Verify.
- # pylint: disable=protected-access
- # pylint: disable=no-member
- pc.SetHighPerfMode()
- mock_device.EnableRoot.assert_called_once_with()
- pc._ForceAllCpusOnline.assert_called_once_with(True)
- pc.SetScalingGovernor.assert_called_once_with('performance')
- pc._SetScalingMaxFreqForCpus.assert_called_once_with(
- 1190400, ' '.join(cpu_list))
- pc._SetMaxGpuClock.assert_called_once_with(200000000)
-
- def testNexus5XHighPerfMode(self):
- # Mock out the device state for PerfControl.
- cpu_list = ['cpu%d' % cpu for cpu in xrange(6)]
- mock_device = mock.Mock(spec=device_utils.DeviceUtils)
- mock_device.product_model = 'Nexus 5X'
- mock_device.adb = mock.Mock(spec=adb_wrapper.AdbWrapper)
- mock_device.ListDirectory.return_value = cpu_list + ['cpufreq']
- mock_device.FileExists.return_value = True
- mock_device.RunShellCommand = mock.Mock(side_effect=_ShellCommandHandler)
- pc = perf_control.PerfControl(mock_device)
- self._MockOutLowLevelPerfControlMethods(pc)
-
- # Verify.
- # pylint: disable=protected-access
- # pylint: disable=no-member
- pc.SetHighPerfMode()
- mock_device.EnableRoot.assert_called_once_with()
- pc._ForceAllCpusOnline.assert_called_once_with(True)
- pc.SetScalingGovernor.assert_called_once_with('performance')
- pc._SetScalingMaxFreqForCpus.assert_called_once_with(
- 1248000, ' '.join(cpu_list))
- pc._SetMaxGpuClock.assert_called_once_with(300000000)
-
- def testNexus5XDefaultPerfMode(self):
- # Mock out the device state for PerfControl.
- cpu_list = ['cpu%d' % cpu for cpu in xrange(6)]
- mock_device = mock.Mock(spec=device_utils.DeviceUtils)
- mock_device.product_model = 'Nexus 5X'
- mock_device.adb = mock.Mock(spec=adb_wrapper.AdbWrapper)
- mock_device.ListDirectory.return_value = cpu_list + ['cpufreq']
- mock_device.FileExists.return_value = True
- mock_device.RunShellCommand = mock.Mock(side_effect=_ShellCommandHandler)
- pc = perf_control.PerfControl(mock_device)
- self._MockOutLowLevelPerfControlMethods(pc)
-
- # Verify.
- # pylint: disable=protected-access
- # pylint: disable=no-member
- pc.SetDefaultPerfMode()
- pc.SetScalingGovernor.assert_called_once_with('ondemand')
- pc._SetScalingMaxFreqForCpus.assert_any_call(1440000, 'cpu0 cpu1 cpu2 cpu3')
- pc._SetScalingMaxFreqForCpus.assert_any_call(1824000, 'cpu4 cpu5')
- pc._SetMaxGpuClock.assert_called_once_with(600000000)
- pc._ForceAllCpusOnline.assert_called_once_with(False)
diff --git a/systrace/catapult/devil/devil/android/perf/surface_stats_collector.py b/systrace/catapult/devil/devil/android/perf/surface_stats_collector.py
index f1140c1..eab493d 100644
--- a/systrace/catapult/devil/devil/android/perf/surface_stats_collector.py
+++ b/systrace/catapult/devil/devil/android/perf/surface_stats_collector.py
@@ -2,9 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import logging
import Queue
-import re
import threading
@@ -118,14 +116,6 @@ class SurfaceStatsCollector(object):
except StopIteration:
raise Exception('Unable to get surface flinger process id')
- def _GetSurfaceViewWindowName(self):
- results = self._device.RunShellCommand(
- ['dumpsys', 'SurfaceFlinger', '--list'], check_return=True)
- for window_name in results:
- if window_name.startswith('SurfaceView'):
- return window_name
- return None
-
def _GetSurfaceFlingerFrameData(self):
"""Returns collected SurfaceFlinger frame timing data.
@@ -137,73 +127,59 @@ class SurfaceStatsCollector(object):
The return value may be (None, None) if there was no data collected (for
example, if the app was closed before the collector thread has finished).
"""
- window_name = self._GetSurfaceViewWindowName()
- command = ['dumpsys', 'SurfaceFlinger', '--latency']
- # Even if we don't find the window name, run the command to get the refresh
- # period.
- if window_name:
- command.append(window_name)
- output = self._device.RunShellCommand(command, check_return=True)
- return ParseFrameData(output, parse_timestamps=bool(window_name))
-
-
-def ParseFrameData(lines, parse_timestamps):
- # adb shell dumpsys SurfaceFlinger --latency <window name>
- # prints some information about the last 128 frames displayed in
- # that window.
- # The data returned looks like this:
- # 16954612
- # 7657467895508 7657482691352 7657493499756
- # 7657484466553 7657499645964 7657511077881
- # 7657500793457 7657516600576 7657527404785
- # (...)
- #
- # The first line is the refresh period (here 16.95 ms), it is followed
- # by 128 lines w/ 3 timestamps in nanosecond each:
- # A) when the app started to draw
- # B) the vsync immediately preceding SF submitting the frame to the h/w
- # C) timestamp immediately after SF submitted that frame to the h/w
- #
- # The difference between the 1st and 3rd timestamp is the frame-latency.
- # An interesting data is when the frame latency crosses a refresh period
- # boundary, this can be calculated this way:
- #
- # ceil((C - A) / refresh-period)
- #
- # (each time the number above changes, we have a "jank").
- # If this happens a lot during an animation, the animation appears
- # janky, even if it runs at 60 fps in average.
- results = []
- for line in lines:
- # Skip over lines with anything other than digits and whitespace.
- if re.search(r'[^\d\s]', line):
- logging.warning('unexpected output: %s', line)
- else:
- results.append(line)
- if not results:
- return None, None
-
- timestamps = []
- nanoseconds_per_millisecond = 1e6
- refresh_period = long(results[0]) / nanoseconds_per_millisecond
- if not parse_timestamps:
- return refresh_period, timestamps
-
- # If a fence associated with a frame is still pending when we query the
- # latency data, SurfaceFlinger gives the frame a timestamp of INT64_MAX.
- # Since we only care about completed frames, we will ignore any timestamps
- # with this value.
- pending_fence_timestamp = (1 << 63) - 1
-
- for line in results[1:]:
- fields = line.split()
- if len(fields) != 3:
- logging.warning('Unexpected line: %s', line)
- continue
- timestamp = long(fields[1])
- if timestamp == pending_fence_timestamp:
- continue
- timestamp /= nanoseconds_per_millisecond
- timestamps.append(timestamp)
-
- return refresh_period, timestamps
+ # adb shell dumpsys SurfaceFlinger --latency <window name>
+ # prints some information about the last 128 frames displayed in
+ # that window.
+ # The data returned looks like this:
+ # 16954612
+ # 7657467895508 7657482691352 7657493499756
+ # 7657484466553 7657499645964 7657511077881
+ # 7657500793457 7657516600576 7657527404785
+ # (...)
+ #
+ # The first line is the refresh period (here 16.95 ms), it is followed
+ # by 128 lines w/ 3 timestamps in nanosecond each:
+ # A) when the app started to draw
+ # B) the vsync immediately preceding SF submitting the frame to the h/w
+ # C) timestamp immediately after SF submitted that frame to the h/w
+ #
+ # The difference between the 1st and 3rd timestamp is the frame-latency.
+ # An interesting data is when the frame latency crosses a refresh period
+ # boundary, this can be calculated this way:
+ #
+ # ceil((C - A) / refresh-period)
+ #
+ # (each time the number above changes, we have a "jank").
+ # If this happens a lot during an animation, the animation appears
+ # janky, even if it runs at 60 fps in average.
+ #
+ # We use the special "SurfaceView" window name because the statistics for
+ # the activity's main window are not updated when the main web content is
+ # composited into a SurfaceView.
+ results = self._device.RunShellCommand(
+ ['dumpsys', 'SurfaceFlinger', '--latency', 'SurfaceView'],
+ check_return=True)
+ if not len(results):
+ return (None, None)
+
+ timestamps = []
+ nanoseconds_per_millisecond = 1e6
+ refresh_period = long(results[0]) / nanoseconds_per_millisecond
+
+ # If a fence associated with a frame is still pending when we query the
+ # latency data, SurfaceFlinger gives the frame a timestamp of INT64_MAX.
+ # Since we only care about completed frames, we will ignore any timestamps
+ # with this value.
+ pending_fence_timestamp = (1 << 63) - 1
+
+ for line in results[1:]:
+ fields = line.split()
+ if len(fields) != 3:
+ continue
+ timestamp = long(fields[1])
+ if timestamp == pending_fence_timestamp:
+ continue
+ timestamp /= nanoseconds_per_millisecond
+ timestamps.append(timestamp)
+
+ return (refresh_period, timestamps)
diff --git a/systrace/catapult/devil/devil/android/perf/surface_stats_collector_test.py b/systrace/catapult/devil/devil/android/perf/surface_stats_collector_test.py
deleted file mode 100644
index 13b345c..0000000
--- a/systrace/catapult/devil/devil/android/perf/surface_stats_collector_test.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# Copyright 2019 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 unittest
-
-from devil.android.perf import surface_stats_collector
-
-
-class SurfaceStatsCollectorTests(unittest.TestCase):
- def testParseFrameData_simple(self):
- actual = surface_stats_collector.ParseFrameData([
- '16954612',
- '7657467895508 7657482691352 7657493499756',
- '7657484466553 7657499645964 7657511077881',
- '7657500793457 7657516600576 7657527404785',
- ], parse_timestamps=True)
- self.assertEqual(
- actual, (16.954612, [7657482.691352, 7657499.645964, 7657516.600576]))
-
- def testParseFrameData_withoutTimestamps(self):
- actual = surface_stats_collector.ParseFrameData([
- '16954612',
- '7657467895508 7657482691352 7657493499756',
- '7657484466553 7657499645964 7657511077881',
- '7657500793457 7657516600576 7657527404785',
- ], parse_timestamps=False)
- self.assertEqual(
- actual, (16.954612, []))
-
- def testParseFrameData_withWarning(self):
- actual = surface_stats_collector.ParseFrameData([
- 'SurfaceFlinger appears to be unresponsive, dumping anyways',
- '16954612',
- '7657467895508 7657482691352 7657493499756',
- '7657484466553 7657499645964 7657511077881',
- '7657500793457 7657516600576 7657527404785',
- ], parse_timestamps=True)
- self.assertEqual(
- actual, (16.954612, [7657482.691352, 7657499.645964, 7657516.600576]))
diff --git a/systrace/catapult/devil/devil/android/perf/thermal_throttle.py b/systrace/catapult/devil/devil/android/perf/thermal_throttle.py
index fd6b08f..546a92e 100644
--- a/systrace/catapult/devil/devil/android/perf/thermal_throttle.py
+++ b/systrace/catapult/devil/devil/android/perf/thermal_throttle.py
@@ -76,7 +76,6 @@ class ThermalThrottle(object):
self._device = device
self._throttled = False
self._detector = None
- # pylint: disable=redefined-variable-type
if OmapThrottlingDetector.IsSupported(device):
self._detector = OmapThrottlingDetector(device)
elif ExynosThrottlingDetector.IsSupported(device):
diff --git a/systrace/catapult/devil/devil/android/ports.py b/systrace/catapult/devil/devil/android/ports.py
index 4547a62..1d4e5f2 100644
--- a/systrace/catapult/devil/devil/android/ports.py
+++ b/systrace/catapult/devil/devil/android/ports.py
@@ -116,7 +116,7 @@ def IsDevicePortUsed(device, device_port, state=''):
"""
base_urls = ('127.0.0.1:%d' % device_port, 'localhost:%d' % device_port)
netstat_results = device.RunShellCommand(
- ['netstat', '-an'], check_return=True, large_output=True)
+ ['netstat', '-a'], check_return=True, large_output=True)
for single_connect in netstat_results:
# Column 3 is the local address which we want to check with.
connect_results = single_connect.split()
diff --git a/systrace/catapult/devil/devil/android/sdk/adb_wrapper.py b/systrace/catapult/devil/devil/android/sdk/adb_wrapper.py
index 13c0f52..5d24d47 100644
--- a/systrace/catapult/devil/devil/android/sdk/adb_wrapper.py
+++ b/systrace/catapult/devil/devil/android/sdk/adb_wrapper.py
@@ -9,9 +9,7 @@ should be delegated to a higher level (ex. DeviceUtils).
"""
import collections
-# pylint: disable=import-error
-# pylint: disable=no-name-in-module
-import distutils.version as du_version
+import distutils.version
import errno
import logging
import os
@@ -19,7 +17,6 @@ import posixpath
import re
import subprocess
-from devil import base_error
from devil import devil_env
from devil.android import decorators
from devil.android import device_errors
@@ -34,7 +31,6 @@ logger = logging.getLogger(__name__)
ADB_KEYS_FILE = '/data/misc/adb/adb_keys'
-ADB_HOST_KEYS_DIR = os.path.join(os.path.expanduser('~'), '.android')
DEFAULT_TIMEOUT = 30
DEFAULT_RETRIES = 2
@@ -43,10 +39,8 @@ _ADB_VERSION_RE = re.compile(r'Android Debug Bridge version (\d+\.\d+\.\d+)')
_EMULATOR_RE = re.compile(r'^emulator-[0-9]+$')
_DEVICE_NOT_FOUND_RE = re.compile(r"error: device '(?P<serial>.+)' not found")
_READY_STATE = 'device'
-_VERITY_DISABLE_RE = re.compile(r'(V|v)erity (is )?(already )?disabled'
- r'|Successfully disabled verity')
-_VERITY_ENABLE_RE = re.compile(r'(V|v)erity (is )?(already )?enabled'
- r'|Successfully enabled verity')
+_VERITY_DISABLE_RE = re.compile(r'Verity (already )?disabled')
+_VERITY_ENABLE_RE = re.compile(r'Verity (already )?enabled')
_WAITING_FOR_DEVICE_RE = re.compile(r'- waiting for device -')
@@ -98,11 +92,7 @@ def _GetVersion():
def _ShouldRetryAdbCmd(exc):
- # Errors are potentially transient and should be retried, with the exception
- # of NoAdbError. Exceptions [e.g. generated from SIGTERM handler] should be
- # raised.
- return (isinstance(exc, base_error.BaseError) and
- not isinstance(exc, device_errors.NoAdbError))
+ return not isinstance(exc, device_errors.NoAdbError)
DeviceStat = collections.namedtuple('DeviceStat',
@@ -263,22 +253,13 @@ class AdbWrapper(object):
@classmethod
@decorators.WithTimeoutAndConditionalRetries(_ShouldRetryAdbCmd)
def _RunAdbCmd(cls, args, timeout=None, retries=None, device_serial=None,
- check_error=True, cpu_affinity=None, additional_env=None):
- if timeout:
- remaining = timeout_retry.CurrentTimeoutThreadGroup().GetRemainingTime()
- if remaining:
- # Use a slightly smaller timeout than remaining time to ensure that we
- # have time to collect output from the command.
- timeout = 0.95 * remaining
- else:
- timeout = None
- env = cls._ADB_ENV.copy()
- if additional_env:
- env.update(additional_env)
+ check_error=True, cpu_affinity=None):
+ # pylint: disable=no-member
try:
status, output = cmd_helper.GetCmdStatusAndOutputWithTimeout(
cls._BuildAdbCmd(args, device_serial, cpu_affinity=cpu_affinity),
- timeout, env=env)
+ timeout_retry.CurrentTimeoutThreadGroup().GetRemainingTime(),
+ env=cls._ADB_ENV)
except OSError as e:
if e.errno in (errno.ENOENT, errno.ENOEXEC):
raise device_errors.NoAdbError(msg=str(e))
@@ -310,8 +291,7 @@ class AdbWrapper(object):
timeout: Timeout in seconds.
retries: Number of retries.
check_error: Check that the command doesn't return an error message. This
- does check the error status of adb but DOES NOT check the exit status
- of shell commands.
+ does NOT check the exit status of shell commands.
Returns:
The output of the command.
@@ -320,17 +300,13 @@ class AdbWrapper(object):
device_serial=self._device_serial,
check_error=check_error)
- def _IterRunDeviceAdbCmd(self, args, iter_timeout, timeout,
- check_error=True):
+ def _IterRunDeviceAdbCmd(self, args, iter_timeout, timeout):
"""Runs an adb command and returns an iterator over its output lines.
Args:
args: A list of arguments to adb.
iter_timeout: Timeout for each iteration in seconds.
timeout: Timeout for the entire command in seconds.
- check_error: Check that the command succeeded. This does check the
- error status of the adb command but DOES NOT check the exit status
- of shell commands.
Yields:
The output of the command line by line.
@@ -339,8 +315,7 @@ class AdbWrapper(object):
self._BuildAdbCmd(args, self._device_serial),
iter_timeout=iter_timeout,
timeout=timeout,
- env=self._ADB_ENV,
- check_status=check_error)
+ env=self._ADB_ENV)
def __eq__(self, other):
"""Consider instances equal if they refer to the same device.
@@ -378,21 +353,10 @@ class AdbWrapper(object):
cls._RunAdbCmd(['kill-server'], timeout=timeout, retries=retries)
@classmethod
- def StartServer(cls, keys=None, timeout=DEFAULT_TIMEOUT,
- retries=DEFAULT_RETRIES):
- """Starts the ADB server.
-
- Args:
- keys: (optional) List of local ADB keys to use to auth with devices.
- timeout: (optional) Timeout per try in seconds.
- retries: (optional) Number of retries to attempt.
- """
- additional_env = {}
- if keys:
- additional_env['ADB_VENDOR_KEYS'] = ':'.join(keys)
+ def StartServer(cls, timeout=DEFAULT_TIMEOUT, retries=DEFAULT_RETRIES):
# CPU affinity is used to reduce adb instability http://crbug.com/268450
cls._RunAdbCmd(['start-server'], timeout=timeout, retries=retries,
- cpu_affinity=0, additional_env=additional_env)
+ cpu_affinity=0)
@classmethod
def GetDevices(cls, timeout=DEFAULT_TIMEOUT, retries=DEFAULT_RETRIES):
@@ -458,8 +422,8 @@ class AdbWrapper(object):
"""
VerifyLocalFileExists(local)
- if (du_version.LooseVersion(self.Version()) <
- du_version.LooseVersion('1.0.36')):
+ if (distutils.version.LooseVersion(self.Version()) <
+ distutils.version.LooseVersion('1.0.36')):
# Different versions of adb handle pushing a directory to an existing
# directory differently.
@@ -516,19 +480,6 @@ class AdbWrapper(object):
'File pulled from the device did not arrive on the host: %s' % local,
device_serial=str(self))
- def StartShell(self, cmd):
- """Starts a subprocess on the device and returns a handle to the process.
-
- Args:
- args: A sequence of program arguments. The executable to run is the first
- item in the sequence.
-
- Returns:
- An instance of subprocess.Popen associated with the live process.
- """
- return cmd_helper.StartCmd(
- self._BuildAdbCmd(['shell'] + cmd, self._device_serial))
-
def Shell(self, command, expect_status=0, timeout=DEFAULT_TIMEOUT,
retries=DEFAULT_RETRIES):
"""Runs a shell command on the device.
@@ -624,7 +575,7 @@ class AdbWrapper(object):
def Logcat(self, clear=False, dump=False, filter_specs=None,
logcat_format=None, ring_buffer=None, iter_timeout=None,
- check_error=True, timeout=None, retries=DEFAULT_RETRIES):
+ timeout=None, retries=DEFAULT_RETRIES):
"""Get an iterable over the logcat output.
Args:
@@ -640,7 +591,6 @@ class AdbWrapper(object):
iter_timeout: If set and neither clear nor dump is set, the number of
seconds to wait between iterations. If no line is found before the
given number of seconds elapses, the iterable will yield None.
- check_error: Whether to check the exit status of the logcat command.
timeout: (optional) If set, timeout per try in seconds. If clear or dump
is set, defaults to DEFAULT_TIMEOUT.
retries: (optional) If clear or dump is set, the number of retries to
@@ -666,13 +616,10 @@ class AdbWrapper(object):
cmd.extend(filter_specs)
if use_iter:
- return self._IterRunDeviceAdbCmd(
- cmd, iter_timeout, timeout, check_error=check_error)
+ return self._IterRunDeviceAdbCmd(cmd, iter_timeout, timeout)
else:
timeout = timeout if timeout is not None else DEFAULT_TIMEOUT
- output = self._RunDeviceAdbCmd(
- cmd, timeout, retries, check_error=check_error)
- return output.splitlines()
+ return self._RunDeviceAdbCmd(cmd, timeout, retries).splitlines()
def Forward(self, local, remote, allow_rebind=False,
timeout=DEFAULT_TIMEOUT, retries=DEFAULT_RETRIES):
@@ -724,8 +671,8 @@ class AdbWrapper(object):
Returns:
The output of adb forward --list as a string.
"""
- if (du_version.LooseVersion(self.Version()) >=
- du_version.LooseVersion('1.0.36')):
+ if (distutils.version.LooseVersion(self.Version()) >=
+ distutils.version.LooseVersion('1.0.36')):
# Starting in 1.0.36, this can occasionally fail with a protocol fault.
# As this interrupts all connections with all devices, we instead just
# return an empty list. This may give clients an inaccurate result, but
@@ -968,28 +915,18 @@ class AdbWrapper(object):
return self._RunDeviceAdbCmd(['emu'] + cmd, timeout, retries)
def DisableVerity(self, timeout=DEFAULT_TIMEOUT, retries=DEFAULT_RETRIES):
- """Disable Marshmallow's Verity security feature.
-
- Returns:
- The output of the disable-verity command as a string.
- """
+ """Disable Marshmallow's Verity security feature"""
output = self._RunDeviceAdbCmd(['disable-verity'], timeout, retries)
if output and not _VERITY_DISABLE_RE.search(output):
raise device_errors.AdbCommandFailedError(
['disable-verity'], output, device_serial=self._device_serial)
- return output
def EnableVerity(self, timeout=DEFAULT_TIMEOUT, retries=DEFAULT_RETRIES):
- """Enable Marshmallow's Verity security feature.
-
- Returns:
- The output of the enable-verity command as a string.
- """
+ """Enable Marshmallow's Verity security feature"""
output = self._RunDeviceAdbCmd(['enable-verity'], timeout, retries)
if output and not _VERITY_ENABLE_RE.search(output):
raise device_errors.AdbCommandFailedError(
['enable-verity'], output, device_serial=self._device_serial)
- return output
@property
def is_emulator(self):
diff --git a/systrace/catapult/devil/devil/android/sdk/fastboot.py b/systrace/catapult/devil/devil/android/sdk/fastboot.py
index 47f4167..ae99d39 100644
--- a/systrace/catapult/devil/devil/android/sdk/fastboot.py
+++ b/systrace/catapult/devil/devil/android/sdk/fastboot.py
@@ -54,7 +54,7 @@ class Fastboot(object):
Raises:
TypeError: If cmd is not of type list.
"""
- if isinstance(cmd, list):
+ if type(cmd) == list:
cmd = [cls._fastboot_path.read()] + cmd
else:
raise TypeError(
@@ -76,7 +76,7 @@ class Fastboot(object):
Raises:
TypeError: If cmd is not of type list.
"""
- if isinstance(cmd, list):
+ if type(cmd) == list:
cmd = ['-s', self._device_serial] + cmd
return self._RunFastbootCommand(cmd)
diff --git a/systrace/catapult/devil/devil/android/sdk/shared_prefs.py b/systrace/catapult/devil/devil/android/sdk/shared_prefs.py
index c8c82b4..2fa2e6a 100644
--- a/systrace/catapult/devil/devil/android/sdk/shared_prefs.py
+++ b/systrace/catapult/devil/devil/android/sdk/shared_prefs.py
@@ -10,10 +10,10 @@ See e.g.:
import logging
import posixpath
-from xml.etree import ElementTree
from devil.android import device_errors
from devil.android.sdk import version_codes
+from xml.etree import ElementTree
logger = logging.getLogger(__name__)
@@ -167,7 +167,7 @@ _PREF_TYPES = {c.tag_name: c for c in [BooleanPref, FloatPref, IntPref,
class SharedPrefs(object):
- def __init__(self, device, package, filename, use_encrypted_path=False):
+ def __init__(self, device, package, filename):
"""Helper object to read and update "Shared Prefs" of Android apps.
Such files typically look like, e.g.:
@@ -196,29 +196,12 @@ class SharedPrefs(object):
package: A string with the package name of the app that owns the shared
preferences file.
filename: A string with the name of the preferences file to read/write.
- use_encrypted_path: Whether to read and write to the shared prefs location
- in the device-encrypted path (/data/user_de) instead of the older,
- unencrypted path (/data/data). Only supported on N+, but falls back to
- the unencrypted path if the encrypted path is not supported on the given
- device.
"""
self._device = device
self._xml = None
self._package = package
self._filename = filename
- self._unencrypted_path = '/data/data/%s/shared_prefs/%s' % (package,
- filename)
- self._encrypted_path = '/data/user_de/0/%s/shared_prefs/%s' % (package,
- filename)
- self._path = self._unencrypted_path
- self._encrypted = use_encrypted_path
- if use_encrypted_path:
- if self._device.build_version_sdk < version_codes.NOUGAT:
- logging.info('SharedPrefs set to use encrypted path, but given device '
- 'is not running N+. Falling back to unencrypted path')
- self._encrypted = False
- else:
- self._path = self._encrypted_path
+ self._path = '/data/data/%s/shared_prefs/%s' % (package, filename)
self._changed = False
def __repr__(self):
@@ -278,17 +261,12 @@ class SharedPrefs(object):
self._xml = None
self._changed = True
- def Commit(self, force_commit=False):
+ def Commit(self):
"""Save the current set of preferences to the device.
- Only actually saves if some preferences have been modified or force_commit
- is set to True.
-
- Args:
- force_commit: Commit even if no changes have been made to the SharedPrefs
- instance.
+ Only actually saves if some preferences have been modified.
"""
- if not (self.changed or force_commit):
+ if not self.changed:
return
self._device.RunShellCommand(
['mkdir', '-p', posixpath.dirname(self.path)],
@@ -299,24 +277,14 @@ class SharedPrefs(object):
# to the shared_prefs directory, which mimics the behavior of a file
# created by the app itself
if self._device.build_version_sdk >= version_codes.MARSHMALLOW:
- security_context = self._device.GetSecurityContextForPackage(self.package,
- encrypted=self._encrypted)
- if security_context is None:
+ security_context = self._GetSecurityContext(self.package)
+ if security_context == None:
raise device_errors.CommandFailedError(
'Failed to get security context for %s' % self.package)
- paths = [posixpath.dirname(self.path), self.path]
- self._device.ChangeSecurityContext(security_context, paths)
-
- # Ensure that there isn't both an encrypted and unencrypted version of the
- # file on the device at the same time.
- if self._device.build_version_sdk >= version_codes.NOUGAT:
- remove_path = (self._unencrypted_path if self._encrypted
- else self._encrypted_path)
- if self._device.PathExists(remove_path, as_root=True):
- logging.warning('Found an equivalent shared prefs file at %s, removing',
- remove_path)
- self._device.RemovePath(remove_path, as_root=True)
-
+ self._device.RunShellCommand(
+ ['chcon', '-R', security_context,
+ '/data/data/%s/shared_prefs' % self.package],
+ as_root=True, check_return=True)
self._device.KillAll(self.package, exact=True, as_root=True, quiet=True)
self._changed = False
@@ -438,3 +406,15 @@ class SharedPrefs(object):
pref.set(value)
self._changed = True
logger.info('Setting property: %s', pref)
+
+ def _GetSecurityContext(self, package):
+ for line in self._device.RunShellCommand(['ls', '-Z', '/data/data/'],
+ as_root=True, check_return=True):
+ split_line = line.split()
+ # ls -Z output differs between Android versions, but the package is
+ # always last and the context always starts with "u:object"
+ if split_line[-1] == package:
+ for column in split_line:
+ if column.startswith('u:object'):
+ return column
+ return None
diff --git a/systrace/catapult/devil/devil/android/sdk/shared_prefs_test.py b/systrace/catapult/devil/devil/android/sdk/shared_prefs_test.py
index 08bbb46..4c31c56 100755
--- a/systrace/catapult/devil/devil/android/sdk/shared_prefs_test.py
+++ b/systrace/catapult/devil/devil/android/sdk/shared_prefs_test.py
@@ -19,14 +19,6 @@ with devil_env.SysPath(devil_env.PYMOCK_PATH):
import mock # pylint: disable=import-error
-INITIAL_XML = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
- '<map>\n'
- ' <int name="databaseVersion" value="107" />\n'
- ' <boolean name="featureEnabled" value="false" />\n'
- ' <string name="someHashValue">249b3e5af13d4db2</string>\n'
- '</map>')
-
-
def MockDeviceWithFiles(files=None):
if files is None:
files = {}
@@ -51,7 +43,13 @@ class SharedPrefsTest(unittest.TestCase):
def setUp(self):
self.device = MockDeviceWithFiles({
- '/data/data/com.some.package/shared_prefs/prefs.xml': INITIAL_XML})
+ '/data/data/com.some.package/shared_prefs/prefs.xml':
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ '<map>\n'
+ ' <int name="databaseVersion" value="107" />\n'
+ ' <boolean name="featureEnabled" value="false" />\n'
+ ' <string name="someHashValue">249b3e5af13d4db2</string>\n'
+ '</map>'})
self.expected_data = {'databaseVersion': 107,
'featureEnabled': False,
'someHashValue': '249b3e5af13d4db2'}
@@ -129,22 +127,6 @@ class SharedPrefsTest(unittest.TestCase):
'bigNumner': 6000000000,
'apps': ['gmail', 'chrome', 'music']}) # data survived roundtrip
- def testForceCommit(self):
- prefs = shared_prefs.SharedPrefs(
- self.device, 'com.some.package', 'prefs.xml')
- prefs.Load()
- new_xml = 'Not valid XML'
- self.device.WriteFile('/data/data/com.some.package/shared_prefs/prefs.xml',
- new_xml)
- prefs.Commit()
- # Since we didn't change anything, Commit() should be a no-op.
- self.assertEquals(self.device.ReadFile(
- '/data/data/com.some.package/shared_prefs/prefs.xml'), new_xml)
- prefs.Commit(force_commit=True)
- # Forcing the commit should restore the originally read XML.
- self.assertEquals(self.device.ReadFile(
- '/data/data/com.some.package/shared_prefs/prefs.xml'), INITIAL_XML)
-
def testAsContextManager_onlyReads(self):
with shared_prefs.SharedPrefs(
self.device, 'com.some.package', 'prefs.xml') as prefs:
@@ -184,19 +166,6 @@ class SharedPrefsTest(unittest.TestCase):
# contents were not modified
self.assertEquals(prefs.AsDict(), self.expected_data)
- def testEncryptedPath(self):
- type(self.device).build_version_sdk = mock.PropertyMock(
- return_value=version_codes.MARSHMALLOW)
- with shared_prefs.SharedPrefs(self.device, 'com.some.package',
- 'prefs.xml', use_encrypted_path=True) as prefs:
- self.assertTrue(prefs.path.startswith('/data/data'))
-
- type(self.device).build_version_sdk = mock.PropertyMock(
- return_value=version_codes.NOUGAT)
- with shared_prefs.SharedPrefs(self.device, 'com.some.package',
- 'prefs.xml', use_encrypted_path=True) as prefs:
- self.assertTrue(prefs.path.startswith('/data/user_de/0'))
-
if __name__ == '__main__':
logging.getLogger().setLevel(logging.DEBUG)
unittest.main(verbosity=2)
diff --git a/systrace/catapult/devil/devil/android/sdk/version_codes.py b/systrace/catapult/devil/devil/android/sdk/version_codes.py
index 29c7285..ec14359 100644
--- a/systrace/catapult/devil/devil/android/sdk/version_codes.py
+++ b/systrace/catapult/devil/devil/android/sdk/version_codes.py
@@ -17,6 +17,5 @@ LOLLIPOP_MR1 = 22
MARSHMALLOW = 23
NOUGAT = 24
NOUGAT_MR1 = 25
-OREO = 26
-OREO_MR1 = 27
-PIE = 28
+O = 26
+O_MR1 = 27
diff --git a/systrace/catapult/devil/devil/android/tools/device_monitor.py b/systrace/catapult/devil/devil/android/tools/device_monitor.py
index 565f865..10e0333 100755
--- a/systrace/catapult/devil/devil/android/tools/device_monitor.py
+++ b/systrace/catapult/devil/devil/android/tools/device_monitor.py
@@ -112,9 +112,9 @@ def get_device_status_unsafe(device):
except ValueError:
continue
key = line.split(':')[0].strip()
- if key == 'MemTotal':
+ if 'MemTotal' == key:
status['mem']['total'] = value
- elif key == 'MemFree':
+ elif 'MemFree' == key:
status['mem']['free'] = value
# Process
@@ -162,8 +162,7 @@ def get_device_status(device):
try:
status = get_device_status_unsafe(device)
except device_errors.DeviceUnreachableError:
- status = collections.defaultdict(dict)
- status['state'] = 'offline'
+ status = {'state': 'offline'}
return status
diff --git a/systrace/catapult/devil/devil/android/tools/device_recovery.py b/systrace/catapult/devil/devil/android/tools/device_recovery.py
index 8050e6f..0925aae 100755
--- a/systrace/catapult/devil/devil/android/tools/device_recovery.py
+++ b/systrace/catapult/devil/devil/android/tools/device_recovery.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env vpython
+#!/usr/bin/env python
# Copyright 2016 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.
@@ -6,14 +6,12 @@
"""A script to recover devices in a known bad state."""
import argparse
-import glob
import logging
import os
+import psutil
import signal
import sys
-import psutil
-
if __name__ == '__main__':
sys.path.append(
os.path.abspath(os.path.join(os.path.dirname(__file__),
@@ -31,20 +29,16 @@ from devil.utils import reset_usb # pylint: disable=unused-import
logger = logging.getLogger(__name__)
-from py_utils import modules_util
-
-
-# Script depends on features from psutil version 2.0 or higher.
-modules_util.RequireVersion(psutil, '2.0')
-
def KillAllAdb():
def get_all_adb():
for p in psutil.process_iter():
try:
- # Retrieve all required process infos at once.
+ # Note: p.as_dict is compatible with both older (v1 and under) as well
+ # as newer (v2 and over) versions of psutil.
+ # See: http://grodola.blogspot.com/2014/01/psutil-20-porting.html
pinfo = p.as_dict(attrs=['pid', 'name', 'cmdline'])
- if pinfo['name'] == 'adb':
+ if 'adb' == pinfo['name']:
pinfo['cmdline'] = ' '.join(pinfo['cmdline'])
yield p, pinfo
except (psutil.NoSuchProcess, psutil.AccessDenied):
@@ -65,58 +59,12 @@ def KillAllAdb():
pass
-def TryAuth(device):
- """Uses anything in ~/.android/ that looks like a key to auth with the device.
-
- Args:
- device: The DeviceUtils device to attempt to auth.
-
- Returns:
- True if device successfully authed.
- """
- possible_keys = glob.glob(os.path.join(adb_wrapper.ADB_HOST_KEYS_DIR, '*key'))
- if len(possible_keys) <= 1:
- logger.warning(
- 'Only %d ADB keys available. Not forcing auth.', len(possible_keys))
- return False
-
- KillAllAdb()
- adb_wrapper.AdbWrapper.StartServer(keys=possible_keys)
- new_state = device.adb.GetState()
- if new_state != 'device':
- logger.error(
- 'Auth failed. Device %s still stuck in %s.', str(device), new_state)
- return False
-
- # It worked! Now register the host's default ADB key on the device so we don't
- # have to do all that again.
- pub_key = os.path.join(adb_wrapper.ADB_HOST_KEYS_DIR, 'adbkey.pub')
- if not os.path.exists(pub_key): # This really shouldn't happen.
- logger.error('Default ADB key not available at %s.', pub_key)
- return False
-
- with open(pub_key) as f:
- pub_key_contents = f.read()
- try:
- device.WriteFile(adb_wrapper.ADB_KEYS_FILE, pub_key_contents, as_root=True)
- except (device_errors.CommandTimeoutError,
- device_errors.CommandFailedError,
- device_errors.DeviceUnreachableError):
- logger.exception('Unable to write default ADB key to %s.', str(device))
- return False
- return True
-
-
def RecoverDevice(device, blacklist, should_reboot=lambda device: True):
if device_status.IsBlacklisted(device.adb.GetDeviceSerial(),
blacklist):
logger.debug('%s is blacklisted, skipping recovery.', str(device))
return
- if device.adb.GetState() == 'unauthorized' and TryAuth(device):
- logger.info('Successfully authed device %s!', str(device))
- return
-
if should_reboot(device):
try:
device.WaitUntilFullyBooted(retries=0)
@@ -187,7 +135,7 @@ def RecoverDevices(devices, blacklist, enable_usb_reset=False):
should_restart_adb = should_restart_usb.union(set(
status['serial'] for status in statuses
if status['adb_status'] == 'unauthorized'))
- should_reboot_device = should_restart_usb.union(set(
+ should_reboot_device = should_restart_adb.union(set(
status['serial'] for status in statuses
if status['blacklisted']))
diff --git a/systrace/catapult/devil/devil/android/tools/provision_devices.py b/systrace/catapult/devil/devil/android/tools/provision_devices.py
index 47b1dc3..68aca3b 100755
--- a/systrace/catapult/devil/devil/android/tools/provision_devices.py
+++ b/systrace/catapult/devil/devil/android/tools/provision_devices.py
@@ -39,7 +39,6 @@ from devil.android import settings
from devil.android.sdk import adb_wrapper
from devil.android.sdk import intent
from devil.android.sdk import keyevent
-from devil.android.sdk import shared_prefs
from devil.android.sdk import version_codes
from devil.android.tools import script_common
from devil.constants import exit_codes
@@ -52,9 +51,6 @@ _SYSTEM_APP_DIRECTORIES = ['/system/app/', '/system/priv-app/']
_SYSTEM_WEBVIEW_NAMES = ['webview', 'WebViewGoogle']
_CHROME_PACKAGE_REGEX = re.compile('.*chrom.*')
_TOMBSTONE_REGEX = re.compile('tombstone.*')
-_STANDALONE_VR_DEVICES = [
- 'vega', # Lenovo Mirage Solo
-]
class _DEFAULT_TIMEOUTS(object):
@@ -138,7 +134,6 @@ def ProvisionDevices(
steps.append(ProvisionStep(SetDate))
steps.append(ProvisionStep(CheckExternalStorage))
- steps.append(ProvisionStep(StandaloneVrDeviceSetup))
parallel_devices.pMap(ProvisionDevice, steps, blacklist, reboot_timeout)
@@ -514,8 +509,6 @@ def LogDeviceProperties(device):
logger.info(' %s', prop)
-# TODO(jbudorick): Relocate this either to device_utils or a separate
-# and more intentionally reusable layer on top of device_utils.
def CheckExternalStorage(device):
"""Checks that storage is writable and if not makes it writable.
@@ -536,30 +529,6 @@ def CheckExternalStorage(device):
device.WriteFile(f.name, 'test')
-def StandaloneVrDeviceSetup(device):
- """Performs any additional setup necessary for standalone Android VR devices.
-
- Arguments:
- device: The device to check.
- """
- if device.product_name not in _STANDALONE_VR_DEVICES:
- return
-
- # Modify VrCore's settings so that any first time setup, etc. is skipped.
- shared_pref = shared_prefs.SharedPrefs(device, 'com.google.vr.vrcore',
- 'VrCoreSettings.xml', use_encrypted_path=True)
- shared_pref.Load()
- # Skip first time setup.
- shared_pref.SetBoolean('DaydreamSetupComplete', True)
- # Disable the automatic prompt that shows anytime the device detects that a
- # controller isn't connected.
- shared_pref.SetBoolean('gConfigFlags:controller_recovery_enabled', False)
- # Use an automated controller instead of a real one so we get past the
- # controller pairing screen that's shown on startup.
- shared_pref.SetBoolean('UseAutomatedController', True)
- shared_pref.Commit()
-
-
def main(raw_args):
# Recommended options on perf bots:
# --disable-network
diff --git a/systrace/catapult/devil/devil/android/tools/script_common.py b/systrace/catapult/devil/devil/android/tools/script_common.py
index 897659b..150e63f 100644
--- a/systrace/catapult/devil/devil/android/tools/script_common.py
+++ b/systrace/catapult/devil/devil/android/tools/script_common.py
@@ -11,38 +11,13 @@ from devil.android import device_utils
def AddEnvironmentArguments(parser):
- """Adds environment-specific arguments to the provided parser.
-
- After adding these arguments, you must pass the user-specified values when
- initializing devil. See the InitializeEnvironment() to determine how to do so.
-
- Args:
- parser: an instance of argparse.ArgumentParser
- """
+ """Adds environment-specific arguments to the provided parser."""
parser.add_argument(
'--adb-path', type=os.path.realpath,
help='Path to the adb binary')
def InitializeEnvironment(args):
- """Initializes devil based on the args added by AddEnvironmentArguments().
-
- This initializes devil, and configures it to use the adb binary specified by
- the '--adb-path' flag (if provided by the user, otherwise this defaults to
- devil's copy of adb). Although this is one possible way to initialize devil,
- you should check if your project has prefered ways to initialize devil (ex.
- the chromium project uses devil_chromium.Initialize() to have different
- defaults for dependencies).
-
- This method requires having previously called AddEnvironmentArguments() on the
- relevant argparse.ArgumentParser.
-
- Note: you should only initialize devil once, and subsequent calls to any
- method wrapping devil_env.config.Initialize() will have no effect.
-
- Args:
- args: the parsed args returned by an argparse.ArgumentParser
- """
devil_dynamic_config = devil_env.EmptyConfig()
if args.adb_path:
devil_dynamic_config['dependencies'].update(
@@ -53,11 +28,7 @@ def InitializeEnvironment(args):
def AddDeviceArguments(parser):
- """Adds device and blacklist arguments to the provided parser.
-
- Args:
- parser: an instance of argparse.ArgumentParser
- """
+ """Adds device and blacklist arguments to the provided parser."""
parser.add_argument(
'-d', '--device', dest='devices', action='append',
help='Serial number of the Android device to use. (default: use all)')
diff --git a/systrace/catapult/devil/devil/android/tools/script_common_test.py b/systrace/catapult/devil/devil/android/tools/script_common_test.py
index 30f9aaf..3ddb1c1 100755
--- a/systrace/catapult/devil/devil/android/tools/script_common_test.py
+++ b/systrace/catapult/devil/devil/android/tools/script_common_test.py
@@ -18,7 +18,6 @@ with devil_env.SysPath(devil_env.PYMOCK_PATH):
import mock # pylint: disable=import-error
with devil_env.SysPath(devil_env.DEPENDENCY_MANAGER_PATH):
- # pylint: disable=wrong-import-order
from dependency_manager import exceptions
@@ -91,3 +90,4 @@ class InitializeEnvironmentTest(unittest.TestCase):
if __name__ == '__main__':
sys.exit(unittest.main())
+
diff --git a/systrace/catapult/devil/devil/android/tools/system_app.py b/systrace/catapult/devil/devil/android/tools/system_app.py
index 8629ae6..00ea312 100755
--- a/systrace/catapult/devil/devil/android/tools/system_app.py
+++ b/systrace/catapult/devil/devil/android/tools/system_app.py
@@ -10,7 +10,6 @@ import contextlib
import logging
import os
import posixpath
-import re
import sys
@@ -32,19 +31,6 @@ from devil.utils import run_tests_helper
logger = logging.getLogger(__name__)
-# Some system apps aren't actually installed in the /system/ directory, so
-# special case them here with the correct install location.
-SPECIAL_SYSTEM_APP_LOCATIONS = {
- # This also gets installed in /data/app when not a system app, so this script
- # will remove either version. This doesn't appear to cause any issues, but
- # will cause a few unnecessary reboots if this is the only package getting
- # removed and it's already not a system app.
- 'com.google.ar.core': '/data/app/',
-}
-
-# Gets app path and package name pm list packages -f output.
-_PM_LIST_PACKAGE_PATH_RE = re.compile(r'^\s*package:(\S+)=(\S+)\s*$')
-
def RemoveSystemApps(device, package_names):
"""Removes the given system apps.
@@ -60,8 +46,7 @@ def RemoveSystemApps(device, package_names):
@contextlib.contextmanager
-def ReplaceSystemApp(device, package_name, replacement_apk,
- install_timeout=None):
+def ReplaceSystemApp(device, package_name, replacement_apk):
"""A context manager that replaces the given system app while in scope.
Args:
@@ -72,7 +57,7 @@ def ReplaceSystemApp(device, package_name, replacement_apk,
"""
storage_dir = device_temp_file.NamedDeviceTemporaryDirectory(device.adb)
relocate_app = _RelocateApp(device, package_name, storage_dir.name)
- install_app = _TemporarilyInstallApp(device, replacement_apk, install_timeout)
+ install_app = _TemporarilyInstallApp(device, replacement_apk)
with storage_dir, relocate_app, install_app:
yield
@@ -81,36 +66,8 @@ def _FindSystemPackagePaths(device, system_package_list):
"""Finds all system paths for the given packages."""
found_paths = []
for system_package in system_package_list:
- paths = _GetApplicationPaths(device, system_package)
- p = _GetSystemPath(system_package, paths)
- if p:
- found_paths.append(p)
- return found_paths
-
-
-# Find all application paths, even those flagged as uninstalled, as these
-# would still block another package with the same name from installation
-# if they differ in signing keys.
-# TODO(aluo): Move this into device_utils.py
-def _GetApplicationPaths(device, package):
- paths = []
- lines = device.RunShellCommand(['pm', 'list', 'packages', '-f', '-u',
- package], check_return=True)
- for line in lines:
- match = re.match(_PM_LIST_PACKAGE_PATH_RE, line)
- if match:
- path = match.group(1)
- package_name = match.group(2)
- if package_name == package:
- paths.append(path)
- return paths
-
-
-def _GetSystemPath(package, paths):
- for p in paths:
- if p.startswith(SPECIAL_SYSTEM_APP_LOCATIONS.get(package, '/system/')):
- return p
- return None
+ found_paths.extend(device.GetApplicationPaths(system_package))
+ return [p for p in found_paths if p.startswith('/system/')]
_ENABLE_MODIFICATION_PROP = 'devil.modify_sys_apps'
@@ -127,12 +84,6 @@ def EnableSystemAppModification(device):
yield
return
- # All calls that could potentially need root should run with as_root=True, but
- # it looks like some parts of Telemetry work as-is by implicitly assuming that
- # root is already granted if it's necessary. The reboot can mess with this, so
- # as a workaround, check whether we're starting with root already, and if so,
- # restore the device to that state at the end.
- should_restore_root = device.HasRoot()
device.EnableRoot()
if not device.HasRoot():
raise device_errors.CommandFailedError(
@@ -156,8 +107,6 @@ def EnableSystemAppModification(device):
device.SetProp(_ENABLE_MODIFICATION_PROP, '0')
device.Reboot()
device.WaitUntilFullyBooted()
- if should_restore_root:
- device.EnableRoot()
@contextlib.contextmanager
@@ -187,17 +136,13 @@ def _RelocateApp(device, package_name, relocate_to):
@contextlib.contextmanager
-def _TemporarilyInstallApp(device, apk, install_timeout=None):
+def _TemporarilyInstallApp(device, apk):
"""A context manager that installs an app while in scope."""
- if install_timeout is None:
- device.Install(apk, reinstall=True)
- else:
- device.Install(apk, reinstall=True, timeout=install_timeout)
-
+ device.adb.Install(apk, reinstall=True)
try:
yield
finally:
- device.Uninstall(apk_helper.GetPackageName(apk))
+ device.adb.Uninstall(apk_helper.GetPackageName(apk))
def _MoveApp(device, relocation_map):
diff --git a/systrace/catapult/devil/devil/android/tools/system_app_devicetest.py b/systrace/catapult/devil/devil/android/tools/system_app_devicetest.py
index 293bad1..0e8afdc 100755
--- a/systrace/catapult/devil/devil/android/tools/system_app_devicetest.py
+++ b/systrace/catapult/devil/devil/android/tools/system_app_devicetest.py
@@ -39,7 +39,7 @@ class SystemAppDeviceTest(device_test_case.DeviceTestCase):
self._cached_apks = {}
for o in self._original_paths:
h = os.path.join(self._apk_cache_dir, posixpath.basename(o))
- self._device.PullFile(o, h, timeout=60)
+ self._device.PullFile(o, h)
self._cached_apks[h] = o
def tearDown(self):
diff --git a/systrace/catapult/devil/devil/android/tools/system_app_test.py b/systrace/catapult/devil/devil/android/tools/system_app_test.py
index 44df7ea..f72aa16 100644
--- a/systrace/catapult/devil/devil/android/tools/system_app_test.py
+++ b/systrace/catapult/devil/devil/android/tools/system_app_test.py
@@ -21,16 +21,6 @@ with devil_env.SysPath(devil_env.PYMOCK_PATH):
import mock
-_PACKAGE_NAME = 'com.android'
-_PACKAGE_PATH = '/path/to/com.android.apk'
-_PM_LIST_PACKAGES_COMMAND = ['pm', 'list', 'packages', '-f', '-u',
- _PACKAGE_NAME]
-_PM_LIST_PACKAGES_OUTPUT_WITH_PATH = ['package:/path/to/other=' + _PACKAGE_NAME
- + '.other', 'package:' + _PACKAGE_PATH +
- '=' + _PACKAGE_NAME]
-_PM_LIST_PACKAGES_OUTPUT_WITHOUT_PATH = ['package:/path/to/other=' +
- _PACKAGE_NAME + '.other']
-
class SystemAppTest(unittest.TestCase):
def testDoubleEnableModification(self):
@@ -57,7 +47,7 @@ class SystemAppTest(unittest.TestCase):
mock_device.GetProp.side_effect = dict_getprop
with system_app.EnableSystemAppModification(mock_device):
- mock_device.EnableRoot.assert_called_once_with()
+ mock_device.EnableRoot.assert_called_once()
mock_device.GetProp.assert_called_once_with(
system_app._ENABLE_MODIFICATION_PROP)
mock_device.SetProp.assert_called_once_with(
@@ -65,71 +55,15 @@ class SystemAppTest(unittest.TestCase):
mock_device.reset_mock()
with system_app.EnableSystemAppModification(mock_device):
- self.assertFalse(mock_device.EnableRoot.mock_calls) # assert not called
+ mock_device.EnableRoot.assert_not_called()
mock_device.GetProp.assert_called_once_with(
system_app._ENABLE_MODIFICATION_PROP)
- self.assertFalse(mock_device.SetProp.mock_calls) # assert not called
+ mock_device.SetProp.assert_not_called()
mock_device.reset_mock()
mock_device.SetProp.assert_called_once_with(
system_app._ENABLE_MODIFICATION_PROP, '0')
- def test_GetApplicationPaths_found(self):
- """Path found in output along with another package having similar name."""
- # pylint: disable=protected-access
- mock_device = mock.Mock(spec=device_utils.DeviceUtils)
- mock_device.RunShellCommand.configure_mock(
- return_value=_PM_LIST_PACKAGES_OUTPUT_WITH_PATH
- )
-
- paths = system_app._GetApplicationPaths(mock_device, _PACKAGE_NAME)
-
- self.assertEquals([_PACKAGE_PATH], paths)
- mock_device.RunShellCommand.assert_called_once_with(
- _PM_LIST_PACKAGES_COMMAND, check_return=True)
-
- def test_GetApplicationPaths_notFound(self):
- """Path not found in output, only another package with similar name."""
- # pylint: disable=protected-access
- mock_device = mock.Mock(spec=device_utils.DeviceUtils)
- mock_device.RunShellCommand.configure_mock(
- return_value=_PM_LIST_PACKAGES_OUTPUT_WITHOUT_PATH
- )
-
- paths = system_app._GetApplicationPaths(mock_device, _PACKAGE_NAME)
-
- self.assertEquals([], paths)
- mock_device.RunShellCommand.assert_called_once_with(
- _PM_LIST_PACKAGES_COMMAND, check_return=True)
-
- def test_GetApplicationPaths_noPaths(self):
- """Nothing containing text of package name found in output."""
- # pylint: disable=protected-access
- mock_device = mock.Mock(spec=device_utils.DeviceUtils)
- mock_device.RunShellCommand.configure_mock(
- return_value=[]
- )
-
- paths = system_app._GetApplicationPaths(mock_device, _PACKAGE_NAME)
-
- self.assertEquals([], paths)
- mock_device.RunShellCommand.assert_called_once_with(
- _PM_LIST_PACKAGES_COMMAND, check_return=True)
-
- def test_GetApplicationPaths_emptyName(self):
- """Called with empty name, should not return any packages."""
- # pylint: disable=protected-access
- mock_device = mock.Mock(spec=device_utils.DeviceUtils)
- mock_device.RunShellCommand.configure_mock(
- return_value=_PM_LIST_PACKAGES_OUTPUT_WITH_PATH
- )
-
- paths = system_app._GetApplicationPaths(mock_device, '')
-
- self.assertEquals([], paths)
- mock_device.RunShellCommand.assert_called_once_with(
- _PM_LIST_PACKAGES_COMMAND[:-1] + [''], check_return=True)
-
if __name__ == '__main__':
unittest.main()
diff --git a/systrace/catapult/devil/devil/android/tools/unlock_bootloader.py b/systrace/catapult/devil/devil/android/tools/unlock_bootloader.py
index b38f669..46fec9d 100644
--- a/systrace/catapult/devil/devil/android/tools/unlock_bootloader.py
+++ b/systrace/catapult/devil/devil/android/tools/unlock_bootloader.py
@@ -106,10 +106,8 @@ def unlock_bootloader(d):
logging.info('Device %s already unlocked.', d)
elif 'unlock is not allowed' in out:
logging.error("Device %s is oem locked. Can't unlock bootloader.", d)
- return 1
else:
logging.error('Device %s in unknown state: "%s"', d, out)
- return 1
break
if leftover_pids:
diff --git a/systrace/catapult/devil/devil/android/tools/webview_app.py b/systrace/catapult/devil/devil/android/tools/webview_app.py
deleted file mode 100755
index 36b7039..0000000
--- a/systrace/catapult/devil/devil/android/tools/webview_app.py
+++ /dev/null
@@ -1,205 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2019 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.
-
-"""A script to use a package as the WebView provider while running a command."""
-
-import argparse
-import contextlib
-import logging
-import os
-import re
-import sys
-
-
-if __name__ == '__main__':
- sys.path.append(
- os.path.abspath(os.path.join(os.path.dirname(__file__),
- '..', '..', '..')))
- sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__),
- '..', '..', '..', '..', 'common', 'py_utils')))
-
-
-from devil.android import apk_helper
-from devil.android import device_errors
-from devil.android.sdk import version_codes
-from devil.android.tools import script_common
-from devil.android.tools import system_app
-from devil.utils import cmd_helper
-from devil.utils import parallelizer
-from devil.utils import run_tests_helper
-from py_utils import tempfile_ext
-
-logger = logging.getLogger(__name__)
-
-_SYSTEM_PATH_RE = re.compile(r'^\s*\/system\/')
-_WEBVIEW_INSTALL_TIMEOUT = 300
-
-@contextlib.contextmanager
-def UseWebViewProvider(device, apk, expected_package=''):
- """A context manager that uses the apk as the webview provider while in scope.
-
- Args:
- device: (device_utils.DeviceUtils) The device for which the webview apk
- should be used as the provider.
- apk: (str) The path to the webview APK to use.
- expected_package: (str) If non-empty, verify apk's package name matches
- this value.
- """
- package_name = apk_helper.GetPackageName(apk)
-
- if expected_package:
- if package_name != expected_package:
- raise device_errors.CommandFailedError(
- 'WebView Provider package %s does not match expected %s' %
- (package_name, expected_package), str(device))
-
- if (device.build_version_sdk in
- [version_codes.NOUGAT, version_codes.NOUGAT_MR1]):
- logger.warning('Due to webviewupdate bug in Nougat, WebView Fallback Logic '
- 'will be disabled and WebView provider may be changed after '
- 'exit of UseWebViewProvider context manager scope.')
-
- webview_update = device.GetWebViewUpdateServiceDump()
- original_fallback_logic = webview_update.get('FallbackLogicEnabled', None)
- original_provider = webview_update.get('CurrentWebViewPackage', None)
-
- # This is only necessary if the provider is a fallback provider, but we can't
- # generally determine this, so we set this just in case.
- device.SetWebViewFallbackLogic(False)
-
- try:
- # If user installed versions of the package is present, they must be
- # uninstalled first, so that the system version of the package,
- # if any, can be found by the ReplaceSystemApp context manager
- with _UninstallNonSystemApp(device, package_name):
- all_paths = device.GetApplicationPaths(package_name)
- system_paths = _FilterPaths(all_paths, True)
- non_system_paths = _FilterPaths(all_paths, False)
- if non_system_paths:
- raise device_errors.CommandFailedError(
- 'Non-System application paths found after uninstallation: ',
- str(non_system_paths))
- elif system_paths:
- # app is system app, use ReplaceSystemApp to install
- with system_app.ReplaceSystemApp(
- device,
- package_name,
- apk,
- install_timeout=_WEBVIEW_INSTALL_TIMEOUT):
- _SetWebViewProvider(device, package_name)
- yield
- else:
- # app is not present on device, can directly install
- with _InstallApp(device, apk):
- _SetWebViewProvider(device, package_name)
- yield
- finally:
- # restore the original provider only if it was known and not the current
- # provider
- if original_provider is not None:
- webview_update = device.GetWebViewUpdateServiceDump()
- new_provider = webview_update.get('CurrentWebViewPackage', None)
- if new_provider != original_provider:
- device.SetWebViewImplementation(original_provider)
-
- # enable the fallback logic only if it was known to be enabled
- if original_fallback_logic is True:
- device.SetWebViewFallbackLogic(True)
-
-
-def _SetWebViewProvider(device, package_name):
- """ Set the WebView provider to the package_name if supported. """
- if device.build_version_sdk >= version_codes.NOUGAT:
- device.SetWebViewImplementation(package_name)
-
-
-def _FilterPaths(path_list, is_system):
- """ Return paths in the path_list that are/aren't system paths. """
- return [
- p for p in path_list if is_system == bool(re.match(_SYSTEM_PATH_RE, p))
- ]
-
-
-def _RebasePath(new_root, old_root):
- """ Graft old_root onto new_root and return the result. """
- return os.path.join(new_root, os.path.relpath(old_root, '/'))
-
-
-@contextlib.contextmanager
-def _UninstallNonSystemApp(device, package_name):
- """ Make package un-installed while in scope. """
- all_paths = device.GetApplicationPaths(package_name)
- user_paths = _FilterPaths(all_paths, False)
- host_paths = []
- if user_paths:
- with tempfile_ext.NamedTemporaryDirectory() as temp_dir:
- for user_path in user_paths:
- host_path = _RebasePath(temp_dir, user_path)
- # PullFile takes care of host_path creation if needed.
- device.PullFile(user_path, host_path)
- host_paths.append(host_path)
- device.Uninstall(package_name)
- try:
- yield
- finally:
- for host_path in reversed(host_paths):
- device.Install(host_path, reinstall=True,
- timeout=_WEBVIEW_INSTALL_TIMEOUT)
- else:
- yield
-
-
-@contextlib.contextmanager
-def _InstallApp(device, apk):
- """ Make apk installed while in scope. """
- package_name = apk_helper.GetPackageName(apk)
- device.Install(apk, reinstall=True, timeout=_WEBVIEW_INSTALL_TIMEOUT)
- try:
- yield
- finally:
- device.Uninstall(package_name)
-
-
-def main(raw_args):
- parser = argparse.ArgumentParser()
-
- def add_common_arguments(p):
- script_common.AddDeviceArguments(p)
- script_common.AddEnvironmentArguments(p)
- p.add_argument(
- '-v', '--verbose', action='count', default=0,
- help='Print more information.')
- p.add_argument('command', nargs='*')
-
- @contextlib.contextmanager
- def use_webview_provider(device, args):
- with UseWebViewProvider(device, args.apk, args.expected_package):
- yield
-
- parser.add_argument(
- '--apk', required=True,
- help='The apk to use as the provider.')
- parser.add_argument(
- '--expected-package', default='',
- help="Verify apk's package name matches value, disabled by default.")
- add_common_arguments(parser)
- parser.set_defaults(func=use_webview_provider)
-
- args = parser.parse_args(raw_args)
-
- run_tests_helper.SetLogLevel(args.verbose)
- script_common.InitializeEnvironment(args)
-
- devices = script_common.GetDevices(args.devices, args.blacklist_file)
- parallel_devices = parallelizer.SyncParallelizer(
- [args.func(d, args) for d in devices])
- with parallel_devices:
- if args.command:
- return cmd_helper.Call(args.command)
- return 0
-
-
-if __name__ == '__main__':
- sys.exit(main(sys.argv[1:]))
diff --git a/systrace/catapult/devil/devil/devil_dependencies.json b/systrace/catapult/devil/devil/devil_dependencies.json
index 8b39788..6884a36 100644
--- a/systrace/catapult/devil/devil/devil_dependencies.json
+++ b/systrace/catapult/devil/devil/devil_dependencies.json
@@ -6,7 +6,7 @@
"cloud_storage_bucket": "chromium-telemetry",
"file_info": {
"linux2_x86_64": {
- "cloud_storage_hash": "87bd288daab30624e41faa62aa2c1d5bac3e60aa",
+ "cloud_storage_hash": "16ba3180141a2489d7ec99b39fd6e3434a9a373f",
"download_path": "../bin/deps/linux2/x86_64/bin/aapt"
}
}
@@ -26,8 +26,8 @@
"cloud_storage_bucket": "chromium-telemetry",
"file_info": {
"linux2_x86_64": {
- "cloud_storage_hash": "9b986774ad27288a6777ebfa9a08fd8a52003008",
- "download_path": "../bin/deps/linux2/x86_64/lib64/libc++.so"
+ "cloud_storage_hash": "91cdce1e3bd81b2ac1fd380013896d0e2cdb40a0",
+ "download_path": "../bin/deps/linux2/x86_64/lib/libc++.so"
}
}
},
@@ -46,7 +46,7 @@
"cloud_storage_bucket": "chromium-telemetry",
"file_info": {
"linux2_x86_64": {
- "cloud_storage_hash": "c3fdf75afe8eb4062d66703cb556ee1e2064b8ae",
+ "cloud_storage_hash": "acfb10f7a868baf9bcf446a2d9f8ed6b5d52c3c6",
"download_path": "../bin/deps/linux2/x86_64/bin/dexdump"
}
}
@@ -55,10 +55,6 @@
"cloud_storage_base_folder": "binary_dependencies",
"cloud_storage_bucket": "chromium-telemetry",
"file_info": {
- "android_arm64-v8a": {
- "cloud_storage_hash": "34e583c631a495afbba82ce8a1d4f9b5118a4411",
- "download_path": "../bin/deps/android/arm64-v8a/apks/EmptySystemWebView.apk"
- },
"android_armeabi-v7a": {
"cloud_storage_hash": "220ff3ba1a6c3c81877997e32784ffd008f293a5",
"download_path": "../bin/deps/android/armeabi-v7a/apks/EmptySystemWebView.apk"
@@ -132,10 +128,10 @@
"cloud_storage_bucket": "chromium-telemetry",
"file_info": {
"linux2_x86_64": {
- "cloud_storage_hash": "c116fd0d7ff089561971c078317b75b90f053207",
+ "cloud_storage_hash": "abb9753a8d3efeea4144e328933931729e01571c",
"download_path": "../bin/deps/linux2/x86_64/bin/split-select"
}
}
}
}
-}
+} \ No newline at end of file
diff --git a/systrace/catapult/devil/devil/utils/__init__.py b/systrace/catapult/devil/devil/utils/__init__.py
index e69de29..ff84988 100644
--- a/systrace/catapult/devil/devil/utils/__init__.py
+++ b/systrace/catapult/devil/devil/utils/__init__.py
@@ -0,0 +1,23 @@
+# 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 os
+import sys
+
+def _JoinPath(*path_parts):
+ return os.path.abspath(os.path.join(*path_parts))
+
+
+def _AddDirToPythonPath(*path_parts):
+ path = _JoinPath(*path_parts)
+ if os.path.isdir(path) and path not in sys.path:
+ # Some call sites that use Telemetry assume that sys.path[0] is the
+ # directory containing the script, so we add these extra paths to right
+ # after sys.path[0].
+ sys.path.insert(1, path)
+
+_CATAPULT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)),
+ os.path.pardir, os.path.pardir, os.path.pardir)
+
+_AddDirToPythonPath(_CATAPULT_DIR, 'common', 'battor')
diff --git a/systrace/catapult/devil/devil/utils/battor_device_mapping.py b/systrace/catapult/devil/devil/utils/battor_device_mapping.py
new file mode 100755
index 0000000..8cabb83
--- /dev/null
+++ b/systrace/catapult/devil/devil/utils/battor_device_mapping.py
@@ -0,0 +1,309 @@
+#!/usr/bin/python
+# Copyright 2016 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.
+
+
+'''
+This script provides tools to map BattOrs to phones.
+
+Phones are identified by the following string:
+
+"Phone serial number" - Serial number of the phone. This can be
+obtained via 'adb devices' or 'usb-devices', and is not expected
+to change for a given phone.
+
+BattOrs are identified by the following two strings:
+
+"BattOr serial number" - Serial number of the BattOr. This can be
+obtained via 'usb-devices', and is not expected to change for
+a given BattOr.
+
+"BattOr path" - The path of the form '/dev/ttyUSB*' that is used
+to communicate with the BattOr (the battor_agent binary takes
+this BattOr path as a parameter). The BattOr path is frequently
+reassigned by the OS, most often when the device is disconnected
+and then reconnected. Thus, the BattOr path cannot be expected
+to be stable.
+
+In a typical application, the user will require the BattOr path
+for the BattOr that is plugged into a given phone. For instance,
+the user will be running tracing on a particular phone, and will
+need to know which BattOr path to use to communicate with the BattOr
+to get the corresponding power trace.
+
+Getting this mapping requires two steps: (1) determining the
+mapping between phone serial numbers and BattOr serial numbers, and
+(2) getting the BattOr path corresponding to a given BattOr serial
+number.
+
+For step (1), we generate a JSON file giving this mapping. This
+JSON file consists of a list of items of the following form:
+[{'phone': <phone serial 1>, 'battor': <battor serial 1>},
+{'phone': <phone serial 2>, 'battor': <battor serial 2>}, ...]
+
+The default way to generate this JSON file is using the function
+GenerateSerialMapFile, which generates a mapping based on assuming
+that the system has two identical USB hubs connected to it, and
+the phone plugged into physical port number 1 on one hub corresponds
+to the BattOr plugged into physical port number 1 on the other hub,
+and similarly with physical port numbers 2, 3, etc. This generates
+the map file based on the structure at the time GenerateSerialMapFile called.
+Note that after the map file is generated, port numbers are no longer used;
+the user could move around the devices in the ports without affecting
+which phone goes with which BattOr. (Thus, if the user wanted to update the
+mapping to match the new port connections, the user would have to
+re-generate this file.)
+
+The script update_mapping.py will do this updating from the command line.
+
+If the user wanted to specify a custom mapping, the user could instead
+create the JSON file manually. (In this case, hubs would not be necessary
+and the physical ports connected would be irrelevant.)
+
+Step (2) is conducted through the function GetBattOrPathFromPhoneSerial,
+which takes a serial number mapping generated via step (1) and a phone
+serial number, then gets the corresponding BattOr serial number from the
+map and determines its BattOr path (e.g. /dev/ttyUSB0). Since BattOr paths
+can change if devices are connected and disconnected (even if connected
+or disconnected via the same port) this function should be called to
+determine the BattOr path every time before connecting to the BattOr.
+
+Note that if there is only one BattOr connected to the system, then
+GetBattOrPathFromPhoneSerial will always return that BattOr and will ignore
+the mapping file. Thus, if the user never has more than one BattOr connected
+to the system, the user will not need to generate mapping files.
+'''
+
+
+import json
+import collections
+
+from battor import battor_error
+from devil.utils import find_usb_devices
+from devil.utils import usb_hubs
+
+
+def GetBattOrList(device_tree_map):
+ return [x for x in find_usb_devices.GetTTYList()
+ if IsBattOr(x, device_tree_map)]
+
+
+def IsBattOr(tty_string, device_tree_map):
+ (bus, device) = find_usb_devices.GetBusDeviceFromTTY(tty_string)
+ node = device_tree_map[bus].FindDeviceNumber(device)
+ return '0403:6001' in node.desc
+
+
+def GetBattOrSerialNumbers(device_tree_map):
+ for x in find_usb_devices.GetTTYList():
+ if IsBattOr(x, device_tree_map):
+ (bus, device) = find_usb_devices.GetBusDeviceFromTTY(x)
+ devnode = device_tree_map[bus].FindDeviceNumber(device)
+ yield devnode.serial
+
+
+def ReadSerialMapFile(filename):
+ """Reads JSON file giving phone-to-battor serial number map.
+
+ Parses a JSON file consisting of a list of items of the following form:
+ [{'phone': <phone serial 1>, 'battor': <battor serial 1>},
+ {'phone': <phone serial 2>, 'battor': <battor serial 2>}, ...]
+
+ indicating which phone serial numbers should be matched with
+ which BattOr serial numbers. Returns dictionary of the form:
+
+ {<phone serial 1>: <BattOr serial 1>,
+ <phone serial 2>: <BattOr serial 2>}
+
+ Args:
+ filename: Name of file to read.
+ """
+ result = {}
+ with open(filename, 'r') as infile:
+ in_dict = json.load(infile)
+ for x in in_dict:
+ result[x['phone']] = x['battor']
+ return result
+
+def WriteSerialMapFile(filename, serial_map):
+ """Writes a map of phone serial numbers to BattOr serial numbers to file.
+
+ Writes a JSON file consisting of a list of items of the following form:
+ [{'phone': <phone serial 1>, 'battor': <battor serial 1>},
+ {'phone': <phone serial 2>, 'battor': <battor serial 2>}, ...]
+
+ indicating which phone serial numbers should be matched with
+ which BattOr serial numbers. Mapping is based on the physical port numbers
+ of the hubs that the BattOrs and phones are connected to.
+
+ Args:
+ filename: Name of file to write.
+ serial_map: Serial map {phone: battor}
+ """
+ result = []
+ for (phone, battor) in serial_map.iteritems():
+ result.append({'phone': phone, 'battor': battor})
+ with open(filename, 'w') as outfile:
+ json.dump(result, outfile)
+
+def GenerateSerialMap(hub_types=None):
+ """Generates a map of phone serial numbers to BattOr serial numbers.
+
+ Generates a dict of:
+ {<phone serial 1>: <battor serial 1>,
+ <phone serial 2>: <battor serial 2>}
+ indicating which phone serial numbers should be matched with
+ which BattOr serial numbers. Mapping is based on the physical port numbers
+ of the hubs that the BattOrs and phones are connected to.
+
+ Args:
+ hub_types: List of hub types to check for. If not specified, checks
+ for all defined hub types. (see usb_hubs.py for details)
+ """
+ if hub_types:
+ hub_types = [usb_hubs.GetHubType(x) for x in hub_types]
+ else:
+ hub_types = usb_hubs.ALL_HUBS
+
+ devtree = find_usb_devices.GetBusNumberToDeviceTreeMap()
+
+ # List of serial numbers in the system that represent BattOrs.
+ battor_serials = list(GetBattOrSerialNumbers(devtree))
+
+ # If there's only one BattOr in the system, then a serial number ma
+ # is not necessary.
+ if len(battor_serials) == 1:
+ return {}
+
+ # List of dictionaries, one for each hub, that maps the physical
+ # port number to the serial number of that hub. For instance, in a 2
+ # hub system, this could return [{1:'ab', 2:'cd'}, {1:'jkl', 2:'xyz'}]
+ # where 'ab' and 'cd' are the phone serial numbers and 'jkl' and 'xyz'
+ # are the BattOr serial numbers.
+ port_to_serial = find_usb_devices.GetAllPhysicalPortToSerialMaps(
+ hub_types, device_tree_map=devtree)
+
+ class serials(object):
+ def __init__(self):
+ self.phone = None
+ self.battor = None
+
+ # Map of {physical port number: [phone serial #, BattOr serial #]. This
+ # map is populated by executing the code below. For instance, in the above
+ # example, after the code below is executed, port_to_devices would equal
+ # {1: ['ab', 'jkl'], 2: ['cd', 'xyz']}
+ port_to_devices = collections.defaultdict(serials)
+ for hub in port_to_serial:
+ for (port, serial) in hub.iteritems():
+ if serial in battor_serials:
+ if port_to_devices[port].battor is not None:
+ raise battor_error.BattOrError('Multiple BattOrs on same port number')
+ else:
+ port_to_devices[port].battor = serial
+ else:
+ if port_to_devices[port].phone is not None:
+ raise battor_error.BattOrError('Multiple phones on same port number')
+ else:
+ port_to_devices[port].phone = serial
+
+ # Turn the port_to_devices map into a map of the form
+ # {phone serial number: BattOr serial number}.
+ result = {}
+ for pair in port_to_devices.values():
+ if pair.phone is None:
+ continue
+ if pair.battor is None:
+ raise battor_error.BattOrError(
+ 'Phone detected with no corresponding BattOr')
+ result[pair.phone] = pair.battor
+ return result
+
+def GenerateSerialMapFile(filename, hub_types=None):
+ """Generates a serial map file and writes it."""
+ WriteSerialMapFile(filename, GenerateSerialMap(hub_types))
+
+def _PhoneToPathMap(serial, serial_map, devtree):
+ """Maps phone serial number to TTY path, assuming serial map is provided."""
+ try:
+ battor_serial = serial_map[serial]
+ except KeyError:
+ raise battor_error.BattOrError('Serial number not found in serial map.')
+ for tree in devtree.values():
+ for node in tree.AllNodes():
+ if isinstance(node, find_usb_devices.USBDeviceNode):
+ if node.serial == battor_serial:
+ bus_device_to_tty = find_usb_devices.GetBusDeviceToTTYMap()
+ bus_device = (node.bus_num, node.device_num)
+ try:
+ return bus_device_to_tty[bus_device]
+ except KeyError:
+ raise battor_error.BattOrError(
+ 'Device with given serial number not a BattOr '
+ '(does not have TTY path)')
+
+
+def GetBattOrPathFromPhoneSerial(serial, serial_map=None,
+ serial_map_file=None):
+ """Gets the TTY path (e.g. '/dev/ttyUSB0') to communicate with the BattOr.
+
+ (1) If serial_map is given, it is treated as a dictionary mapping
+ phone serial numbers to BattOr serial numbers. This function will get the
+ TTY path for the given BattOr serial number.
+
+ (2) If serial_map_file is given, it is treated as the name of a
+ phone-to-BattOr mapping file (generated with GenerateSerialMapFile)
+ and this will be loaded and used as the dict to map port numbers to
+ BattOr serial numbers.
+
+ You can only give one of serial_map and serial_map_file.
+
+ Args:
+ serial: Serial number of phone connected on the same physical port that
+ the BattOr is connected to.
+ serial_map: Map of phone serial numbers to BattOr serial numbers, given
+ as a dictionary.
+ serial_map_file: Map of phone serial numbers to BattOr serial numbers,
+ given as a file.
+ hub_types: List of hub types to check for. Used only if serial_map_file
+ is None.
+
+ Returns:
+ Device string used to communicate with device.
+
+ Raises:
+ ValueError: If serial number is not given.
+ BattOrError: If BattOr not found or unexpected USB topology.
+ """
+ # If there's only one BattOr connected to the system, just use that one.
+ # This allows for use on, e.g., a developer's workstation with no hubs.
+ devtree = find_usb_devices.GetBusNumberToDeviceTreeMap()
+ all_battors = GetBattOrList(devtree)
+ if len(all_battors) == 1:
+ return '/dev/' + all_battors[0]
+
+ if not serial:
+ raise battor_error.BattOrError(
+ 'Two or more BattOrs connected, no serial provided')
+
+ if serial_map and serial_map_file:
+ raise ValueError('Cannot specify both serial_map and serial_map_file')
+
+ if serial_map_file:
+ serial_map = ReadSerialMapFile(serial_map_file)
+
+ tty_string = _PhoneToPathMap(serial, serial_map, devtree)
+
+ if not tty_string:
+ raise battor_error.BattOrError(
+ 'No device with given serial number detected.')
+
+ if IsBattOr(tty_string, devtree):
+ return '/dev/' + tty_string
+ else:
+ raise battor_error.BattOrError(
+ 'Device with given serial number is not a BattOr.')
+
+if __name__ == '__main__':
+ # Main function for testing purposes
+ print GenerateSerialMap()
diff --git a/systrace/catapult/devil/devil/utils/cmd_helper.py b/systrace/catapult/devil/devil/utils/cmd_helper.py
index 3c4a06e..b477c70 100644
--- a/systrace/catapult/devil/devil/utils/cmd_helper.py
+++ b/systrace/catapult/devil/devil/utils/cmd_helper.py
@@ -4,7 +4,6 @@
"""A wrapper for subprocess to make calling shell commands easier."""
-import codecs
import logging
import os
import pipes
@@ -16,16 +15,11 @@ import subprocess
import sys
import time
-from devil import base_error
logger = logging.getLogger(__name__)
_SafeShellChars = frozenset(string.ascii_letters + string.digits + '@%_-+=:,./')
-# Cache the string-escape codec to ensure subprocess can find it
-# later. Return value doesn't matter.
-codecs.lookup('string-escape')
-
def SingleQuote(s):
"""Return an shell-escaped version of the string using single quotes.
@@ -158,12 +152,12 @@ def _ValidateAndLogCommand(args, cwd, shell):
else:
if shell:
raise Exception('array args must be run with shell=False')
- args = ' '.join(SingleQuote(str(c)) for c in args)
+ args = ' '.join(SingleQuote(c) for c in args)
if cwd is None:
cwd = ''
else:
cwd = ':' + cwd
- logger.debug('[host]%s> %s', cwd, args)
+ logger.info('[host]%s> %s', cwd, args)
return args
@@ -193,27 +187,6 @@ def GetCmdStatusAndOutput(args, cwd=None, shell=False, env=None):
return (status, stdout)
-def StartCmd(args, cwd=None, shell=False, env=None):
- """Starts a subprocess and returns a handle to the process.
-
- Args:
- args: A string or a sequence of program arguments. The program to execute is
- the string or the first item in the args sequence.
- cwd: If not None, the subprocess's current directory will be changed to
- |cwd| before it's executed.
- shell: Whether to execute args as a shell command. Must be True if args
- is a string and False if args is a sequence.
- env: If not None, a mapping that defines environment variables for the
- subprocess.
-
- Returns:
- A process handle from subprocess.Popen.
- """
- _ValidateAndLogCommand(args, cwd, shell)
- return Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
- shell=shell, cwd=cwd, env=env)
-
-
def GetCmdStatusOutputAndError(args, cwd=None, shell=False, env=None):
"""Executes a subprocess and returns its exit code, output, and errors.
@@ -237,11 +210,11 @@ def GetCmdStatusOutputAndError(args, cwd=None, shell=False, env=None):
return (pipe.returncode, stdout, stderr)
-class TimeoutError(base_error.BaseError):
+class TimeoutError(Exception):
"""Module-specific timeout exception."""
def __init__(self, output=None):
- super(TimeoutError, self).__init__('Timeout')
+ super(TimeoutError, self).__init__()
self._output = output
@property
@@ -253,7 +226,6 @@ def _IterProcessStdoutFcntl(
process, iter_timeout=None, timeout=None, buffer_size=4096,
poll_interval=1):
"""An fcntl-based implementation of _IterProcessStdout."""
- # pylint: disable=too-many-nested-blocks
import fcntl
try:
# Enable non-blocking reads from the child's stdout.
diff --git a/systrace/catapult/devil/devil/utils/find_usb_devices.py b/systrace/catapult/devil/devil/utils/find_usb_devices.py
index b6dcc70..74b888d 100755
--- a/systrace/catapult/devil/devil/utils/find_usb_devices.py
+++ b/systrace/catapult/devil/devil/utils/find_usb_devices.py
@@ -452,9 +452,9 @@ def GetBusDeviceFromTTY(tty_string):
for line in _GetTtyUSBInfo(tty_string).splitlines():
bus_match = _BUS_NUM_REGEX.match(line)
device_match = _DEVICE_NUM_REGEX.match(line)
- if bus_match and bus_num is None:
+ if bus_match and bus_num == None:
bus_num = int(bus_match.group(1))
- if device_match and device_num is None:
+ if device_match and device_num == None:
device_num = int(device_match.group(1))
if bus_num is None or device_num is None:
raise ValueError('Info not found')
diff --git a/systrace/catapult/devil/devil/utils/find_usb_devices_test.py b/systrace/catapult/devil/devil/utils/find_usb_devices_test.py
index c22f21b..e8b00c8 100755
--- a/systrace/catapult/devil/devil/utils/find_usb_devices_test.py
+++ b/systrace/catapult/devil/devil/utils/find_usb_devices_test.py
@@ -17,21 +17,23 @@ Bus 001:
Bus 002:
1: Device 011 "quux"
2: Device 020 "My Test HUB" #hub 1
-2:1: Device 021 "usb_device_p7_h1_t0" #physical port 7 on hub 1, on ttyUSB0
-2:3: Device 022 "usb_device_p5_h1_t1" #physical port 5 on hub 1, on ttyUSB1
+2:1: Device 021 "battor_p7_h1_t0" #physical port 7 on hub 1, on ttyUSB0
+2:3: Device 022 "battor_p5_h1_t1" #physical port 5 on hub 1, on ttyUSB1
2:4: Device 023 "My Test Internal HUB" #internal section of hub 1
-2:4:2: Device 024 "usb_device_p3_h1_t2" #physical port 3 on hub 1, on ttyUSB2
+2:4:2: Device 024 "battor_p3_h1_t2" #physical port 3 on hub 1, on ttyUSB2
2:4:3: Device 026 "Not a Battery Monitor" #physical port 1 on hub 1, on ttyUSB3
-2:4:4: Device 025 "usb_device_p1_h1_t3" #physical port 1 on hub 1, on ttyUSB3
+2:4:4: Device 025 "battor_p1_h1_t3" #physical port 1 on hub 1, on ttyUSB3
3: Device 100 "My Test HUB" #hub 2
3:4: Device 101 "My Test Internal HUB" #internal section of hub 2
-3:4:4: Device 102 "usb_device_p1_h2_t4" #physical port 1 on hub 2, on ttyusb4
+3:4:4: Device 102 "battor_p1_h2_t4" #physical port 1 on hub 2, on ttyusb4
"""
import logging
+import os
import unittest
from devil import devil_env
+from devil.utils import battor_device_mapping
from devil.utils import find_usb_devices
from devil.utils import lsusb
from devil.utils import usb_hubs
@@ -49,15 +51,15 @@ DEVLIST = [(1, 11, 'foo'),
(1, 13, 'baz'),
(2, 11, 'quux'),
(2, 20, 'My Test HUB'),
- (2, 21, 'ID 0403:6001 usb_device_p7_h1_t0'),
- (2, 22, 'ID 0403:6001 usb_device_p5_h1_t1'),
+ (2, 21, 'ID 0403:6001 battor_p7_h1_t0'),
+ (2, 22, 'ID 0403:6001 battor_p5_h1_t1'),
(2, 23, 'My Test Internal HUB'),
- (2, 24, 'ID 0403:6001 usb_device_p3_h1_t2'),
- (2, 25, 'ID 0403:6001 usb_device_p1_h1_t3'),
+ (2, 24, 'ID 0403:6001 battor_p3_h1_t2'),
+ (2, 25, 'ID 0403:6001 battor_p1_h1_t3'),
(2, 26, 'Not a Battery Monitor'),
(2, 100, 'My Test HUB'),
(2, 101, 'My Test Internal HUB'),
- (2, 102, 'ID 0403:6001 usb_device_p1_h1_t4')]
+ (2, 102, 'ID 0403:6001 battor_p1_h1_t4')]
LSUSB_OUTPUT = [
{'bus': b, 'device': d, 'desc': t, 'id': (1000*b)+d}
@@ -80,14 +82,14 @@ T: Bus=02 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 11 Spd=000 MxCh=00
T: Bus=02 Lev=00 Prnt=00 Port=01 Cnt=00 Dev#= 20 Spd=000 MxCh=00
T: Bus=02 Lev=00 Prnt=20 Port=00 Cnt=00 Dev#= 21 Spd=000 MxCh=00
-S: SerialNumber=UsbDevice0
+S: SerialNumber=BattOr0
T: Bus=02 Lev=00 Prnt=20 Port=02 Cnt=00 Dev#= 22 Spd=000 MxCh=00
-S: SerialNumber=UsbDevice1
+S: SerialNumber=BattOr1
T: Bus=02 Lev=00 Prnt=20 Port=03 Cnt=00 Dev#= 23 Spd=000 MxCh=00
T: Bus=02 Lev=00 Prnt=23 Port=01 Cnt=00 Dev#= 24 Spd=000 MxCh=00
-S: SerialNumber=UsbDevice2
+S: SerialNumber=BattOr2
T: Bus=02 Lev=00 Prnt=23 Port=03 Cnt=00 Dev#= 25 Spd=000 MxCh=00
-S: SerialNumber=UsbDevice3
+S: SerialNumber=BattOr3
T: Bus=02 Lev=00 Prnt=23 Port=02 Cnt=00 Dev#= 26 Spd=000 MxCh=00
T: Bus=02 Lev=00 Prnt=00 Port=02 Cnt=00 Dev#=100 Spd=000 MxCh=00
@@ -101,15 +103,15 @@ Bus 001 Device 012: FAST bar
Bus 001 Device 013: baz
Bus 002 Device 011: quux
Bus 002 Device 020: My Test HUB
-Bus 002 Device 021: ID 0403:6001 usb_device_p7_h1_t0
-Bus 002 Device 022: ID 0403:6001 usb_device_p5_h1_t1
+Bus 002 Device 021: ID 0403:6001 battor_p7_h1_t0
+Bus 002 Device 022: ID 0403:6001 battor_p5_h1_t1
Bus 002 Device 023: My Test Internal HUB
-Bus 002 Device 024: ID 0403:6001 usb_device_p3_h1_t2
-Bus 002 Device 025: ID 0403:6001 usb_device_p1_h1_t3
+Bus 002 Device 024: ID 0403:6001 battor_p3_h1_t2
+Bus 002 Device 025: ID 0403:6001 battor_p1_h1_t3
Bus 002 Device 026: Not a Battery Monitor
Bus 002 Device 100: My Test HUB
Bus 002 Device 101: My Test Internal HUB
-Bus 002 Device 102: ID 0403:6001 usb_device_p1_h1_t4
+Bus 002 Device 102: ID 0403:6001 battor_p1_h1_t4
'''
LIST_TTY_OUTPUT = '''
@@ -211,6 +213,17 @@ class USBScriptTest(unittest.TestCase):
lsusb.raw_lsusb = mock.Mock(
return_value=RAW_LSUSB_OUTPUT)
+ def testIsBattOr(self):
+ bd = find_usb_devices.GetBusNumberToDeviceTreeMap()
+ self.assertTrue(battor_device_mapping.IsBattOr('ttyUSB3', bd))
+ self.assertFalse(battor_device_mapping.IsBattOr('ttyUSB5', bd))
+
+ def testGetBattOrs(self):
+ bd = find_usb_devices.GetBusNumberToDeviceTreeMap()
+ self.assertEquals(battor_device_mapping.GetBattOrList(bd),
+ ['ttyUSB0', 'ttyUSB1', 'ttyUSB2',
+ 'ttyUSB3', 'ttyUSB4'])
+
def testGetTTYDevices(self):
pp = find_usb_devices.GetAllPhysicalPortToTTYMaps([TEST_HUB])
result = list(pp)
@@ -234,49 +247,131 @@ class USBScriptTest(unittest.TestCase):
def testGetSerialMapping(self):
pp = find_usb_devices.GetAllPhysicalPortToSerialMaps([TEST_HUB])
result = list(pp)
- self.assertEquals(result[0], {7:'UsbDevice0',
- 5:'UsbDevice1',
- 3:'UsbDevice2',
- 1:'UsbDevice3'})
+ self.assertEquals(result[0], {7:'BattOr0',
+ 5:'BattOr1',
+ 3:'BattOr2',
+ 1:'BattOr3'})
self.assertEquals(result[1], {})
def testFastDeviceDescriptions(self):
bd = find_usb_devices.GetBusNumberToDeviceTreeMap()
dev_foo = bd[1].FindDeviceNumber(11)
dev_bar = bd[1].FindDeviceNumber(12)
- dev_usb_device_p7_h1_t0 = bd[2].FindDeviceNumber(21)
+ dev_battor_p7_h1_t0 = bd[2].FindDeviceNumber(21)
self.assertEquals(dev_foo.desc, 'FAST foo')
self.assertEquals(dev_bar.desc, 'FAST bar')
- self.assertEquals(dev_usb_device_p7_h1_t0.desc,
- 'ID 0403:6001 usb_device_p7_h1_t0')
+ self.assertEquals(dev_battor_p7_h1_t0.desc,
+ 'ID 0403:6001 battor_p7_h1_t0')
def testDeviceDescriptions(self):
bd = find_usb_devices.GetBusNumberToDeviceTreeMap(fast=False)
dev_foo = bd[1].FindDeviceNumber(11)
dev_bar = bd[1].FindDeviceNumber(12)
- dev_usb_device_p7_h1_t0 = bd[2].FindDeviceNumber(21)
+ dev_battor_p7_h1_t0 = bd[2].FindDeviceNumber(21)
self.assertEquals(dev_foo.desc, 'foo')
self.assertEquals(dev_bar.desc, 'bar')
- self.assertEquals(dev_usb_device_p7_h1_t0.desc,
- 'ID 0403:6001 usb_device_p7_h1_t0')
+ self.assertEquals(dev_battor_p7_h1_t0.desc,
+ 'ID 0403:6001 battor_p7_h1_t0')
def testDeviceInformation(self):
bd = find_usb_devices.GetBusNumberToDeviceTreeMap(fast=False)
dev_foo = bd[1].FindDeviceNumber(11)
dev_bar = bd[1].FindDeviceNumber(12)
- dev_usb_device_p7_h1_t0 = bd[2].FindDeviceNumber(21)
+ dev_battor_p7_h1_t0 = bd[2].FindDeviceNumber(21)
self.assertEquals(dev_foo.info['id'], 1011)
self.assertEquals(dev_bar.info['id'], 1012)
- self.assertEquals(dev_usb_device_p7_h1_t0.info['id'], 2021)
+ self.assertEquals(dev_battor_p7_h1_t0.info['id'], 2021)
def testSerialNumber(self):
bd = find_usb_devices.GetBusNumberToDeviceTreeMap(fast=False)
dev_foo = bd[1].FindDeviceNumber(11)
dev_bar = bd[1].FindDeviceNumber(12)
- dev_usb_device_p7_h1_t0 = bd[2].FindDeviceNumber(21)
+ dev_battor_p7_h1_t0 = bd[2].FindDeviceNumber(21)
self.assertEquals(dev_foo.serial, 'FooSerial')
self.assertEquals(dev_bar.serial, 'BarSerial')
- self.assertEquals(dev_usb_device_p7_h1_t0.serial, 'UsbDevice0')
+ self.assertEquals(dev_battor_p7_h1_t0.serial, 'BattOr0')
+
+ def testBattOrDictMapping(self):
+ map_dict = {'Phone1':'BattOr1', 'Phone2':'BattOr2', 'Phone3':'BattOr3'}
+ a1 = battor_device_mapping.GetBattOrPathFromPhoneSerial(
+ 'Phone1', serial_map=map_dict)
+ a2 = battor_device_mapping.GetBattOrPathFromPhoneSerial(
+ 'Phone2', serial_map=map_dict)
+ a3 = battor_device_mapping.GetBattOrPathFromPhoneSerial(
+ 'Phone3', serial_map=map_dict)
+ self.assertEquals(a1, '/dev/ttyUSB1')
+ self.assertEquals(a2, '/dev/ttyUSB2')
+ self.assertEquals(a3, '/dev/ttyUSB3')
+
+ def testBattOrDictFromFileMapping(self):
+ try:
+ map_dict = {'Phone1':'BattOr1', 'Phone2':'BattOr2', 'Phone3':'BattOr3'}
+ curr_dir = os.path.dirname(os.path.realpath(__file__))
+ filename = os.path.join(curr_dir, 'test', 'data', 'test_write_map.json')
+ battor_device_mapping.WriteSerialMapFile(filename, map_dict)
+ a1 = battor_device_mapping.GetBattOrPathFromPhoneSerial(
+ 'Phone1', serial_map_file=filename)
+ a2 = battor_device_mapping.GetBattOrPathFromPhoneSerial(
+ 'Phone2', serial_map_file=filename)
+ a3 = battor_device_mapping.GetBattOrPathFromPhoneSerial(
+ 'Phone3', serial_map_file=filename)
+ finally:
+ os.remove(filename)
+ self.assertEquals(a1, '/dev/ttyUSB1')
+ self.assertEquals(a2, '/dev/ttyUSB2')
+ self.assertEquals(a3, '/dev/ttyUSB3')
+
+ def testReadSerialMapFile(self):
+ curr_dir = os.path.dirname(os.path.realpath(__file__))
+ map_dict = battor_device_mapping.ReadSerialMapFile(
+ os.path.join(curr_dir, 'test', 'data', 'test_serial_map.json'))
+ self.assertEquals(len(map_dict.keys()), 3)
+ self.assertEquals(map_dict['Phone1'], 'BattOr1')
+ self.assertEquals(map_dict['Phone2'], 'BattOr2')
+ self.assertEquals(map_dict['Phone3'], 'BattOr3')
+
+original_PPTSM = find_usb_devices.GetAllPhysicalPortToSerialMaps
+original_PPTTM = find_usb_devices.GetAllPhysicalPortToTTYMaps
+original_GBL = battor_device_mapping.GetBattOrList
+original_GBNDM = find_usb_devices.GetBusNumberToDeviceTreeMap
+original_IB = battor_device_mapping.IsBattOr
+original_GBSM = battor_device_mapping.GetBattOrSerialNumbers
+
+def setup_battor_test(serial, tty, battor, bser=None):
+ serial_mapper = mock.Mock(return_value=serial)
+ tty_mapper = mock.Mock(return_value=tty)
+ battor_lister = mock.Mock(return_value=battor)
+ devtree = mock.Mock(return_value=None)
+ is_battor = mock.Mock(side_effect=lambda x, y: x in battor)
+ battor_serials = mock.Mock(return_value=bser)
+ find_usb_devices.GetAllPhysicalPortToSerialMaps = serial_mapper
+ find_usb_devices.GetAllPhysicalPortToTTYMaps = tty_mapper
+ battor_device_mapping.GetBattOrList = battor_lister
+ find_usb_devices.GetBusNumberToDeviceTreeMap = devtree
+ battor_device_mapping.IsBattOr = is_battor
+ battor_device_mapping.GetBattOrSerialNumbers = battor_serials
+
+class BattOrMappingTest(unittest.TestCase):
+ def tearDown(self):
+ find_usb_devices.GetAllPhysicalPortToSerialMaps = original_PPTSM
+ find_usb_devices.GetAllPhysicalPortToTTYMaps = original_PPTTM
+ battor_device_mapping.GetBattOrList = original_GBL
+ find_usb_devices.GetBusNumberToDeviceTreeMap = original_GBNDM
+ battor_device_mapping.IsBattOr = original_IB
+ battor_device_mapping.GetBattOrSerialNumbers = original_GBSM
+
+ def test_generate_serial_map(self):
+ setup_battor_test([{1:'Phn1', 2:'Phn2', 3:'Phn3'},
+ {1:'Bat1', 2:'Bat2', 3:'Bat3'}],
+ [{},
+ {1:'ttyUSB0', 2:'ttyUSB1', 3:'ttyUSB2'}],
+ ['ttyUSB0', 'ttyUSB1', 'ttyUSB2'],
+ ['Bat1', 'Bat2', 'Bat3'])
+ result = battor_device_mapping.GenerateSerialMap()
+ self.assertEqual(len(result), 3)
+ self.assertEqual(result['Phn1'], 'Bat1')
+ self.assertEqual(result['Phn2'], 'Bat2')
+ self.assertEqual(result['Phn3'], 'Bat3')
if __name__ == "__main__":
diff --git a/systrace/catapult/devil/devil/utils/lazy/weak_constant.py b/systrace/catapult/devil/devil/utils/lazy/weak_constant.py
index 24ad940..3558f29 100644
--- a/systrace/catapult/devil/devil/utils/lazy/weak_constant.py
+++ b/systrace/catapult/devil/devil/utils/lazy/weak_constant.py
@@ -4,9 +4,6 @@
import threading
-from devil.utils import reraiser_thread
-from devil.utils import timeout_retry
-
class WeakConstant(object):
"""A thread-safe, lazily initialized object.
@@ -16,27 +13,17 @@ class WeakConstant(object):
"""
def __init__(self, initializer):
- self._initialized = threading.Event()
+ self._initialized = False
self._initializer = initializer
self._lock = threading.Lock()
self._val = None
def read(self):
"""Get the object, creating it if necessary."""
- if self._initialized.is_set():
+ if self._initialized:
return self._val
with self._lock:
- if not self._initialized.is_set():
- # We initialize the value on a separate thread to protect
- # from holding self._lock indefinitely in the event that
- # self._initializer hangs.
- initializer_thread = reraiser_thread.ReraiserThread(
- self._initializer)
- initializer_thread.start()
- timeout_retry.WaitFor(
- lambda: initializer_thread.join(1) or not initializer_thread.isAlive(),
- wait_period=0)
- self._val = initializer_thread.GetReturnValue()
- self._initialized.set()
-
+ if not self._initialized:
+ self._val = self._initializer()
+ self._initialized = True
return self._val
diff --git a/systrace/catapult/devil/devil/utils/lazy/weak_constant_test.py b/systrace/catapult/devil/devil/utils/lazy/weak_constant_test.py
deleted file mode 100644
index 643351d..0000000
--- a/systrace/catapult/devil/devil/utils/lazy/weak_constant_test.py
+++ /dev/null
@@ -1,70 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2018 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.
-
-# pylint: disable=protected-access
-
-import time
-import unittest
-
-from devil import devil_env
-from devil.utils import lazy
-from devil.utils import timeout_retry
-
-with devil_env.SysPath(devil_env.PYMOCK_PATH):
- import mock
-
-
-class DynamicSideEffect(object):
- """A helper object for handling a sequence of single-use side effects."""
-
- def __init__(self, side_effects):
- self._side_effects = iter(side_effects or [])
-
- def __call__(self):
- val = next(self._side_effects)()
- if isinstance(val, Exception):
- raise val
- return val
-
-
-class WeakConstantTest(unittest.TestCase):
-
- def testUninitialized(self):
- """Ensure that the first read calls the initializer."""
- initializer = mock.Mock(return_value='initializer called')
- test_constant = lazy.WeakConstant(initializer)
- self.assertEquals(
- 'initializer called',
- test_constant.read())
- initializer.assert_called_once_with()
-
- def testInitialized(self):
- """Ensure that reading doesn't reinitialize the value."""
- initializer = mock.Mock(return_value='initializer called')
- test_constant = lazy.WeakConstant(initializer)
- test_constant._initialized.set()
- test_constant._val = 'initializer not called'
- self.assertEquals(
- 'initializer not called',
- test_constant.read())
- self.assertFalse(initializer.mock_calls) # assert not called
-
- def testFirstCallHangs(self):
- """Ensure that reading works even if the first initializer call hangs."""
- dyn = DynamicSideEffect([
- lambda: time.sleep(10),
- lambda: 'second try worked!'
- ])
-
- initializer = mock.Mock(side_effect=dyn)
- test_constant = lazy.WeakConstant(initializer)
- self.assertEquals(
- 'second try worked!',
- timeout_retry.Run(test_constant.read, 1, 1))
- initializer.assert_has_calls([mock.call(), mock.call()])
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/systrace/catapult/devil/devil/utils/logging_common.py b/systrace/catapult/devil/devil/utils/logging_common.py
index ab364a2..5aea3c6 100644
--- a/systrace/catapult/devil/devil/utils/logging_common.py
+++ b/systrace/catapult/devil/devil/utils/logging_common.py
@@ -8,32 +8,13 @@ import time
def AddLoggingArguments(parser):
- """Adds standard logging flags to the parser.
-
- After parsing args, remember to invoke InitializeLogging() with the parsed
- args, to configure the log level.
- """
- group = parser.add_mutually_exclusive_group()
- group.add_argument(
+ parser.add_argument(
'-v', '--verbose', action='count', default=0,
help='Log more. Use multiple times for even more logging.')
- group.add_argument(
- '-q', '--quiet', action='count', default=0,
- help=('Log less (suppress output). Use multiple times for even less '
- 'output.'))
def InitializeLogging(args, handler=None):
- """Initialized the log level based on commandline flags.
-
- This expects to be given an "args" object with the options defined by
- AddLoggingArguments().
- """
- if args.quiet >= 2:
- log_level = logging.CRITICAL
- elif args.quiet == 1:
- log_level = logging.ERROR
- elif args.verbose == 0:
+ if args.verbose == 0:
log_level = logging.WARNING
elif args.verbose == 1:
log_level = logging.INFO
diff --git a/systrace/catapult/devil/devil/utils/markdown.py b/systrace/catapult/devil/devil/utils/markdown.py
index ba66664..54e7ed5 100755
--- a/systrace/catapult/devil/devil/utils/markdown.py
+++ b/systrace/catapult/devil/devil/utils/markdown.py
@@ -3,8 +3,6 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-from __future__ import print_function
-
import argparse
import imp
import os
@@ -184,7 +182,7 @@ def md_module(module_obj, module_path=None, module_link=None):
A list of markdown-formatted lines.
"""
def should_doc(name):
- return (not isinstance(module_obj.__dict__[name], types.ModuleType)
+ return (type(module_obj.__dict__[name]) != types.ModuleType
and not name.startswith('_'))
stuff_to_doc = sorted(
@@ -195,9 +193,9 @@ def md_module(module_obj, module_path=None, module_link=None):
functions_to_doc = []
for s in stuff_to_doc:
- if isinstance(s, types.TypeType):
+ if type(s) == types.TypeType:
classes_to_doc.append(s)
- elif isinstance(s, types.FunctionType):
+ elif type(s) == types.FunctionType:
functions_to_doc.append(s)
command = ['devil/utils/markdown.py']
@@ -223,7 +221,7 @@ def md_module(module_obj, module_path=None, module_link=None):
for f in functions_to_doc:
content += md_function(f)
- print('\n'.join(content))
+ print '\n'.join(content)
return 0
@@ -245,7 +243,7 @@ def md_class(class_obj):
content.extend(md_docstring(class_obj.__doc__))
def should_doc(name, obj):
- return (isinstance(obj, types.FunctionType)
+ return (type(obj) == types.FunctionType
and (name.startswith('__') or not name.startswith('_')))
methods_to_doc = sorted(
diff --git a/systrace/catapult/devil/devil/utils/reraiser_thread.py b/systrace/catapult/devil/devil/utils/reraiser_thread.py
index 6e6c810..56d95f3 100644
--- a/systrace/catapult/devil/devil/utils/reraiser_thread.py
+++ b/systrace/catapult/devil/devil/utils/reraiser_thread.py
@@ -11,14 +11,12 @@ import threading
import time
import traceback
-from devil import base_error
from devil.utils import watchdog_timer
-class TimeoutError(base_error.BaseError):
+class TimeoutError(Exception):
"""Module-specific timeout exception."""
- def __init__(self, message):
- super(TimeoutError, self).__init__(message)
+ pass
def LogThreadStack(thread, error_log_func=logging.critical):
@@ -49,13 +47,10 @@ class ReraiserThread(threading.Thread):
func: callable to call on a new thread.
args: list of positional arguments for callable, defaults to empty.
kwargs: dictionary of keyword arguments for callable, defaults to empty.
- name: thread name, defaults to the function name.
+ name: thread name, defaults to Thread-N.
"""
- if not name:
- if hasattr(func, '__name__') and func.__name__ != '<lambda>':
- name = func.__name__
- else:
- name = 'anonymous'
+ if not name and func.__name__ != '<lambda>':
+ name = func.__name__
super(ReraiserThread, self).__init__(name=name)
if not args:
args = []
@@ -69,17 +64,10 @@ class ReraiserThread(threading.Thread):
self._exc_info = None
self._thread_group = None
- if sys.version_info < (3,):
- # pylint: disable=exec-used
- exec('''def ReraiseIfException(self):
- """Reraise exception if an exception was raised in the thread."""
- if self._exc_info:
- raise self._exc_info[0], self._exc_info[1], self._exc_info[2]''')
- else:
- def ReraiseIfException(self):
- """Reraise exception if an exception was raised in the thread."""
- if self._exc_info:
- raise self._exc_info[1]
+ def ReraiseIfException(self):
+ """Reraise exception if an exception was raised in the thread."""
+ if self._exc_info:
+ raise self._exc_info[0], self._exc_info[1], self._exc_info[2]
def GetReturnValue(self):
"""Reraise exception if present, otherwise get the return value."""
diff --git a/systrace/catapult/devil/devil/utils/reset_usb.py b/systrace/catapult/devil/devil/utils/reset_usb.py
index 404a44c..0335227 100755
--- a/systrace/catapult/devil/devil/utils/reset_usb.py
+++ b/systrace/catapult/devil/devil/utils/reset_usb.py
@@ -3,15 +3,12 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import sys
-if sys.platform == 'win32':
- raise ImportError('devil.utils.reset_usb only supported on unix systems.')
-
import argparse
import fcntl
import logging
import os
import re
+import sys
if __name__ == '__main__':
sys.path.append(
diff --git a/systrace/catapult/devil/devil/utils/run_tests_helper.py b/systrace/catapult/devil/devil/utils/run_tests_helper.py
index 0b9dd47..7f71b65 100644
--- a/systrace/catapult/devil/devil/utils/run_tests_helper.py
+++ b/systrace/catapult/devil/devil/utils/run_tests_helper.py
@@ -14,7 +14,7 @@ CustomFormatter = logging_common.CustomFormatter
_WrappedLoggingArgs = collections.namedtuple(
- '_WrappedLoggingArgs', ['verbose', 'quiet'])
+ '_WrappedLoggingArgs', ['verbose'])
def SetLogLevel(verbose_count, add_handler=True):
@@ -25,5 +25,5 @@ def SetLogLevel(verbose_count, add_handler=True):
add_handler: If true, adds a handler with |CustomFormatter|.
"""
logging_common.InitializeLogging(
- _WrappedLoggingArgs(verbose_count, 0),
+ _WrappedLoggingArgs(verbose_count),
handler=None if add_handler else logging.NullHandler())
diff --git a/systrace/catapult/devil/devil/utils/test/data/test_serial_map.json b/systrace/catapult/devil/devil/utils/test/data/test_serial_map.json
new file mode 100644
index 0000000..f068281
--- /dev/null
+++ b/systrace/catapult/devil/devil/utils/test/data/test_serial_map.json
@@ -0,0 +1 @@
+[{"phone": "Phone1", "battor": "BattOr1"}, {"phone": "Phone2", "battor": "BattOr2"}, {"phone": "Phone3", "battor": "BattOr3"}]
diff --git a/systrace/catapult/devil/devil/utils/timeout_retry.py b/systrace/catapult/devil/devil/utils/timeout_retry.py
index d662c1d..2327b6b 100644
--- a/systrace/catapult/devil/devil/utils/timeout_retry.py
+++ b/systrace/catapult/devil/devil/utils/timeout_retry.py
@@ -109,8 +109,7 @@ def WaitFor(condition, wait_period=5, max_tries=None):
# pylint: disable=no-member
timeout_thread_group.GetRemainingTime(wait_period,
suffix=' waiting for condition %r' % condition_name)
- if wait_period:
- time.sleep(wait_period)
+ time.sleep(wait_period)
return None
diff --git a/systrace/catapult/devil/devil/utils/update_mapping.py b/systrace/catapult/devil/devil/utils/update_mapping.py
new file mode 100755
index 0000000..6666b9b
--- /dev/null
+++ b/systrace/catapult/devil/devil/utils/update_mapping.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+
+# Copyright 2016 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 argparse
+import sys
+
+from devil.utils import battor_device_mapping
+
+def parse_options():
+ """Parses and checks the command-line options.
+
+ Returns:
+ A tuple containing the options structure.
+ """
+ usage = 'Usage: ./update_mapping.py [options]'
+ desc = ('Example: ./update_mapping.py -o mapping.json.\n'
+ 'This script generates and stores a file that gives the\n'
+ 'mapping between phone serial numbers and BattOr serial numbers\n'
+ 'Mapping is based on which physical ports on the USB hubs the\n'
+ 'devices are plugged in to. For instance, if there are two hubs,\n'
+ 'the phone connected to port N on the first hub is mapped to the\n'
+ 'BattOr connected to port N on the second hub, for each N.')
+ parser = argparse.ArgumentParser(usage=usage, description=desc)
+ parser.add_argument('-o', '--output', dest='out_file',
+ default='mapping.json', type=str,
+ action='store', help='mapping file name')
+ parser.add_argument('-u', '--hub', dest='hub_types',
+ action='append', choices=['plugable_7port',
+ 'plugable_7port_usb3_part2',
+ 'plugable_7port_usb3_part3'],
+ help='USB hub types.')
+ options = parser.parse_args()
+ if not options.hub_types:
+ options.hub_types = ['plugable_7port', 'plugable_7port_usb3_part2',
+ 'plugable_7port_usb3_part3']
+ return options
+
+def main():
+ options = parse_options()
+ battor_device_mapping.GenerateSerialMapFile(options.out_file,
+ options.hub_types)
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/systrace/catapult/devil/pylintrc b/systrace/catapult/devil/pylintrc
index 1a059cf..7e024a2 100644
--- a/systrace/catapult/devil/pylintrc
+++ b/systrace/catapult/devil/pylintrc
@@ -14,7 +14,6 @@ disable=
locally-enabled,
missing-docstring,
star-args,
- wrong-import-position,
[REPORTS]