diff options
Diffstat (limited to 'catapult/devil/devil/android/device_utils.py')
-rw-r--r-- | catapult/devil/devil/android/device_utils.py | 121 |
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)), |