aboutsummaryrefslogtreecommitdiff
path: root/catapult/devil
diff options
context:
space:
mode:
authorWei Wang <wvw@google.com>2021-01-31 14:41:15 -0800
committerWei Wang <wvw@google.com>2021-01-31 14:41:15 -0800
commit07a9f048f159df9fcedabcb0359ece38549aa8ce (patch)
tree4fae679f441b84389fa75886c45a246891176bba /catapult/devil
parent01475bd1c746c98178fdc60cb665074e0b1c5e4c (diff)
downloadchromium-trace-07a9f048f159df9fcedabcb0359ece38549aa8ce.tar.gz
Update catapult to latest version (91735e2e6)
git log --oneline --no-merges c4d3ff474..91735e2e6 tracing systrace 91735e2e6 Rename webuiMetric to customMetric 7a253c40a [v8][wasm] Match compilation events by ID 7646ce870 [wasm] Add metric for WebAssembly sync compilation 9d5ec4692 Add CppGC events to v8 gc metrics bb9414aff [tracing] Fix DOCUMENTATION_URLS reserved name in html ae206b8b8 [trace-viewer] Speed up event histogram computation de6e3a9d8 [trace-viewer] Fix displaying args when value is 0. be9a4317e Fix typo and add quotes for better readability 92c771005 Revert "Reland "Switch to vpython version of mock"" f22b977bc Reland "Switch to vpython version of mock" a4d2246a2 Add freezing / roughness to media telemetry tests 8b9cff42b Revert "Switch to vpython version of mock" 2c271ab27 Switch to vpython version of mock e43bd0925 WebUI metric: Implement benchmark_value 4565794f5 Add ProcessCreateToApplicationStart metric to androidStartup 88efddc6a Implement the unified V8 and CppGC metric computation 5537c0394 PCScan: Add metrics for PCScan. 146912bb6 blink_gc_metric: Fix missing import. 759827265 Fix NaN that appears in RunningStatistics::meanlogs a629d81fa Revert "Add .vpython file for histogram_proto.py." cb4bb2088 Add .vpython file for histogram_proto.py. 2ed6fc040 atrace_agent: pass device_serial to get_tracing_path when possible. 0c019226a [tbmv2] Add a way to add alert groupings to histograms 80f68fb06 Remove uma_metric Subtract 6fe79c7c6 Fork WebView startup metric to use for WebLayer b8b4d61dd Implement webuiMetric da58605a6 Start using polyfill for tracing ui tests 888c27281 Fix some tracing UI tests under the polyfill 584ab4910 GN: Export tracing include directory. 2019c0bb9 Change :generate_about_tracing to be a python2_action. 703029135 Add a way to skip tests 87da8e01e Add msm gpu trace support 22b0de95c trace-viewer: Don't show warning bar for polyfill 982bf51c4 Add way to have a hidden container slice cd48ca049 [uma metric] Restrict check for linear bins. 3fc3588cd [uma metric] Show the name of the metric on error. b7c1c3f11 fix v8 sample processing in multiprocess context 504ee6e60 Revert "Reland #2 "Make Catapult build as a part of Chromium under Python 3."" a989f948c Remove frame_lengths from frame_time metrics c70f06e2c Reland #2 "Make Catapult build as a part of Chromium under Python 3." 7ad7a98bf Revert "Reland "Make Catapult build as a part of Chromium under Python 3."" 532e564f6 Reland "Make Catapult build as a part of Chromium under Python 3." b494582b9 Revert "Make Catapult build as a part of Chromium under Python 3." 25c6e0901 Make Catapult build as a part of Chromium under Python 3. 1d8e2d5ac Include the first valid tab switch latency cb190dce0 [rendering benchmark] Remove a noisy metric. 4e51400c4 [systrace] Show absolute timestamp for selected slices 69d93258d heap: Add epoch based metric for blink gc 9f3ce85ba heap: Add ServiceWorker threads to gc metrics. Test: systrace Bug: 178973505 Change-Id: Ic59e217c8969bd10e446836692fc2873c8a7399c
Diffstat (limited to 'catapult/devil')
-rwxr-xr-xcatapult/devil/bin/generate_md_docs2
-rwxr-xr-xcatapult/devil/bin/run_py_devicetests2
-rwxr-xr-xcatapult/devil/bin/run_py_tests2
-rw-r--r--catapult/devil/build/cipd.yaml7
-rw-r--r--catapult/devil/devil/android/device_blacklist.py9
-rw-r--r--catapult/devil/devil/android/device_errors.py9
-rw-r--r--catapult/devil/devil/android/device_utils.py140
-rwxr-xr-xcatapult/devil/devil/android/device_utils_test.py248
-rw-r--r--catapult/devil/devil/android/forwarder.py13
-rw-r--r--catapult/devil/devil/android/sdk/adb_wrapper.py37
-rwxr-xr-xcatapult/devil/devil/android/sdk/adb_wrapper_test.py9
-rw-r--r--catapult/devil/devil/android/sdk/bundletool.py4
-rwxr-xr-xcatapult/devil/devil/android/tools/device_monitor.py5
-rwxr-xr-xcatapult/devil/devil/android/tools/device_recovery.py8
-rwxr-xr-xcatapult/devil/devil/android/tools/device_status.py5
-rwxr-xr-xcatapult/devil/devil/android/tools/provision_devices.py9
-rw-r--r--catapult/devil/devil/android/tools/script_common.py19
-rwxr-xr-xcatapult/devil/devil/android/tools/system_app.py5
-rwxr-xr-xcatapult/devil/devil/android/tools/webview_app.py16
-rw-r--r--catapult/devil/devil/devil_dependencies.json2
-rw-r--r--catapult/devil/devil/devil_env.py5
-rwxr-xr-xcatapult/devil/devil/utils/update_dependencies.py2
-rw-r--r--catapult/devil/devil/utils/zip_utils.py13
-rw-r--r--catapult/devil/devil/utils/zip_utils_test.py5
24 files changed, 309 insertions, 267 deletions
diff --git a/catapult/devil/bin/generate_md_docs b/catapult/devil/bin/generate_md_docs
index 1cca44c1..d1dbf06f 100755
--- a/catapult/devil/bin/generate_md_docs
+++ b/catapult/devil/bin/generate_md_docs
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env vpython
# 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.
diff --git a/catapult/devil/bin/run_py_devicetests b/catapult/devil/bin/run_py_devicetests
index 6a6da188..9329f3a8 100755
--- a/catapult/devil/bin/run_py_devicetests
+++ b/catapult/devil/bin/run_py_devicetests
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env vpython
# 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.
diff --git a/catapult/devil/bin/run_py_tests b/catapult/devil/bin/run_py_tests
index 112451c6..be245c6c 100755
--- a/catapult/devil/bin/run_py_tests
+++ b/catapult/devil/bin/run_py_tests
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env vpython
# 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.
diff --git a/catapult/devil/build/cipd.yaml b/catapult/devil/build/cipd.yaml
index fcb4db3b..204400a9 100644
--- a/catapult/devil/build/cipd.yaml
+++ b/catapult/devil/build/cipd.yaml
@@ -1,3 +1,9 @@
+# Copyright 2020 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.
+
+# To create CIPD package run the following command.
+# cipd create --pkg-def cipd.yaml -ref latest
package: chromium/third_party/catapult/devil/${platform}
description: All of devil along with its dependencies in catapult.
@@ -11,4 +17,5 @@ data:
- dir: common/py_utils
- dir: dependency_manager
- dir: devil
+ - dir: third_party/gsutil
- dir: third_party/zipfile
diff --git a/catapult/devil/devil/android/device_blacklist.py b/catapult/devil/devil/android/device_blacklist.py
deleted file mode 100644
index 7112204e..00000000
--- a/catapult/devil/devil/android/device_blacklist.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright 2014 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.
-
-from devil.android import device_denylist
-
-# TODO(crbug.com/1097306): Remove this (and this file) once existing uses
-# have switched to using device_denylist directly.
-Blacklist = device_denylist.Denylist
diff --git a/catapult/devil/devil/android/device_errors.py b/catapult/devil/devil/android/device_errors.py
index d1454683..6e710876 100644
--- a/catapult/devil/devil/android/device_errors.py
+++ b/catapult/devil/devil/android/device_errors.py
@@ -14,6 +14,7 @@ The class hierarchy for device exceptions is:
| +-- FastbootCommandFailedError
| +-- DeviceVersionError
| +-- DeviceChargingError
+ | +-- RootUserBuildError
+-- CommandTimeoutError
+-- DeviceUnreachableError
+-- NoDevicesError
@@ -228,3 +229,11 @@ class DeviceChargingError(CommandFailedError):
def __init__(self, message, device_serial=None):
super(DeviceChargingError, self).__init__(message, device_serial)
+
+
+class RootUserBuildError(CommandFailedError):
+ """Exception for being unable to root a device with "user" build."""
+
+ def __init__(self, message=None, device_serial=None):
+ super(RootUserBuildError, self).__init__(
+ message or 'Unable to root device with user build.', device_serial)
diff --git a/catapult/devil/devil/android/device_utils.py b/catapult/devil/devil/android/device_utils.py
index 0a041edb..7b7dad24 100644
--- a/catapult/devil/devil/android/device_utils.py
+++ b/catapult/devil/devil/android/device_utils.py
@@ -45,7 +45,8 @@ from devil.utils import reraiser_thread
from devil.utils import timeout_retry
from devil.utils import zip_utils
-from py_utils import tempfile_ext
+with devil_env.SysPath(devil_env.PY_UTILS_PATH):
+ from py_utils import tempfile_ext
try:
from devil.utils import reset_usb
@@ -132,6 +133,7 @@ _PERMISSIONS_DENYLIST_RE = re.compile('|'.join(
'android.permission.MANAGE_ACCOUNTS',
'android.permission.MODIFY_AUDIO_SETTINGS',
'android.permission.NFC',
+ 'android.permission.QUERY_ALL_PACKAGES',
'android.permission.READ_SYNC_SETTINGS',
'android.permission.READ_SYNC_STATS',
'android.permission.RECEIVE_BOOT_COMPLETED',
@@ -277,6 +279,15 @@ _WEBVIEW_SYSUPDATE_MIN_VERSION_CODE = re.compile(
_GOOGLE_FEATURES_RE = re.compile(r'^\s*com\.google\.')
+_EMULATOR_RE = re.compile(r'^generic_.*$')
+
+# Regular expressions for determining if a package is installed using the
+# output of `dumpsys package`.
+# Matches lines like "Package [com.google.android.youtube] (c491050):".
+# or "Package [org.chromium.trichromelibrary_425300033] (e476383):"
+_DUMPSYS_PACKAGE_RE_STR =\
+ r'^\s*Package\s*\[%s(_(?P<version_code>\d*))?\]\s*\(\w*\):$'
+
PS_COLUMNS = ('name', 'pid', 'ppid')
ProcessInfo = collections.namedtuple('ProcessInfo', PS_COLUMNS)
@@ -556,22 +567,14 @@ class DeviceUtils(object):
CommandTimeoutError on timeout.
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
- # Devices using the system-as-root partition layout appear to not have
- # a /root directory. See http://bit.ly/37F34sx for more context.
- if (self.build_system_root_image == 'true'
- or self.build_version_sdk >= version_codes.Q
- # This may be redundant with the checks above.
- or self.product_name in _SPECIAL_ROOT_DEVICE_LIST):
- return self.GetProp('service.adb.root') == '1'
- self.RunShellCommand(['ls', '/root'], check_return=True)
+ if self.build_type == 'eng':
+ # 'eng' builds have root enabled by default and the adb session cannot
+ # be unrooted.
return True
- except device_errors.AdbCommandFailedError:
- return False
+ # Check if uid is 0. Such behavior has remained unchanged since
+ # android 2.2.3 (https://bit.ly/2QQzg67)
+ output = self.RunShellCommand(['id'], single_line=True)
+ return output.startswith('uid=0(root)')
def NeedsSU(self, timeout=DEFAULT, retries=DEFAULT):
"""Checks whether 'su' is needed to access protected resources.
@@ -638,8 +641,7 @@ class DeviceUtils(object):
self.adb.Root()
except device_errors.AdbCommandFailedError as e:
if self.IsUserBuild():
- raise device_errors.CommandFailedError(
- 'Unable to root device with user build.', str(self))
+ raise device_errors.RootUserBuildError(device_serial=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.
@@ -768,21 +770,50 @@ class DeviceUtils(object):
raise device_errors.CommandFailedError('Unable to fetch IMEI.')
@decorators.WithTimeoutAndRetriesFromInstance()
- def IsApplicationInstalled(self, package, timeout=None, retries=None):
+ def IsApplicationInstalled(
+ self, package, version_code=None, timeout=None, retries=None):
"""Determines whether a particular package is installed on the device.
Args:
package: Name of the package.
+ version_code: The version of the package to check for as an int, if
+ applicable. Only used for static shared libraries, otherwise ignored.
Returns:
True if the application is installed, False otherwise.
"""
- # `pm list packages` allows matching substrings, but we want exact matches
- # only.
- matching_packages = self.RunShellCommand(
- ['pm', 'list', 'packages', package], check_return=True)
- desired_line = 'package:' + package
- return desired_line in matching_packages
+ # `pm list packages` doesn't include the version code, so if it was
+ # provided, skip this since we can't guarantee that the installed
+ # version is the requested version.
+ if version_code is None:
+ # `pm list packages` allows matching substrings, but we want exact matches
+ # only.
+ matching_packages = self.RunShellCommand(
+ ['pm', 'list', 'packages', package], check_return=True)
+ desired_line = 'package:' + package
+ found_package = desired_line in matching_packages
+ if found_package:
+ return True
+
+ # Some packages do not properly show up via `pm list packages`, so fall back
+ # to checking via `dumpsys package`.
+ matcher = re.compile(_DUMPSYS_PACKAGE_RE_STR % package)
+ dumpsys_output = self.RunShellCommand(
+ ['dumpsys', 'package'], check_return=True, large_output=True)
+ for line in dumpsys_output:
+ match = matcher.match(line)
+ # We should have one of these cases:
+ # 1. The package is a regular app, in which case it will show up without
+ # its version code in the line we're filtering for.
+ # 2. The package is a static shared library, in which case one or more
+ # entries with the version code can show up, but not one without the
+ # version code.
+ if match:
+ installed_version_code = match.groupdict().get('version_code')
+ if (installed_version_code is None
+ or installed_version_code == str(version_code)):
+ return True
+ return False
@decorators.WithTimeoutAndRetriesFromInstance()
def GetApplicationPaths(self, package, timeout=None, retries=None):
@@ -1062,6 +1093,10 @@ class DeviceUtils(object):
retries=None):
"""Reboot the device.
+ Note if the device has the root privilege, it will likely lose it after the
+ reboot. When |block| is True, it will try to restore the root status if
+ applicable.
+
Args:
block: A boolean indicating if we should wait for the reboot to complete.
wifi: A boolean indicating if we should wait for wifi to be enabled after
@@ -1081,11 +1116,15 @@ class DeviceUtils(object):
def device_offline():
return not self.IsOnline()
+ # Only check the root when block is True
+ should_restore_root = self.HasRoot() if block else False
self.adb.Reboot()
self.ClearCache()
timeout_retry.WaitFor(device_offline, wait_period=1)
if block:
self.WaitUntilFullyBooted(wifi=wifi, decrypt=decrypt)
+ if should_restore_root:
+ self.EnableRoot()
INSTALL_DEFAULT_TIMEOUT = 8 * _DEFAULT_TIMEOUT
MODULES_SRC_DIRECTORY_PATH = '/data/local/tmp/modules'
@@ -1282,6 +1321,7 @@ class DeviceUtils(object):
apks_to_install = apk_paths
if device_apk_paths and apks_to_install and not reinstall:
+ logger.info('Uninstalling package %s', package_name)
self.Uninstall(package_name)
if apks_to_install:
@@ -1292,6 +1332,8 @@ class DeviceUtils(object):
streaming = None
if self.product_name in _NO_STREAMING_DEVICE_LIST:
streaming = False
+ logger.info('Installing package %s using APKs %s',
+ package_name, apks_to_install)
if len(apks_to_install) > 1 or partial:
self.adb.InstallMultiple(
apks_to_install,
@@ -1306,10 +1348,20 @@ class DeviceUtils(object):
streaming=streaming,
allow_downgrade=allow_downgrade)
else:
+ logger.info('Skipping installation of package %s', package_name)
# Running adb install terminates running instances of the app, so to be
# consistent, we explicitly terminate it when skipping the install.
self.ForceStop(package_name)
+ # There have been cases of APKs not being detected after being explicitly
+ # installed, so perform a sanity check now and fail early if the
+ # installation somehow failed.
+ apk_version = apk.GetVersionCode()
+ if not self.IsApplicationInstalled(package_name, apk_version):
+ raise device_errors.CommandFailedError(
+ 'Package %s with version %s not installed on device after explicit '
+ 'install attempt.' % (package_name, apk_version))
+
if (permissions is None
and self.build_version_sdk >= version_codes.MARSHMALLOW):
permissions = apk.GetPermissions()
@@ -2045,9 +2097,14 @@ class DeviceUtils(object):
def _ComputeDeviceChecksumsForApks(self, package_name):
ret = self._cache['package_apk_checksums'].get(package_name)
if ret is None:
- device_paths = self._GetApplicationPathsInternal(package_name)
- file_to_checksums = md5sum.CalculateDeviceMd5Sums(device_paths, self)
- ret = set(file_to_checksums.values())
+ if self.PathExists('/data/data/' + package_name, as_root=True):
+ device_paths = self._GetApplicationPathsInternal(package_name)
+ file_to_checksums = md5sum.CalculateDeviceMd5Sums(device_paths, self)
+ ret = set(file_to_checksums.values())
+ else:
+ logger.info('Cannot reuse package %s (data directory missing)',
+ package_name)
+ ret = set()
self._cache['package_apk_checksums'][package_name] = ret
return ret
@@ -2720,11 +2777,16 @@ class DeviceUtils(object):
@property
def pixel_density(self):
density = self.GetProp('ro.sf.lcd_density', cache=True)
- if not density and self.adb.is_emulator:
+ if not density:
+ # It might be an emulator, try the qemu prop.
density = self.GetProp('qemu.sf.lcd_density', cache=True)
return int(density)
@property
+ def is_emulator(self):
+ return _EMULATOR_RE.match(self.GetProp('ro.product.device', cache=True))
+
+ @property
def build_description(self):
"""Returns the build description of the system.
@@ -3186,11 +3248,12 @@ class DeviceUtils(object):
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.
+ The returned dictionary may not include all of the above keys: this depends
+ on the support of the platform's underlying WebViewUpdateService. This may
+ return an empty dictionary on OS versions which do not support querying the
+ WebViewUpdateService.
Raises:
- CommandFailedError on failure.
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
"""
@@ -3229,12 +3292,6 @@ class DeviceUtils(object):
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()
@@ -3552,9 +3609,6 @@ class DeviceUtils(object):
retries=1,
enable_usb_resets=False,
abis=None,
- # TODO(crbug.com/1097306): Remove this once clients have
- # stopped passing it.
- blacklist=None,
**kwargs):
"""Returns a list of DeviceUtils instances.
@@ -3607,10 +3661,6 @@ class DeviceUtils(object):
if device_arg:
device_arg = (device_arg, )
- # TODO(crbug.com/1097306): Remove this once clients have switched.
- if blacklist and not denylist:
- denylist = blacklist
-
denylisted_devices = denylist.Read() if denylist else []
# adb looks for ANDROID_SERIAL, so support it as well.
diff --git a/catapult/devil/devil/android/device_utils_test.py b/catapult/devil/devil/android/device_utils_test.py
index 38e64a97..62313c5b 100755
--- a/catapult/devil/devil/android/device_utils_test.py
+++ b/catapult/devil/devil/android/device_utils_test.py
@@ -31,8 +31,7 @@ from devil.android.sdk import version_codes
from devil.utils import cmd_helper
from devil.utils import mock_calls
-with devil_env.SysPath(
- os.path.join(devil_env.CATAPULT_ROOT_PATH, 'common', 'py_utils')):
+with devil_env.SysPath(os.path.join(devil_env.PY_UTILS_PATH)):
from py_utils import tempfile_ext
with devil_env.SysPath(devil_env.PYMOCK_PATH):
@@ -80,6 +79,7 @@ class _MockApkHelper(object):
self.perms = perms
self.splits = splits if splits else []
self.abis = [abis.ARM]
+ self.version_code = None
def GetPackageName(self):
return self.package_name
@@ -87,6 +87,9 @@ class _MockApkHelper(object):
def GetPermissions(self):
return self.perms
+ def GetVersionCode(self):
+ return self.version_code
+
def GetAbis(self):
return self.abis
@@ -332,119 +335,21 @@ 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.build_system_root_image,
- return_value='')), (self.patch_call(
- self.call.device.build_version_sdk,
- return_value=version_codes.PIE)), (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.build_system_root_image,
- return_value='')), (self.patch_call(
- self.call.device.build_version_sdk,
- return_value=version_codes.PIE)), (self.patch_call(
- self.call.device.product_name,
- return_value='sailfish')), (self.assertCall(
- self.call.adb.Shell('getprop service.adb.root'),
- '1\n')):
+ with self.patch_call(self.call.device.build_type,
+ return_value='userdebug'), (self.assertCall(
+ self.call.adb.Shell('id'), 'uid=0(root)\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.build_system_root_image,
- return_value='')), (self.patch_call(
- self.call.device.build_version_sdk,
- return_value=version_codes.PIE)), (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):
+ 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.build_system_root_image,
- return_value='')), (self.patch_call(
- self.call.device.build_version_sdk,
- return_value=version_codes.PIE)), (self.patch_call(
- self.call.device.product_name,
- return_value='notasailfish')), (self.assertCall(
- self.call.adb.Shell('ls /root'), self.ShellError())):
+ with self.patch_call(self.call.device.build_type,
+ return_value='userdebug'), (self.assertCall(
+ self.call.adb.Shell('id'), 'uid=2000(shell)\n')):
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.build_system_root_image,
- return_value='')), (self.patch_call(
- self.call.device.build_version_sdk,
- return_value=version_codes.PIE)), (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.build_system_root_image,
- return_value='')), (self.patch_call(
- self.call.device.build_version_sdk,
- return_value=version_codes.PIE)), (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())
-
- def testHasRootSystemRootImage(self):
- with self.patch_call(
- self.call.device.build_type,
- return_value='userdebug'), (self.patch_call(
- self.call.device.build_system_root_image,
- return_value='true')), (self.patch_call(
- self.call.device.build_version_sdk,
- return_value=version_codes.PIE)), (self.patch_call(
- self.call.device.product_name,
- return_value='notasailfish')), (self.assertCall(
- self.call.adb.Shell('getprop service.adb.root'),
- '1\n')):
- self.assertTrue(self.device.HasRoot())
-
- def testHasRoot10(self):
- # All devices on Android 10 / Q and above should use the system-as-root
- # partition layout, though they may not have the property set.
- with self.patch_call(
- self.call.device.build_type,
- return_value='userdebug'), (self.patch_call(
- self.call.device.build_system_root_image,
- return_value='')), (self.patch_call(
- self.call.device.build_version_sdk,
- return_value=version_codes.Q)), (self.patch_call(
- self.call.device.product_name,
- return_value='notasailfish')), (self.assertCall(
- self.call.adb.Shell('getprop service.adb.root'),
- '1\n')):
- self.assertTrue(self.device.HasRoot())
-
class DeviceUtilsEnableRootTest(DeviceUtilsTest):
def testEnableRoot_succeeds(self):
@@ -530,21 +435,54 @@ class DeviceUtilsIsApplicationInstalledTest(DeviceUtilsTest):
self.assertTrue(self.device.IsApplicationInstalled('some.installed.app'))
def testIsApplicationInstalled_notInstalled(self):
- with self.assertCalls((self.call.device.RunShellCommand(
- ['pm', 'list', 'packages', 'not.installed.app'], check_return=True),
- '')):
+ with self.assertCalls(
+ (self.call.device.RunShellCommand(
+ ['pm', 'list', 'packages', 'not.installed.app'], check_return=True),
+ ''),
+ (self.call.device.RunShellCommand(
+ ['dumpsys', 'package'], check_return=True, large_output=True), [])):
self.assertFalse(self.device.IsApplicationInstalled('not.installed.app'))
def testIsApplicationInstalled_substringMatch(self):
- with self.assertCalls((self.call.device.RunShellCommand(
- ['pm', 'list', 'packages', 'substring.of.package'], check_return=True),
- [
- 'package:first.substring.of.package',
- 'package:second.substring.of.package',
- ])):
+ with self.assertCalls(
+ (self.call.device.RunShellCommand(
+ ['pm', 'list', 'packages', 'substring.of.package'],
+ check_return=True),
+ [
+ 'package:first.substring.of.package',
+ 'package:second.substring.of.package',
+ ]),
+ (self.call.device.RunShellCommand(
+ ['dumpsys', 'package'], check_return=True, large_output=True), [])):
self.assertFalse(
self.device.IsApplicationInstalled('substring.of.package'))
+ def testIsApplicationInstalled_dumpsysFallback(self):
+ with self.assertCalls(
+ (self.call.device.RunShellCommand(
+ ['pm', 'list', 'packages', 'some.installed.app'],
+ check_return=True), []),
+ (self.call.device.RunShellCommand(
+ ['dumpsys', 'package'], check_return=True, large_output=True),
+ ['Package [some.installed.app] (a12345):'])):
+ self.assertTrue(self.device.IsApplicationInstalled('some.installed.app'))
+
+ def testIsApplicationInstalled_dumpsysFallbackVersioned(self):
+ with self.assertCalls(
+ (self.call.device.RunShellCommand(
+ ['dumpsys', 'package'], check_return=True, large_output=True),
+ ['Package [some.installed.app_1234] (a12345):'])):
+ self.assertTrue(
+ self.device.IsApplicationInstalled('some.installed.app', 1234))
+
+ def testIsApplicationInstalled_dumpsysFallbackVersionNotNeeded(self):
+ with self.assertCalls(
+ (self.call.device.RunShellCommand(
+ ['dumpsys', 'package'], check_return=True, large_output=True),
+ ['Package [some.installed.app] (a12345):'])):
+ self.assertTrue(
+ self.device.IsApplicationInstalled('some.installed.app', 1234))
+
class DeviceUtilsGetApplicationPathsInternalTest(DeviceUtilsTest):
def testGetApplicationPathsInternal_exists(self):
@@ -913,13 +851,24 @@ class DeviceUtilsRebootTest(DeviceUtilsTest):
def testReboot_blocking(self):
with self.assertCalls(
+ (self.call.device.HasRoot(), False),
self.call.adb.Reboot(), (self.call.device.IsOnline(), True),
(self.call.device.IsOnline(), False),
self.call.device.WaitUntilFullyBooted(wifi=False, decrypt=False)):
self.device.Reboot(block=True)
+ def testReboot_blockingWithRoot(self):
+ with self.assertCalls(
+ (self.call.device.HasRoot(), True),
+ self.call.adb.Reboot(), (self.call.device.IsOnline(), True),
+ (self.call.device.IsOnline(), False),
+ self.call.device.WaitUntilFullyBooted(wifi=False, decrypt=False),
+ self.call.device.EnableRoot()):
+ self.device.Reboot(block=True)
+
def testReboot_blockUntilWifi(self):
with self.assertCalls(
+ (self.call.device.HasRoot(), False),
self.call.adb.Reboot(), (self.call.device.IsOnline(), True),
(self.call.device.IsOnline(), False),
self.call.device.WaitUntilFullyBooted(wifi=True, decrypt=False)):
@@ -927,6 +876,7 @@ class DeviceUtilsRebootTest(DeviceUtilsTest):
def testReboot_blockUntilDecrypt(self):
with self.assertCalls(
+ (self.call.device.HasRoot(), False),
self.call.adb.Reboot(), (self.call.device.IsOnline(), True),
(self.call.device.IsOnline(), False),
self.call.device.WaitUntilFullyBooted(wifi=False, decrypt=True)):
@@ -950,6 +900,7 @@ class DeviceUtilsInstallTest(DeviceUtilsTest):
reinstall=False,
streaming=None,
allow_downgrade=False),
+ (self.call.device.IsApplicationInstalled(TEST_PACKAGE, None), True),
(self.call.device.GrantPermissions(TEST_PACKAGE, ['p1']), [])):
self.device.Install(DeviceUtilsInstallTest.mock_apk, retries=0)
@@ -966,6 +917,7 @@ class DeviceUtilsInstallTest(DeviceUtilsTest):
reinstall=False,
streaming=False,
allow_downgrade=False),
+ (self.call.device.IsApplicationInstalled(TEST_PACKAGE, None), True),
(self.call.device.GrantPermissions(TEST_PACKAGE, ['p1']), [])):
self.device.Install(DeviceUtilsInstallTest.mock_apk, retries=0)
@@ -981,7 +933,8 @@ class DeviceUtilsInstallTest(DeviceUtilsTest):
(self.call.adb.Install(TEST_APK_PATH,
reinstall=False,
streaming=None,
- allow_downgrade=False))):
+ allow_downgrade=False)),
+ (self.call.device.IsApplicationInstalled(TEST_PACKAGE, None), True)):
self.device.Install(DeviceUtilsInstallTest.mock_apk, retries=0)
def testInstall_findPermissions(self):
@@ -997,6 +950,7 @@ class DeviceUtilsInstallTest(DeviceUtilsTest):
reinstall=False,
streaming=None,
allow_downgrade=False)),
+ (self.call.device.IsApplicationInstalled(TEST_PACKAGE, None), True),
(self.call.device.GrantPermissions(TEST_PACKAGE, ['p1']), [])):
self.device.Install(DeviceUtilsInstallTest.mock_apk, retries=0)
@@ -1011,6 +965,7 @@ class DeviceUtilsInstallTest(DeviceUtilsTest):
reinstall=False,
streaming=None,
allow_downgrade=False)),
+ (self.call.device.IsApplicationInstalled(TEST_PACKAGE, None), True),
(self.call.device.GrantPermissions(TEST_PACKAGE, ['p1', 'p2']), [])):
self.device.Install(
DeviceUtilsInstallTest.mock_apk,
@@ -1024,7 +979,8 @@ class DeviceUtilsInstallTest(DeviceUtilsTest):
(self.call.device._GetApplicationPathsInternal(TEST_PACKAGE),
['/fake/data/app/test.package.apk']),
(self.call.device._ComputeStaleApks(TEST_PACKAGE, [TEST_APK_PATH]),
- ([], None)), (self.call.device.ForceStop(TEST_PACKAGE))):
+ ([], None)), (self.call.device.ForceStop(TEST_PACKAGE)),
+ (self.call.device.IsApplicationInstalled(TEST_PACKAGE, None), True)):
self.device.Install(
DeviceUtilsInstallTest.mock_apk, retries=0, permissions=[])
@@ -1041,7 +997,8 @@ class DeviceUtilsInstallTest(DeviceUtilsTest):
self.call.adb.Install(TEST_APK_PATH,
reinstall=False,
streaming=None,
- allow_downgrade=False)):
+ allow_downgrade=False),
+ (self.call.device.IsApplicationInstalled(TEST_PACKAGE, None), True)):
self.device.Install(
DeviceUtilsInstallTest.mock_apk, retries=0, permissions=[])
@@ -1058,7 +1015,8 @@ class DeviceUtilsInstallTest(DeviceUtilsTest):
self.call.adb.Install(TEST_APK_PATH,
reinstall=False,
streaming=None,
- allow_downgrade=False)):
+ allow_downgrade=False),
+ (self.call.device.IsApplicationInstalled(TEST_PACKAGE, None), True)):
self.device.Install(
DeviceUtilsInstallTest.mock_apk, retries=0, permissions=[])
@@ -1075,7 +1033,8 @@ class DeviceUtilsInstallTest(DeviceUtilsTest):
self.call.adb.Install(TEST_APK_PATH,
reinstall=True,
streaming=None,
- allow_downgrade=False)):
+ allow_downgrade=False),
+ (self.call.device.IsApplicationInstalled(TEST_PACKAGE, None), True)):
self.device.Install(
DeviceUtilsInstallTest.mock_apk,
reinstall=True,
@@ -1089,7 +1048,8 @@ class DeviceUtilsInstallTest(DeviceUtilsTest):
(self.call.device._GetApplicationPathsInternal(TEST_PACKAGE),
['/fake/data/app/test.package.apk']),
(self.call.device._ComputeStaleApks(TEST_PACKAGE, [TEST_APK_PATH]),
- ([], None)), (self.call.device.ForceStop(TEST_PACKAGE))):
+ ([], None)), (self.call.device.ForceStop(TEST_PACKAGE)),
+ (self.call.device.IsApplicationInstalled(TEST_PACKAGE, None), True)):
self.device.Install(
DeviceUtilsInstallTest.mock_apk,
reinstall=True,
@@ -1131,7 +1091,8 @@ class DeviceUtilsInstallTest(DeviceUtilsTest):
self.call.adb.Install(TEST_APK_PATH,
reinstall=True,
streaming=None,
- allow_downgrade=True)):
+ allow_downgrade=True),
+ (self.call.device.IsApplicationInstalled(TEST_PACKAGE, None), True)):
self.device.Install(
DeviceUtilsInstallTest.mock_apk,
reinstall=True,
@@ -1164,10 +1125,32 @@ class DeviceUtilsInstallTest(DeviceUtilsTest):
reinstall=False,
streaming=None,
allow_downgrade=False),
+ (self.call.device.IsApplicationInstalled(TEST_PACKAGE, None), True),
(self.call.device.GrantPermissions(TEST_PACKAGE, None), [])):
self.device.Install(
mock_apk_with_fake, fake_modules=fake_modules, retries=0)
+ def testInstall_packageNotAvailableAfterInstall(self):
+ with self.patch_call(
+ self.call.device.product_name,
+ return_value='notflounder'), (self.patch_call(
+ self.call.device.build_version_sdk, return_value=23)), (
+ self.patch_call(self.call.device.IsApplicationInstalled,
+ return_value=False)):
+ with self.assertCalls(
+ (self.call.device._FakeInstall(set(), None, 'test.package')),
+ (mock.call.os.path.exists(TEST_APK_PATH), True),
+ (self.call.device._GetApplicationPathsInternal(TEST_PACKAGE), []),
+ self.call.adb.Install(TEST_APK_PATH,
+ reinstall=False,
+ streaming=None,
+ allow_downgrade=False)):
+ with self.assertRaisesRegexp(
+ device_errors.CommandFailedError,
+ 'not installed on device after explicit install attempt'):
+ self.device.Install(
+ DeviceUtilsInstallTest.mock_apk, retries=0)
+
class DeviceUtilsInstallSplitApkTest(DeviceUtilsTest):
@@ -1191,7 +1174,8 @@ class DeviceUtilsInstallSplitApkTest(DeviceUtilsTest):
partial=None,
reinstall=False,
streaming=None,
- allow_downgrade=False))):
+ allow_downgrade=False)),
+ (self.call.device.IsApplicationInstalled(TEST_PACKAGE, None), True)):
self.device.InstallSplitApk(
'base.apk', ['split1.apk', 'split2.apk'], permissions=[], retries=0)
@@ -1212,7 +1196,8 @@ class DeviceUtilsInstallSplitApkTest(DeviceUtilsTest):
partial=None,
reinstall=False,
streaming=False,
- allow_downgrade=False))):
+ allow_downgrade=False)),
+ (self.call.device.IsApplicationInstalled(TEST_PACKAGE, None), True)):
self.device.InstallSplitApk(
'base.apk', ['split1.apk', 'split2.apk'], permissions=[], retries=0)
@@ -1237,7 +1222,8 @@ class DeviceUtilsInstallSplitApkTest(DeviceUtilsTest):
partial=TEST_PACKAGE,
reinstall=True,
streaming=None,
- allow_downgrade=False))):
+ allow_downgrade=False)),
+ (self.call.device.IsApplicationInstalled(TEST_PACKAGE, None), True)):
self.device.InstallSplitApk(
DeviceUtilsInstallSplitApkTest.mock_apk,
['split1.apk', 'split2.apk'],
@@ -1266,7 +1252,8 @@ class DeviceUtilsInstallSplitApkTest(DeviceUtilsTest):
partial=TEST_PACKAGE,
reinstall=True,
streaming=None,
- allow_downgrade=True))):
+ allow_downgrade=True)),
+ (self.call.device.IsApplicationInstalled(TEST_PACKAGE, None), True)):
self.device.InstallSplitApk(
DeviceUtilsInstallSplitApkTest.mock_apk,
['split1.apk', 'split2.apk'],
@@ -1311,7 +1298,8 @@ class DeviceUtilsInstallSplitApkTest(DeviceUtilsTest):
partial=None,
reinstall=False,
streaming=None,
- allow_downgrade=False))):
+ allow_downgrade=False)),
+ (self.call.device.IsApplicationInstalled(TEST_PACKAGE, None), True)):
self.device.InstallSplitApk(
DeviceUtilsInstallSplitApkTest.mock_apk,
['split1.apk', 'split2.apk'],
@@ -3132,8 +3120,8 @@ class DeviceUtilsGetWebViewUpdateServiceDumpTest(DeviceUtilsTest):
with self.assertCall(
self.call.adb.Shell('dumpsys webviewupdate'),
'Fallback logic enabled: true'):
- with self.assertRaises(device_errors.CommandFailedError):
- self.device.GetWebViewUpdateServiceDump()
+ update = self.device.GetWebViewUpdateServiceDump()
+ self.assertEqual(True, update['FallbackLogicEnabled'])
def testGetWebViewUpdateServiceDump_noop(self):
with self.patch_call(
diff --git a/catapult/devil/devil/android/forwarder.py b/catapult/devil/devil/android/forwarder.py
index bf4c5ca1..9b1f82e4 100644
--- a/catapult/devil/devil/android/forwarder.py
+++ b/catapult/devil/devil/android/forwarder.py
@@ -435,14 +435,10 @@ class Forwarder(object):
(exit_code, output) = cmd_helper.GetCmdStatusAndOutputWithTimeout(
kill_cmd, Forwarder._TIMEOUT)
if exit_code == -9:
- # pkill can exit with -9, seemingly in cases where the process it's
- # asked to kill dies sometime during pkill running. In this case,
- # re-running should result in pkill succeeding.
- logging.warning(
- 'pkilling host forwarder returned -9, retrying through strace. '
- 'Output: %s', output)
- exit_code, output = cmd_helper.GetCmdStatusAndOutputWithTimeout(
- ['strace', '-f', '-s', '256'] + kill_cmd, Forwarder._TIMEOUT)
+ # pkill can exit with -9, which indicates that it was killed. It's
+ # possible that the forwarder was still killed, though, which will
+ # be checked later.
+ logging.warning('pkilling host forwarder returned -9.')
if exit_code in (0, 1):
# pkill exits with a 0 if it was able to signal at least one process.
# pkill exits with a 1 if it wasn't able to signal a process because
@@ -458,6 +454,7 @@ class Forwarder(object):
'\n '.join(host_forwarder_lines))
else:
logger.error('No remaining host_forwarder processes?')
+ return
_DumpHostLog()
error_msg = textwrap.dedent("""\
`{kill_cmd}` failed to kill host_forwarder.
diff --git a/catapult/devil/devil/android/sdk/adb_wrapper.py b/catapult/devil/devil/android/sdk/adb_wrapper.py
index 8cd73136..71928d58 100644
--- a/catapult/devil/devil/android/sdk/adb_wrapper.py
+++ b/catapult/devil/devil/android/sdk/adb_wrapper.py
@@ -35,11 +35,13 @@ ADB_KEYS_FILE = '/data/misc/adb/adb_keys'
ADB_HOST_KEYS_DIR = os.path.join(os.path.expanduser('~'), '.android')
DEFAULT_TIMEOUT = 30
+DEFAULT_LONG_TIMEOUT = DEFAULT_TIMEOUT * 10
+DEFAULT_SUPER_LONG_TIMEOUT = DEFAULT_LONG_TIMEOUT * 2
DEFAULT_RETRIES = 2
_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")
+_DEVICE_NOT_FOUND_RE = re.compile(r"device '(?P<serial>.+)' not found")
_READY_STATE = 'device'
_VERITY_DISABLE_RE = re.compile(r'(V|v)erity (is )?(already )?disabled'
r'|Successfully disabled verity')
@@ -303,7 +305,7 @@ class AdbWrapper(object):
# inconsistent with error reporting so many command failures present
# differently.
if status != 0 or (check_error and output.startswith('error:')):
- not_found_m = _DEVICE_NOT_FOUND_RE.match(output)
+ not_found_m = _DEVICE_NOT_FOUND_RE.search(output)
device_waiting_m = _WAITING_FOR_DEVICE_RE.match(output)
if (device_waiting_m is not None
or (not_found_m is not None
@@ -476,7 +478,7 @@ class AdbWrapper(object):
local,
remote,
sync=False,
- timeout=60 * 5,
+ timeout=DEFAULT_SUPER_LONG_TIMEOUT,
retries=DEFAULT_RETRIES):
"""Pushes a file from the host to the device.
@@ -549,7 +551,11 @@ class AdbWrapper(object):
self._RunDeviceAdbCmd(push_cmd, timeout, retries)
- def Pull(self, remote, local, timeout=60 * 5, retries=DEFAULT_RETRIES):
+ def Pull(self,
+ remote,
+ local,
+ timeout=DEFAULT_LONG_TIMEOUT,
+ retries=DEFAULT_RETRIES):
"""Pulls a file from the device to the host.
Args:
@@ -832,7 +838,7 @@ class AdbWrapper(object):
reinstall=False,
sd_card=False,
streaming=None,
- timeout=60 * 2,
+ timeout=DEFAULT_LONG_TIMEOUT,
retries=DEFAULT_RETRIES):
"""Install an apk on the device.
@@ -883,7 +889,7 @@ class AdbWrapper(object):
allow_downgrade=False,
partial=False,
streaming=None,
- timeout=60 * 2,
+ timeout=DEFAULT_LONG_TIMEOUT,
retries=DEFAULT_RETRIES):
"""Install an apk with splits on the device.
@@ -1002,7 +1008,8 @@ class AdbWrapper(object):
VerifyLocalFileExists(path)
self._RunDeviceAdbCmd(['restore'] + [path], timeout, retries)
- def WaitForDevice(self, timeout=60 * 5, retries=DEFAULT_RETRIES):
+ def WaitForDevice(self, timeout=DEFAULT_LONG_TIMEOUT,
+ retries=DEFAULT_RETRIES):
"""Block until the device is online.
Args:
@@ -1047,7 +1054,9 @@ class AdbWrapper(object):
"""Remounts the /system partition on the device read-write."""
self._RunDeviceAdbCmd(['remount'], timeout, retries)
- def Reboot(self, to_bootloader=False, timeout=60 * 5,
+ def Reboot(self,
+ to_bootloader=False,
+ timeout=DEFAULT_LONG_TIMEOUT,
retries=DEFAULT_RETRIES):
"""Reboots the device.
@@ -1069,7 +1078,16 @@ class AdbWrapper(object):
timeout: (optional) Timeout per try in seconds.
retries: (optional) Number of retries to attempt.
"""
- output = self._RunDeviceAdbCmd(['root'], timeout, retries)
+ try:
+ output = self._RunDeviceAdbCmd(['root'], timeout, retries)
+ except device_errors.AdbCommandFailedError as e:
+ # For some devices, root can occasionally fail with this error and kick
+ # the device into adb 'offline' mode. Assuming this is transient, try
+ # waiting for the device to come back up before re-raising the exception
+ # and proceeding with any retries.
+ if 'unable to connect for root: closed' in e.output:
+ self.WaitForDevice()
+ raise
if 'cannot' in output:
raise device_errors.AdbCommandFailedError(
['root'], output, device_serial=self._device_serial)
@@ -1115,6 +1133,7 @@ class AdbWrapper(object):
['enable-verity'], output, device_serial=self._device_serial)
return output
+ # Deprecated use device_utils#is_emulator instead.
@property
def is_emulator(self):
return _EMULATOR_RE.match(self._device_serial)
diff --git a/catapult/devil/devil/android/sdk/adb_wrapper_test.py b/catapult/devil/devil/android/sdk/adb_wrapper_test.py
index f30ab152..1b3d5841 100755
--- a/catapult/devil/devil/android/sdk/adb_wrapper_test.py
+++ b/catapult/devil/devil/android/sdk/adb_wrapper_test.py
@@ -68,3 +68,12 @@ class AdbWrapperTest(unittest.TestCase):
get_cmd_mock.return_value = (1, '- waiting for device - ')
self.assertRaises(device_errors.DeviceUnreachableError, self.adb.Shell,
'/bin/true')
+
+ @mock.patch('devil.utils.cmd_helper.GetCmdStatusAndOutputWithTimeout')
+ def testRootConnectionClosedFailure(self, get_cmd_mock):
+ get_cmd_mock.side_effect = [
+ (1, 'unable to connect for root: closed'),
+ (0, ''),
+ ]
+ self.assertRaises(device_errors.AdbCommandFailedError, self.adb.Root,
+ retries=0)
diff --git a/catapult/devil/devil/android/sdk/bundletool.py b/catapult/devil/devil/android/sdk/bundletool.py
index 5c181c56..f210db14 100644
--- a/catapult/devil/devil/android/sdk/bundletool.py
+++ b/catapult/devil/devil/android/sdk/bundletool.py
@@ -9,7 +9,9 @@ from devil import base_error
from devil import devil_env
from devil.utils import cmd_helper
from devil.utils import lazy
-from py_utils import tempfile_ext
+
+with devil_env.SysPath(devil_env.PY_UTILS_PATH):
+ from py_utils import tempfile_ext
_bundletool_path = lazy.WeakConstant(lambda: devil_env.config.FetchPath(
'bundletool'))
diff --git a/catapult/devil/devil/android/tools/device_monitor.py b/catapult/devil/devil/android/tools/device_monitor.py
index 6128dae8..26e89a24 100755
--- a/catapult/devil/devil/android/tools/device_monitor.py
+++ b/catapult/devil/devil/android/tools/device_monitor.py
@@ -203,11 +203,6 @@ def main(argv):
parser = argparse.ArgumentParser(description='Launches the device monitor.')
script_common.AddEnvironmentArguments(parser)
parser.add_argument('--denylist-file', help='Path to device denylist file.')
- # TODO(crbug.com/1097306): Remove this once chromium_android/api.py stops
- # using it.
- parser.add_argument('--blacklist-file',
- dest='denylist_file',
- help=argparse.SUPPRESS)
args = parser.parse_args(argv)
logger = logging.getLogger()
diff --git a/catapult/devil/devil/android/tools/device_recovery.py b/catapult/devil/devil/android/tools/device_recovery.py
index f25c5a25..10c5fe51 100755
--- a/catapult/devil/devil/android/tools/device_recovery.py
+++ b/catapult/devil/devil/android/tools/device_recovery.py
@@ -114,6 +114,7 @@ def RecoverDevice(device, denylist, should_reboot=lambda device: True):
return
if should_reboot(device):
+ should_restore_root = device.HasRoot()
try:
device.WaitUntilFullyBooted(retries=0)
except (device_errors.CommandTimeoutError, device_errors.CommandFailedError,
@@ -154,6 +155,8 @@ def RecoverDevice(device, denylist, should_reboot=lambda device: True):
try:
device.WaitUntilFullyBooted(
retries=0, timeout=device.REBOOT_DEFAULT_TIMEOUT)
+ if should_restore_root:
+ device.EnableRoot()
except (device_errors.CommandFailedError,
device_errors.DeviceUnreachableError):
logger.exception('Failure while waiting for %s.', str(device))
@@ -230,8 +233,6 @@ def main():
parser = argparse.ArgumentParser()
logging_common.AddLoggingArguments(parser)
script_common.AddEnvironmentArguments(parser)
- # TODO(crbug.com/1097306): Remove this once callers switch to --denylist-file.
- parser.add_argument('--blacklist-file', help=argparse.SUPPRESS)
parser.add_argument('--denylist-file', help='Device denylist JSON file.')
parser.add_argument(
'--known-devices-file',
@@ -248,9 +249,6 @@ def main():
denylist = (device_denylist.Denylist(args.denylist_file)
if args.denylist_file else None)
- # TODO(crbug.com/1097306): Remove this once callers switch to --denylist-file.
- if not denylist and args.blacklist_file:
- denylist = device_denylist.Denylist(args.blacklist_file)
expected_devices = device_status.GetExpectedDevices(args.known_devices_files)
usb_devices = set(lsusb.get_android_devices())
diff --git a/catapult/devil/devil/android/tools/device_status.py b/catapult/devil/devil/android/tools/device_status.py
index d0eb7df1..40f714aa 100755
--- a/catapult/devil/devil/android/tools/device_status.py
+++ b/catapult/devil/devil/android/tools/device_status.py
@@ -226,11 +226,6 @@ def AddArguments(parser):
parser.add_argument(
'--json-output', help='Output JSON information into a specified file.')
parser.add_argument('--denylist-file', help='Device denylist JSON file.')
- # TODO(crbug.com/1097306): Remove this once //testing/scripts/host_info.py
- # stops using it.
- parser.add_argument('--blacklist-file',
- dest='denylist_file',
- help=argparse.SUPPRESS)
parser.add_argument(
'--known-devices-file',
action='append',
diff --git a/catapult/devil/devil/android/tools/provision_devices.py b/catapult/devil/devil/android/tools/provision_devices.py
index 4b0f44a2..9a324c48 100755
--- a/catapult/devil/devil/android/tools/provision_devices.py
+++ b/catapult/devil/devil/android/tools/provision_devices.py
@@ -625,9 +625,6 @@ def main(raw_args):
metavar='NUM',
help='wait for the device to reach this minimum battery'
' level before trying to continue')
- # TODO(crbug.com/1097306): Remove after updating clients.
- parser.add_argument('--output-device-blacklist',
- help=argparse.SUPPRESS)
parser.add_argument('--output-device-denylist',
help='Json file to output the device denylist.')
parser.add_argument(
@@ -674,10 +671,6 @@ def main(raw_args):
logging_common.InitializeLogging(args)
script_common.InitializeEnvironment(args)
- output_device_denylist = args.output_device_denylist
- if not output_device_denylist and args.output_device_blacklist:
- output_device_denylist = args.output_device_blacklist
-
try:
return ProvisionDevices(
args.devices,
@@ -691,7 +684,7 @@ def main(raw_args):
enable_java_debug=args.enable_java_debug,
max_battery_temp=args.max_battery_temp,
min_battery_level=args.min_battery_level,
- output_device_denylist=output_device_denylist,
+ output_device_denylist=args.output_device_denylist,
reboot_timeout=args.reboot_timeout,
remove_system_webview=args.remove_system_webview,
system_app_remove_list=args.system_app_remove_list,
diff --git a/catapult/devil/devil/android/tools/script_common.py b/catapult/devil/devil/android/tools/script_common.py
index ae2b7a84..de823662 100644
--- a/catapult/devil/devil/android/tools/script_common.py
+++ b/catapult/devil/devil/android/tools/script_common.py
@@ -2,7 +2,6 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import argparse
import os
from devil import devil_env
@@ -66,22 +65,8 @@ def AddDeviceArguments(parser):
default=[],
help='Serial number of the Android device to use. (default: use all)')
- # TODO(crbug.com/1097306): Simplify this to an ungrouped --denylist-file
- # once all uses of --blacklist-file / args.blacklist_file have been updated.
- class DenylistAction(argparse.Action):
-
- #override
- def __call__(self, parser, namespace, values, option_string=None):
- setattr(namespace, 'denylist_file', values)
- setattr(namespace, 'blacklist_file', values)
-
- denylist_group = parser.add_mutually_exclusive_group()
- denylist_group.add_argument('--denylist-file',
- help='Device denylist JSON file.',
- action=DenylistAction)
- denylist_group.add_argument('--blacklist-file',
- help=argparse.SUPPRESS,
- action=DenylistAction)
+ parser.add_argument('--denylist-file',
+ help='Device denylist JSON file.')
def GetDevices(requested_devices, denylist_file):
diff --git a/catapult/devil/devil/android/tools/system_app.py b/catapult/devil/devil/android/tools/system_app.py
index c2f058e0..62bf5a59 100755
--- a/catapult/devil/devil/android/tools/system_app.py
+++ b/catapult/devil/devil/android/tools/system_app.py
@@ -34,8 +34,9 @@ logger = logging.getLogger(__name__)
SPECIAL_SYSTEM_APP_LOCATIONS = {
# Older versions of ArCore were installed in /data/app/ regardless of
# whether they were system apps or not. Newer versions install in /system/
- # if they are system apps, and in /data/app/ if they aren't.
- 'com.google.ar.core': ['/data/app/', '/system/'],
+ # if they are system apps, and in /data/app/ if they aren't. Some newer
+ # devices/OSes install in /product/app/ for system apps, as well.
+ 'com.google.ar.core': ['/data/app/', '/system/', '/product/app/'],
# On older versions of VrCore, the system app version is installed in
# /system/ like normal. However, at some point, this moved to /data/.
# So, we have to handle both cases. Like ArCore, this means we'll end up
diff --git a/catapult/devil/devil/android/tools/webview_app.py b/catapult/devil/devil/android/tools/webview_app.py
index 406de1cb..5866a66c 100755
--- a/catapult/devil/devil/android/tools/webview_app.py
+++ b/catapult/devil/devil/android/tools/webview_app.py
@@ -12,15 +12,11 @@ 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')))
+ _DEVIL_ROOT_DIR = os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..', '..', '..'))
+ sys.path.append(_DEVIL_ROOT_DIR)
+from devil import devil_env
from devil.android import apk_helper
from devil.android import device_errors
from devil.android.sdk import version_codes
@@ -29,7 +25,9 @@ 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
+
+with devil_env.SysPath(devil_env.PY_UTILS_PATH):
+ from py_utils import tempfile_ext
logger = logging.getLogger(__name__)
diff --git a/catapult/devil/devil/devil_dependencies.json b/catapult/devil/devil/devil_dependencies.json
index 8e15e357..03e8d98a 100644
--- a/catapult/devil/devil/devil_dependencies.json
+++ b/catapult/devil/devil/devil_dependencies.json
@@ -16,7 +16,7 @@
"cloud_storage_bucket": "chromium-telemetry",
"file_info": {
"linux2_x86_64": {
- "cloud_storage_hash": "8bd43e3930f6eec643d5dc64cab9e5bb4ddf4909",
+ "cloud_storage_hash": "528d0e88a0d876b0549840f0797beffca4a6a796",
"download_path": "../bin/deps/linux2/x86_64/bin/adb"
}
}
diff --git a/catapult/devil/devil/devil_env.py b/catapult/devil/devil/devil_env.py
index b39e01c8..b61d1b0e 100644
--- a/catapult/devil/devil/devil_env.py
+++ b/catapult/devil/devil/devil_env.py
@@ -15,6 +15,7 @@ CATAPULT_ROOT_PATH = os.path.abspath(
os.path.join(os.path.dirname(__file__), '..', '..'))
DEPENDENCY_MANAGER_PATH = os.path.join(CATAPULT_ROOT_PATH, 'dependency_manager')
PYMOCK_PATH = os.path.join(CATAPULT_ROOT_PATH, 'third_party', 'mock')
+PY_UTILS_PATH = os.path.join(CATAPULT_ROOT_PATH, 'common', 'py_utils')
@contextlib.contextmanager
@@ -151,7 +152,9 @@ class _Environment(object):
devil_logger.propagate = False
devil_logger.addHandler(handler)
- import py_utils.cloud_storage
+ with SysPath(PY_UTILS_PATH):
+ import py_utils.cloud_storage
+
lock_logger = py_utils.cloud_storage.logger
lock_logger.setLevel(log_level)
lock_logger.propagate = False
diff --git a/catapult/devil/devil/utils/update_dependencies.py b/catapult/devil/devil/utils/update_dependencies.py
index df1f88b2..513c6382 100755
--- a/catapult/devil/devil/utils/update_dependencies.py
+++ b/catapult/devil/devil/utils/update_dependencies.py
@@ -116,7 +116,7 @@ def UpdateDependency(dependency_name, dependency_info, local_path, platform):
remote_path = '%s_%s' % (dependency_name, dependency_sha1)
gs_dest = 'gs://%s/%s/%s' % (bucket, folder, remote_path)
ec, gsutil_output = cmd_helper.GetCmdStatusAndOutput(
- ['gsutil', 'cp', local_path, gs_dest])
+ ['gsutil.py', 'cp', local_path, gs_dest])
if ec:
raise base_error.BaseError(
'Failed to upload %s to %s: %s' % (remote_path, gs_dest, gsutil_output))
diff --git a/catapult/devil/devil/utils/zip_utils.py b/catapult/devil/devil/utils/zip_utils.py
index 35c59056..aac9641e 100644
--- a/catapult/devil/devil/utils/zip_utils.py
+++ b/catapult/devil/devil/utils/zip_utils.py
@@ -9,18 +9,17 @@ import os
import sys
import zipfile
-_DEVIL_ROOT_DIR = os.path.abspath(
- os.path.join(os.path.dirname(__file__), '..', '..'))
if __name__ == '__main__':
+ _DEVIL_ROOT_DIR = os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..', '..'))
sys.path.append(_DEVIL_ROOT_DIR)
-_PY_UTILS_ROOT_DIR = os.path.abspath(
- os.path.join(_DEVIL_ROOT_DIR, '..', 'common', 'py_utils'))
-sys.path.append(_PY_UTILS_ROOT_DIR)
-
+from devil import devil_env
from devil import base_error
from devil.utils import cmd_helper
-from py_utils import tempfile_ext
+
+with devil_env.SysPath(devil_env.PY_UTILS_PATH):
+ from py_utils import tempfile_ext
logger = logging.getLogger(__name__)
diff --git a/catapult/devil/devil/utils/zip_utils_test.py b/catapult/devil/devil/utils/zip_utils_test.py
index 96a55f55..1afc3f48 100644
--- a/catapult/devil/devil/utils/zip_utils_test.py
+++ b/catapult/devil/devil/utils/zip_utils_test.py
@@ -6,8 +6,11 @@ import os
import unittest
import zipfile
+from devil import devil_env
from devil.utils import zip_utils
-from py_utils import tempfile_ext
+
+with devil_env.SysPath(devil_env.PY_UTILS_PATH):
+ from py_utils import tempfile_ext
class WriteZipFileTest(unittest.TestCase):