aboutsummaryrefslogtreecommitdiff
path: root/catapult/devil/devil/android/device_utils.py
diff options
context:
space:
mode:
Diffstat (limited to 'catapult/devil/devil/android/device_utils.py')
-rw-r--r--catapult/devil/devil/android/device_utils.py121
1 files changed, 65 insertions, 56 deletions
diff --git a/catapult/devil/devil/android/device_utils.py b/catapult/devil/devil/android/device_utils.py
index 7b7dad24..093bfc71 100644
--- a/catapult/devil/devil/android/device_utils.py
+++ b/catapult/devil/devil/android/device_utils.py
@@ -24,6 +24,8 @@ import time
import threading
import uuid
+import six
+
from devil import base_error
from devil import devil_env
from devil.utils import cmd_helper
@@ -105,6 +107,13 @@ _RESTART_ADBD_SCRIPT = """
restart &
"""
+_UNZIP_AND_CHMOD_SCRIPT = """
+ {bin_dir}/unzip {zip_file} && (for dir in {dirs}
+ do
+ chmod -R 777 "$dir" || exit 1
+ done)
+"""
+
# Not all permissions can be set.
_PERMISSIONS_DENYLIST_RE = re.compile('|'.join(
fnmatch.translate(p) for p in [
@@ -131,6 +140,7 @@ _PERMISSIONS_DENYLIST_RE = re.compile('|'.join(
'android.permission.INTERNET',
'android.permission.KILL_BACKGROUND_PROCESSES',
'android.permission.MANAGE_ACCOUNTS',
+ 'android.permission.MANAGE_EXTERNAL_STORAGE',
'android.permission.MODIFY_AUDIO_SETTINGS',
'android.permission.NFC',
'android.permission.QUERY_ALL_PACKAGES',
@@ -314,30 +324,6 @@ def GetAVDs():
return avds
-@decorators.WithExplicitTimeoutAndRetries(_DEFAULT_TIMEOUT, _DEFAULT_RETRIES)
-def RestartServer():
- """Restarts the adb server.
-
- Raises:
- CommandFailedError if we fail to kill or restart the server.
- """
-
- def adb_killed():
- return not adb_wrapper.AdbWrapper.IsServerOnline()
-
- def adb_started():
- return adb_wrapper.AdbWrapper.IsServerOnline()
-
- adb_wrapper.AdbWrapper.KillServer()
- if not timeout_retry.WaitFor(adb_killed, wait_period=1, max_tries=5):
- # TODO(crbug.com/442319): Switch this to raise an exception if we
- # figure out why sometimes not all adb servers on bots get killed.
- logger.warning('Failed to kill adb server')
- adb_wrapper.AdbWrapper.StartServer()
- if not timeout_retry.WaitFor(adb_started, wait_period=1, max_tries=5):
- raise device_errors.CommandFailedError('Failed to start adb server')
-
-
def _ParseModeString(mode_str):
"""Parse a mode string, e.g. 'drwxrwxrwx', into a st_mode value.
@@ -376,7 +362,7 @@ def _CreateAdbWrapper(device):
def _FormatPartialOutputError(output):
- lines = output.splitlines() if isinstance(output, basestring) else output
+ lines = output.splitlines() if isinstance(output, six.string_types) else output
message = ['Partial output found:']
if len(lines) > 11:
message.extend('- %s' % line for line in lines[:5])
@@ -468,7 +454,7 @@ class DeviceUtils(object):
operation should be retried on failure if no explicit value is provided.
"""
self.adb = None
- if isinstance(device, basestring):
+ if isinstance(device, six.string_types):
self.adb = _CreateAdbWrapper(device)
elif isinstance(device, adb_wrapper.AdbWrapper):
self.adb = device
@@ -1533,7 +1519,7 @@ class DeviceUtils(object):
else:
raise
- if isinstance(cmd, basestring):
+ if isinstance(cmd, six.string_types):
if not shell:
# TODO(crbug.com/1029769): Make this an error instead.
logger.warning(
@@ -1543,7 +1529,7 @@ class DeviceUtils(object):
else:
cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd)
if env:
- env = ' '.join(env_quote(k, v) for k, v in env.iteritems())
+ env = ' '.join(env_quote(k, v) for k, v in env.items())
cmd = '%s %s' % (env, cmd)
if cwd:
cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd)
@@ -1740,7 +1726,7 @@ class DeviceUtils(object):
cmd.append('-w')
if raw:
cmd.append('-r')
- for k, v in extras.iteritems():
+ for k, v in extras.items():
cmd.extend(['-e', str(k), str(v)])
cmd.append(component)
@@ -2223,13 +2209,19 @@ class DeviceUtils(object):
self.adb, suffix='.zip') as device_temp:
self.adb.Push(zip_path, device_temp.name)
- quoted_dirs = ' '.join(cmd_helper.SingleQuote(d) for d in dirs)
- self.RunShellCommand(
- 'unzip %s&&chmod -R 777 %s' % (device_temp.name, quoted_dirs),
- shell=True,
- as_root=True,
- env={'PATH': '%s:$PATH' % install_commands.BIN_DIR},
- check_return=True)
+ with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script:
+ # Read dirs from temp file to avoid potential errors like
+ # "Argument list too long" (crbug.com/1174331) when the list
+ # is too long.
+ self.WriteFile(
+ script.name,
+ _UNZIP_AND_CHMOD_SCRIPT.format(bin_dir=install_commands.BIN_DIR,
+ zip_file=device_temp.name,
+ dirs=' '.join(dirs)))
+
+ self.RunShellCommand(['source', script.name],
+ check_return=True,
+ as_root=True)
return True
@@ -2263,7 +2255,7 @@ class DeviceUtils(object):
DeviceUnreachableError on missing device.
"""
paths = device_paths
- if isinstance(paths, basestring):
+ if isinstance(paths, six.string_types):
paths = (paths, )
if not paths:
return True
@@ -2322,7 +2314,7 @@ class DeviceUtils(object):
args.append('-f')
if recursive:
args.append('-r')
- if isinstance(device_path, basestring):
+ if isinstance(device_path, six.string_types):
args.append(device_path if not rename else _RenamePath(device_path))
else:
args.extend(
@@ -2596,7 +2588,7 @@ class DeviceUtils(object):
"""
entries = self._ParseLongLsOutput(device_path, as_root=as_root, **kwargs)
for d in entries:
- for key, value in d.items():
+ for key, value in list(d.items()):
if value is None:
del d[key] # Remove missing fields.
d['st_mode'] = _ParseModeString(d['st_mode'])
@@ -2960,7 +2952,7 @@ class DeviceUtils(object):
"""
assert isinstance(
property_name,
- basestring), ("property_name is not a string: %r" % property_name)
+ six.string_types), ("property_name is not a string: %r" % property_name)
if cache:
# It takes ~120ms to query a single property, and ~130ms to query all
@@ -3004,8 +2996,8 @@ class DeviceUtils(object):
"""
assert isinstance(
property_name,
- basestring), ("property_name is not a string: %r" % property_name)
- assert isinstance(value, basestring), "value is not a string: %r" % value
+ six.string_types), ("property_name is not a string: %r" % property_name)
+ assert isinstance(value, six.string_types), "value is not a string: %r" % value
self.RunShellCommand(['setprop', property_name, value], check_return=True)
prop_cache = self._cache['getprop']
@@ -3084,18 +3076,19 @@ class DeviceUtils(object):
Returns:
A list of ProcessInfo tuples with |name|, |pid|, and |ppid| fields.
"""
+ # pylint: disable=broad-except
process_name = process_name or ''
processes = []
for line in self._GetPsOutput(process_name):
row = line.split()
try:
- row = {k: row[i] for k, i in _PS_COLUMNS.iteritems()}
+ row = {k: row[i] for k, i in _PS_COLUMNS.items()}
if row['pid'] == 'PID' or process_name not in row['name']:
# Skip over header and non-matching processes.
continue
row['pid'] = int(row['pid'])
row['ppid'] = int(row['ppid'])
- except StandardError: # e.g. IndexError, TypeError, ValueError.
+ except Exception: # e.g. IndexError, TypeError, ValueError.
logging.warning('failed to parse ps line: %r', line)
continue
processes.append(ProcessInfo(**row))
@@ -3550,10 +3543,10 @@ class DeviceUtils(object):
# When using a cache across script invokations, verify that apps have
# not been uninstalled.
self._cache['package_apk_paths_to_verify'] = set(
- self._cache['package_apk_paths'].iterkeys())
+ self._cache['package_apk_paths'])
package_apk_checksums = obj.get('package_apk_checksums', {})
- for k, v in package_apk_checksums.iteritems():
+ for k, v in package_apk_checksums.items():
package_apk_checksums[k] = set(v)
self._cache['package_apk_checksums'] = package_apk_checksums
device_path_checksums = obj.get('device_path_checksums', {})
@@ -3577,27 +3570,27 @@ class DeviceUtils(object):
obj['package_apk_paths'] = self._cache['package_apk_paths']
obj['package_apk_checksums'] = self._cache['package_apk_checksums']
# JSON can't handle sets.
- for k, v in obj['package_apk_checksums'].iteritems():
+ for k, v in obj['package_apk_checksums'].items():
obj['package_apk_checksums'][k] = list(v)
obj['device_path_checksums'] = self._cache['device_path_checksums']
return json.dumps(obj, separators=(',', ':'))
@classmethod
- def parallel(cls, devices, async=False):
+ def parallel(cls, devices, asyn=False):
"""Creates a Parallelizer to operate over the provided list of devices.
Args:
devices: A list of either DeviceUtils instances or objects from
from which DeviceUtils instances can be constructed. If None,
all attached devices will be used.
- async: If true, returns a Parallelizer that runs operations
+ asyn: If true, returns a Parallelizer that runs operations
asynchronously.
Returns:
A Parallelizer operating over |devices|.
"""
devices = [d if isinstance(d, cls) else cls(d) for d in devices]
- if async:
+ if asyn:
return parallelizer.Parallelizer(devices)
else:
return parallelizer.SyncParallelizer(devices)
@@ -3710,7 +3703,7 @@ class DeviceUtils(object):
else:
reset_usb.reset_all_android_devices()
- for attempt in xrange(retries + 1):
+ for attempt in range(retries + 1):
try:
return _get_devices()
except device_errors.NoDevicesError:
@@ -3728,7 +3721,7 @@ class DeviceUtils(object):
'No devices found. Will try again after restarting adb server '
'and a short nap of %d s.', sleep_s)
time.sleep(sleep_s)
- RestartServer()
+ adb_wrapper.RestartServer()
@decorators.WithTimeoutAndRetriesFromInstance()
def RestartAdbd(self, timeout=None, retries=None):
@@ -3745,6 +3738,17 @@ class DeviceUtils(object):
if not permissions:
return
+ # For Andorid-11(R), enable MANAGE_EXTERNAL_STORAGE for testing.
+ # See https://bit.ly/2MBjBIM for details.
+ if ('android.permission.MANAGE_EXTERNAL_STORAGE' in permissions
+ and self.build_version_sdk >= version_codes.R):
+ script_manage_ext_storage = [
+ 'appops set {package} MANAGE_EXTERNAL_STORAGE allow',
+ 'echo "{sep}MANAGE_EXTERNAL_STORAGE{sep}$?{sep}"',
+ ]
+ else:
+ script_manage_ext_storage = []
+
permissions = set(p for p in permissions
if not _PERMISSIONS_DENYLIST_RE.match(p))
@@ -3752,10 +3756,15 @@ class DeviceUtils(object):
and 'android.permission.READ_EXTERNAL_STORAGE' not in permissions):
permissions.add('android.permission.READ_EXTERNAL_STORAGE')
- script = ';'.join([
- 'p={package}', 'for q in {permissions}', 'do pm grant "$p" "$q"',
- 'echo "{sep}$q{sep}$?{sep}"', 'done'
- ]).format(
+ script_raw = [
+ 'p={package}',
+ 'for q in {permissions}',
+ 'do pm grant "$p" "$q"',
+ 'echo "{sep}$q{sep}$?{sep}"',
+ 'done',
+ ] + script_manage_ext_storage
+
+ script = ';'.join(script_raw).format(
package=cmd_helper.SingleQuote(package),
permissions=' '.join(
cmd_helper.SingleQuote(p) for p in sorted(permissions)),