• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2014 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Provides a variety of device interactions based on adb.
6
7Eventually, this will be based on adb_wrapper.
8"""
9# pylint: disable=unused-argument
10
11import calendar
12import collections
13import contextlib
14import fnmatch
15import json
16import logging
17import math
18import os
19import posixpath
20import pprint
21import random
22import re
23import shutil
24import stat
25import sys
26import tempfile
27import time
28import threading
29import uuid
30
31from devil import base_error
32from devil import devil_env
33from devil.utils import cmd_helper
34from devil.android import apk_helper
35from devil.android import device_signal
36from devil.android import decorators
37from devil.android import device_errors
38from devil.android import device_temp_file
39from devil.android import install_commands
40from devil.android import logcat_monitor
41from devil.android import md5sum
42from devil.android.sdk import adb_wrapper
43from devil.android.sdk import intent
44from devil.android.sdk import keyevent
45from devil.android.sdk import split_select
46from devil.android.sdk import version_codes
47from devil.utils import host_utils
48from devil.utils import parallelizer
49from devil.utils import reraiser_thread
50from devil.utils import timeout_retry
51from devil.utils import zip_utils
52
53from py_utils import tempfile_ext
54
55try:
56  from devil.utils import reset_usb
57except ImportError:
58  # Fail silently if we can't import reset_usb. We're likely on windows.
59  reset_usb = None
60
61logger = logging.getLogger(__name__)
62
63_DEFAULT_TIMEOUT = 30
64_DEFAULT_RETRIES = 3
65
66# A sentinel object for default values
67# TODO(jbudorick,perezju): revisit how default values are handled by
68# the timeout_retry decorators.
69DEFAULT = object()
70
71# A sentinel object to require that calls to RunShellCommand force running the
72# command with su even if the device has been rooted. To use, pass into the
73# as_root param.
74_FORCE_SU = object()
75
76_RECURSIVE_DIRECTORY_LIST_SCRIPT = """
77  function list_subdirs() {
78    for f in "$1"/* ;
79    do
80      if [ -d "$f" ] ;
81      then
82        if [ "$f" == "." ] || [ "$f" == ".." ] ;
83        then
84          continue ;
85        fi ;
86        echo "$f" ;
87        list_subdirs "$f" ;
88      fi ;
89    done ;
90  } ;
91  list_subdirs %s
92"""
93
94_RESTART_ADBD_SCRIPT = """
95  trap '' HUP
96  trap '' TERM
97  trap '' PIPE
98  function restart() {
99    stop adbd
100    start adbd
101  }
102  restart &
103"""
104
105# Not all permissions can be set.
106_PERMISSIONS_BLACKLIST_RE = re.compile('|'.join(fnmatch.translate(p) for p in [
107    'android.permission.ACCESS_LOCATION_EXTRA_COMMANDS',
108    'android.permission.ACCESS_MOCK_LOCATION',
109    'android.permission.ACCESS_NETWORK_STATE',
110    'android.permission.ACCESS_NOTIFICATION_POLICY',
111    'android.permission.ACCESS_VR_STATE',
112    'android.permission.ACCESS_WIFI_STATE',
113    'android.permission.AUTHENTICATE_ACCOUNTS',
114    'android.permission.BLUETOOTH',
115    'android.permission.BLUETOOTH_ADMIN',
116    'android.permission.BROADCAST_STICKY',
117    'android.permission.CHANGE_NETWORK_STATE',
118    'android.permission.CHANGE_WIFI_MULTICAST_STATE',
119    'android.permission.CHANGE_WIFI_STATE',
120    'android.permission.DISABLE_KEYGUARD',
121    'android.permission.DOWNLOAD_WITHOUT_NOTIFICATION',
122    'android.permission.EXPAND_STATUS_BAR',
123    'android.permission.FOREGROUND_SERVICE',
124    'android.permission.GET_PACKAGE_SIZE',
125    'android.permission.INSTALL_SHORTCUT',
126    'android.permission.INJECT_EVENTS',
127    'android.permission.INTERNET',
128    'android.permission.KILL_BACKGROUND_PROCESSES',
129    'android.permission.MANAGE_ACCOUNTS',
130    'android.permission.MODIFY_AUDIO_SETTINGS',
131    'android.permission.NFC',
132    'android.permission.READ_SYNC_SETTINGS',
133    'android.permission.READ_SYNC_STATS',
134    'android.permission.RECEIVE_BOOT_COMPLETED',
135    'android.permission.RECORD_VIDEO',
136    'android.permission.REORDER_TASKS',
137    'android.permission.REQUEST_INSTALL_PACKAGES',
138    'android.permission.RESTRICTED_VR_ACCESS',
139    'android.permission.RUN_INSTRUMENTATION',
140    'android.permission.SET_ALARM',
141    'android.permission.SET_TIME_ZONE',
142    'android.permission.SET_WALLPAPER',
143    'android.permission.SET_WALLPAPER_HINTS',
144    'android.permission.TRANSMIT_IR',
145    'android.permission.USE_CREDENTIALS',
146    'android.permission.USE_FINGERPRINT',
147    'android.permission.VIBRATE',
148    'android.permission.WAKE_LOCK',
149    'android.permission.WRITE_SYNC_SETTINGS',
150    'com.android.browser.permission.READ_HISTORY_BOOKMARKS',
151    'com.android.browser.permission.WRITE_HISTORY_BOOKMARKS',
152    'com.android.launcher.permission.INSTALL_SHORTCUT',
153    'com.chrome.permission.DEVICE_EXTRAS',
154    'com.google.android.apps.now.CURRENT_ACCOUNT_ACCESS',
155    'com.google.android.c2dm.permission.RECEIVE',
156    'com.google.android.providers.gsf.permission.READ_GSERVICES',
157    'com.google.vr.vrcore.permission.VRCORE_INTERNAL',
158    'com.sec.enterprise.knox.MDM_CONTENT_PROVIDER',
159    '*.permission.C2D_MESSAGE',
160    '*.permission.READ_WRITE_BOOKMARK_FOLDERS',
161    '*.TOS_ACKED',
162]))
163_SHELL_OUTPUT_SEPARATOR = '~X~'
164_PERMISSIONS_EXCEPTION_RE = re.compile(
165    r'java\.lang\.\w+Exception: .*$', re.MULTILINE)
166
167_CURRENT_FOCUS_CRASH_RE = re.compile(
168    r'\s*mCurrentFocus.*Application (Error|Not Responding): (\S+)}')
169
170_GETPROP_RE = re.compile(r'\[(.*?)\]: \[(.*?)\]')
171
172# Regex to parse the long (-l) output of 'ls' command, c.f.
173# https://github.com/landley/toybox/blob/master/toys/posix/ls.c#L446
174_LONG_LS_OUTPUT_RE = re.compile(
175    r'(?P<st_mode>[\w-]{10})\s+'                  # File permissions
176    r'(?:(?P<st_nlink>\d+)\s+)?'                  # Number of links (optional)
177    r'(?P<st_owner>\w+)\s+'                       # Name of owner
178    r'(?P<st_group>\w+)\s+'                       # Group of owner
179    r'(?:'                                        # Either ...
180      r'(?P<st_rdev_major>\d+),\s+'                 # Device major, and
181      r'(?P<st_rdev_minor>\d+)\s+'                  # Device minor
182    r'|'                                          # .. or
183      r'(?P<st_size>\d+)\s+'                        # Size in bytes
184    r')?'                                         # .. or nothing
185    r'(?P<st_mtime>\d{4}-\d\d-\d\d \d\d:\d\d)\s+' # Modification date/time
186    r'(?P<filename>.+?)'                          # File name
187    r'(?: -> (?P<symbolic_link_to>.+))?'          # Symbolic link (optional)
188    r'$'                                          # End of string
189)
190_LS_DATE_FORMAT = '%Y-%m-%d %H:%M'
191_FILE_MODE_RE = re.compile(r'[dbclps-](?:[r-][w-][xSs-]){2}[r-][w-][xTt-]$')
192_FILE_MODE_KIND = {
193    'd': stat.S_IFDIR, 'b': stat.S_IFBLK, 'c': stat.S_IFCHR,
194    'l': stat.S_IFLNK, 'p': stat.S_IFIFO, 's': stat.S_IFSOCK,
195    '-': stat.S_IFREG}
196_FILE_MODE_PERMS = [
197    stat.S_IRUSR, stat.S_IWUSR, stat.S_IXUSR,
198    stat.S_IRGRP, stat.S_IWGRP, stat.S_IXGRP,
199    stat.S_IROTH, stat.S_IWOTH, stat.S_IXOTH,
200]
201_FILE_MODE_SPECIAL = [
202    ('s', stat.S_ISUID),
203    ('s', stat.S_ISGID),
204    ('t', stat.S_ISVTX),
205]
206_PS_COLUMNS = {
207  'pid': 1,
208  'ppid': 2,
209  'name': -1
210}
211_SELINUX_MODE = {
212    'enforcing': True,
213    'permissive': False,
214    'disabled': None
215}
216# Some devices require different logic for checking if root is necessary
217_SPECIAL_ROOT_DEVICE_LIST = [
218    'marlin', # Pixel XL
219    'sailfish', # Pixel
220    'taimen', # Pixel 2 XL
221    'vega', # Lenovo Mirage Solo
222    'walleye', # Pixel 2
223    'crosshatch', # Pixel 3 XL
224    'blueline', # Pixel 3
225]
226_SPECIAL_ROOT_DEVICE_LIST += ['aosp_%s' % _d for _d in
227                              _SPECIAL_ROOT_DEVICE_LIST]
228
229_IMEI_RE = re.compile(r'  Device ID = (.+)$')
230# The following regex is used to match result parcels like:
231"""
232Result: Parcel(
233  0x00000000: 00000000 0000000f 00350033 00360033 '........3.5.3.6.'
234  0x00000010: 00360032 00370030 00300032 00300039 '2.6.0.7.2.0.9.0.'
235  0x00000020: 00380033 00000039                   '3.8.9...        ')
236"""
237_PARCEL_RESULT_RE = re.compile(
238    r'0x[0-9a-f]{8}\: (?:[0-9a-f]{8}\s+){1,4}\'(.{16})\'')
239_EBUSY_RE = re.compile(
240    r'mkdir failed for ([^,]*), Device or resource busy')
241
242# http://bit.ly/2WLZhUF added a timeout to adb wait-for-device. We sometimes
243# want to wait longer than the implicit call within adb root allows.
244_WAIT_FOR_DEVICE_TIMEOUT_STR = 'timeout expired while waiting for device'
245
246_WEBVIEW_SYSUPDATE_CURRENT_PKG_RE = re.compile(
247    r'Current WebView package.*:.*\(([a-z.]*),')
248_WEBVIEW_SYSUPDATE_NULL_PKG_RE = re.compile(
249    r'Current WebView package is null')
250_WEBVIEW_SYSUPDATE_FALLBACK_LOGIC_RE = re.compile(
251    r'Fallback logic enabled: (true|false)')
252_WEBVIEW_SYSUPDATE_PACKAGE_INSTALLED_RE = re.compile(
253    r'(?:Valid|Invalid) package\s+(\S+)\s+\(.*\),?\s+(.*)$')
254_WEBVIEW_SYSUPDATE_PACKAGE_NOT_INSTALLED_RE = re.compile(
255    r'(\S+)\s+(is NOT installed\.)')
256_WEBVIEW_SYSUPDATE_MIN_VERSION_CODE = re.compile(
257    r'Minimum WebView version code: (\d+)')
258
259_GOOGLE_FEATURES_RE = re.compile(r'^\s*com\.google\.')
260
261PS_COLUMNS = ('name', 'pid', 'ppid')
262ProcessInfo = collections.namedtuple('ProcessInfo', PS_COLUMNS)
263
264
265@decorators.WithExplicitTimeoutAndRetries(
266    _DEFAULT_TIMEOUT, _DEFAULT_RETRIES)
267def GetAVDs():
268  """Returns a list of Android Virtual Devices.
269
270  Returns:
271    A list containing the configured AVDs.
272  """
273  lines = cmd_helper.GetCmdOutput([
274      os.path.join(devil_env.config.LocalPath('android_sdk'),
275                   'tools', 'android'),
276      'list', 'avd']).splitlines()
277  avds = []
278  for line in lines:
279    if 'Name:' not in line:
280      continue
281    key, value = (s.strip() for s in line.split(':', 1))
282    if key == 'Name':
283      avds.append(value)
284  return avds
285
286
287@decorators.WithExplicitTimeoutAndRetries(
288    _DEFAULT_TIMEOUT, _DEFAULT_RETRIES)
289def RestartServer():
290  """Restarts the adb server.
291
292  Raises:
293    CommandFailedError if we fail to kill or restart the server.
294  """
295  def adb_killed():
296    return not adb_wrapper.AdbWrapper.IsServerOnline()
297
298  def adb_started():
299    return adb_wrapper.AdbWrapper.IsServerOnline()
300
301  adb_wrapper.AdbWrapper.KillServer()
302  if not timeout_retry.WaitFor(adb_killed, wait_period=1, max_tries=5):
303    # TODO(perezju): raise an exception after fixng http://crbug.com/442319
304    logger.warning('Failed to kill adb server')
305  adb_wrapper.AdbWrapper.StartServer()
306  if not timeout_retry.WaitFor(adb_started, wait_period=1, max_tries=5):
307    raise device_errors.CommandFailedError('Failed to start adb server')
308
309
310def _ParseModeString(mode_str):
311  """Parse a mode string, e.g. 'drwxrwxrwx', into a st_mode value.
312
313  Effectively the reverse of |mode_to_string| in, e.g.:
314  https://github.com/landley/toybox/blob/master/lib/lib.c#L896
315  """
316  if not _FILE_MODE_RE.match(mode_str):
317    raise ValueError('Unexpected file mode %r', mode_str)
318  mode = _FILE_MODE_KIND[mode_str[0]]
319  for c, flag in zip(mode_str[1:], _FILE_MODE_PERMS):
320    if c != '-' and c.islower():
321      mode |= flag
322  for c, (t, flag) in zip(mode_str[3::3], _FILE_MODE_SPECIAL):
323    if c.lower() == t:
324      mode |= flag
325  return mode
326
327
328def _GetTimeStamp():
329  """Return a basic ISO 8601 time stamp with the current local time."""
330  return time.strftime('%Y%m%dT%H%M%S', time.localtime())
331
332
333def _JoinLines(lines):
334  # makes sure that the last line is also terminated, and is more memory
335  # efficient than first appending an end-line to each line and then joining
336  # all of them together.
337  return ''.join(s for line in lines for s in (line, '\n'))
338
339
340def _CreateAdbWrapper(device):
341  if isinstance(device, adb_wrapper.AdbWrapper):
342    return device
343  else:
344    return adb_wrapper.AdbWrapper(device)
345
346
347def _FormatPartialOutputError(output):
348  lines = output.splitlines() if isinstance(output, basestring) else output
349  message = ['Partial output found:']
350  if len(lines) > 11:
351    message.extend('- %s' % line for line in lines[:5])
352    message.extend('<snip>')
353    message.extend('- %s' % line for line in lines[-5:])
354  else:
355    message.extend('- %s' % line for line in lines)
356  return '\n'.join(message)
357
358
359class DeviceUtils(object):
360
361  _MAX_ADB_COMMAND_LENGTH = 512
362  _MAX_ADB_OUTPUT_LENGTH = 32768
363  _LAUNCHER_FOCUSED_RE = re.compile(
364      r'\s*mCurrentFocus.*(Launcher|launcher).*')
365  _VALID_SHELL_VARIABLE = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$')
366
367  LOCAL_PROPERTIES_PATH = posixpath.join('/', 'data', 'local.prop')
368
369  # Property in /data/local.prop that controls Java assertions.
370  JAVA_ASSERT_PROPERTY = 'dalvik.vm.enableassertions'
371
372  def __init__(self, device, enable_device_files_cache=False,
373               default_timeout=_DEFAULT_TIMEOUT,
374               default_retries=_DEFAULT_RETRIES):
375    """DeviceUtils constructor.
376
377    Args:
378      device: Either a device serial, an existing AdbWrapper instance, or an
379        an existing AndroidCommands instance.
380      enable_device_files_cache: For PushChangedFiles(), cache checksums of
381        pushed files rather than recomputing them on a subsequent call.
382      default_timeout: An integer containing the default number of seconds to
383        wait for an operation to complete if no explicit value is provided.
384      default_retries: An integer containing the default number or times an
385        operation should be retried on failure if no explicit value is provided.
386    """
387    self.adb = None
388    if isinstance(device, basestring):
389      self.adb = _CreateAdbWrapper(device)
390    elif isinstance(device, adb_wrapper.AdbWrapper):
391      self.adb = device
392    else:
393      raise ValueError('Unsupported device value: %r' % device)
394    self._commands_installed = None
395    self._default_timeout = default_timeout
396    self._default_retries = default_retries
397    self._enable_device_files_cache = enable_device_files_cache
398    self._cache = {}
399    self._client_caches = {}
400    self._cache_lock = threading.RLock()
401    assert hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR)
402    assert hasattr(self, decorators.DEFAULT_RETRIES_ATTR)
403
404    self.ClearCache()
405
406  @property
407  def serial(self):
408    """Returns the device serial."""
409    return self.adb.GetDeviceSerial()
410
411  def __eq__(self, other):
412    """Checks whether |other| refers to the same device as |self|.
413
414    Args:
415      other: The object to compare to. This can be a basestring, an instance
416        of adb_wrapper.AdbWrapper, or an instance of DeviceUtils.
417    Returns:
418      Whether |other| refers to the same device as |self|.
419    """
420    return self.serial == str(other)
421
422  def __lt__(self, other):
423    """Compares two instances of DeviceUtils.
424
425    This merely compares their serial numbers.
426
427    Args:
428      other: The instance of DeviceUtils to compare to.
429    Returns:
430      Whether |self| is less than |other|.
431    """
432    return self.serial < other.serial
433
434  def __str__(self):
435    """Returns the device serial."""
436    return self.serial
437
438  @decorators.WithTimeoutAndRetriesFromInstance()
439  def IsOnline(self, timeout=None, retries=None):
440    """Checks whether the device is online.
441
442    Args:
443      timeout: timeout in seconds
444      retries: number of retries
445
446    Returns:
447      True if the device is online, False otherwise.
448
449    Raises:
450      CommandTimeoutError on timeout.
451    """
452    try:
453      return self.adb.GetState() == 'device'
454    except base_error.BaseError as exc:
455      logger.info('Failed to get state: %s', exc)
456      return False
457
458  @decorators.WithTimeoutAndRetriesFromInstance()
459  def HasRoot(self, timeout=None, retries=None):
460    """Checks whether or not adbd has root privileges.
461
462    A device is considered to have root if all commands are implicitly run
463    with elevated privileges, i.e. without having to use "su" to run them.
464
465    Note that some devices do not allow this implicit privilige elevation,
466    but _can_ run commands as root just fine when done explicitly with "su".
467    To check if your device can run commands with elevated privileges at all
468    use:
469
470      device.HasRoot() or device.NeedsSU()
471
472    Luckily, for the most part you don't need to worry about this and using
473    RunShellCommand(cmd, as_root=True) will figure out for you the right
474    command incantation to run with elevated privileges.
475
476    Args:
477      timeout: timeout in seconds
478      retries: number of retries
479
480    Returns:
481      True if adbd has root privileges, False otherwise.
482
483    Raises:
484      CommandTimeoutError on timeout.
485      DeviceUnreachableError on missing device.
486    """
487    try:
488      if self.build_type == 'eng':
489        # 'eng' builds have root enabled by default and the adb session cannot
490        # be unrooted.
491        return True
492      if self.product_name in _SPECIAL_ROOT_DEVICE_LIST:
493        return self.GetProp('service.adb.root') == '1'
494      self.RunShellCommand(['ls', '/root'], check_return=True)
495      return True
496    except device_errors.AdbCommandFailedError:
497      return False
498
499  def NeedsSU(self, timeout=DEFAULT, retries=DEFAULT):
500    """Checks whether 'su' is needed to access protected resources.
501
502    Args:
503      timeout: timeout in seconds
504      retries: number of retries
505
506    Returns:
507      True if 'su' is available on the device and is needed to to access
508        protected resources; False otherwise if either 'su' is not available
509        (e.g. because the device has a user build), or not needed (because adbd
510        already has root privileges).
511
512    Raises:
513      CommandTimeoutError on timeout.
514      DeviceUnreachableError on missing device.
515    """
516    if 'needs_su' not in self._cache:
517      cmd = '%s && ! ls /root' % self._Su('ls /root')
518      if self.product_name in _SPECIAL_ROOT_DEVICE_LIST:
519        if self.HasRoot():
520          self._cache['needs_su'] = False
521          return False
522        cmd = 'which which && which su'
523      try:
524        self.RunShellCommand(cmd, shell=True, check_return=True,
525            timeout=self._default_timeout if timeout is DEFAULT else timeout,
526            retries=self._default_retries if retries is DEFAULT else retries)
527        self._cache['needs_su'] = True
528      except device_errors.AdbCommandFailedError:
529        self._cache['needs_su'] = False
530    return self._cache['needs_su']
531
532
533  def _Su(self, command):
534    if self.build_version_sdk >= version_codes.MARSHMALLOW:
535      return 'su 0 %s' % command
536    return 'su -c %s' % command
537
538  @decorators.WithTimeoutAndRetriesFromInstance()
539  def EnableRoot(self, timeout=None, retries=None):
540    """Restarts adbd with root privileges.
541
542    Args:
543      timeout: timeout in seconds
544      retries: number of retries
545
546    Raises:
547      CommandFailedError if root could not be enabled.
548      CommandTimeoutError on timeout.
549    """
550    if 'needs_su' in self._cache:
551      del self._cache['needs_su']
552
553    try:
554      self.adb.Root()
555    except device_errors.AdbCommandFailedError as e:
556      if self.IsUserBuild():
557        raise device_errors.CommandFailedError(
558            'Unable to root device with user build.', str(self))
559      elif e.output and _WAIT_FOR_DEVICE_TIMEOUT_STR in e.output:
560        # adb 1.0.41 added a call to wait-for-device *inside* root
561        # with a timeout that can be too short in some cases.
562        # If we hit that timeout, ignore it & do our own wait below.
563        pass
564      else:
565        raise  # Failed probably due to some other reason.
566
567    def device_online_with_root():
568      try:
569        self.adb.WaitForDevice()
570        return self.HasRoot()
571      except (device_errors.AdbCommandFailedError,
572              device_errors.DeviceUnreachableError):
573        return False
574
575    timeout_retry.WaitFor(device_online_with_root, wait_period=1)
576
577  @decorators.WithTimeoutAndRetriesFromInstance()
578  def IsUserBuild(self, timeout=None, retries=None):
579    """Checks whether or not the device is running a user build.
580
581    Args:
582      timeout: timeout in seconds
583      retries: number of retries
584
585    Returns:
586      True if the device is running a user build, False otherwise (i.e. if
587        it's running a userdebug build).
588
589    Raises:
590      CommandTimeoutError on timeout.
591      DeviceUnreachableError on missing device.
592    """
593    return self.build_type == 'user'
594
595  @decorators.WithTimeoutAndRetriesFromInstance()
596  def GetExternalStoragePath(self, timeout=None, retries=None):
597    """Get the device's path to its SD card.
598
599    Args:
600      timeout: timeout in seconds
601      retries: number of retries
602
603    Returns:
604      The device's path to its SD card.
605
606    Raises:
607      CommandFailedError if the external storage path could not be determined.
608      CommandTimeoutError on timeout.
609      DeviceUnreachableError on missing device.
610    """
611    self._EnsureCacheInitialized()
612    if not self._cache['external_storage']:
613      raise device_errors.CommandFailedError('$EXTERNAL_STORAGE is not set',
614                                             str(self))
615    return self._cache['external_storage']
616
617  @decorators.WithTimeoutAndRetriesFromInstance()
618  def GetIMEI(self, timeout=None, retries=None):
619    """Get the device's IMEI.
620
621    Args:
622      timeout: timeout in seconds
623      retries: number of retries
624
625    Returns:
626      The device's IMEI.
627
628    Raises:
629      AdbCommandFailedError on error
630    """
631    if self._cache.get('imei') is not None:
632      return self._cache.get('imei')
633
634    if self.build_version_sdk < 21:
635      out = self.RunShellCommand(['dumpsys', 'iphonesubinfo'],
636                                 raw_output=True, check_return=True)
637      if out:
638        match = re.search(_IMEI_RE, out)
639        if match:
640          self._cache['imei'] = match.group(1)
641          return self._cache['imei']
642    else:
643      out = self.RunShellCommand(['service', 'call', 'iphonesubinfo', '1'],
644                                 check_return=True)
645      if out:
646        imei = ''
647        for line in out:
648          match = re.search(_PARCEL_RESULT_RE, line)
649          if match:
650            imei = imei + match.group(1)
651        imei = imei.replace('.', '').strip()
652        if imei:
653          self._cache['imei'] = imei
654          return self._cache['imei']
655
656    raise device_errors.CommandFailedError('Unable to fetch IMEI.')
657
658  @decorators.WithTimeoutAndRetriesFromInstance()
659  def GetApplicationPaths(self, package, timeout=None, retries=None):
660    """Get the paths of the installed apks on the device for the given package.
661
662    Args:
663      package: Name of the package.
664
665    Returns:
666      List of paths to the apks on the device for the given package.
667    """
668    return self._GetApplicationPathsInternal(package)
669
670  def _GetApplicationPathsInternal(self, package, skip_cache=False):
671    cached_result = self._cache['package_apk_paths'].get(package)
672    if cached_result is not None and not skip_cache:
673      if package in self._cache['package_apk_paths_to_verify']:
674        self._cache['package_apk_paths_to_verify'].remove(package)
675        # Don't verify an app that is not thought to be installed. We are
676        # concerned only with apps we think are installed having been
677        # uninstalled manually.
678        if cached_result and not self.PathExists(cached_result):
679          cached_result = None
680          self._cache['package_apk_checksums'].pop(package, 0)
681      if cached_result is not None:
682        return list(cached_result)
683    # 'pm path' is liable to incorrectly exit with a nonzero number starting
684    # in Lollipop.
685    # TODO(jbudorick): Check if this is fixed as new Android versions are
686    # released to put an upper bound on this.
687    should_check_return = (self.build_version_sdk < version_codes.LOLLIPOP)
688    output = self.RunShellCommand(
689        ['pm', 'path', package], check_return=should_check_return)
690    apks = []
691    bad_output = False
692    for line in output:
693      if line.startswith('package:'):
694        apks.append(line[len('package:'):])
695      elif line.startswith('WARNING:'):
696        continue
697      else:
698        bad_output = True  # Unexpected line in output.
699    if not apks and output:
700      if bad_output:
701        raise device_errors.CommandFailedError(
702            'Unexpected pm path output: %r' % '\n'.join(output), str(self))
703      else:
704        logger.warning('pm returned no paths but the following warnings:')
705        for line in output:
706          logger.warning('- %s', line)
707    self._cache['package_apk_paths'][package] = list(apks)
708    return apks
709
710  @decorators.WithTimeoutAndRetriesFromInstance()
711  def GetApplicationVersion(self, package, timeout=None, retries=None):
712    """Get the version name of a package installed on the device.
713
714    Args:
715      package: Name of the package.
716
717    Returns:
718      A string with the version name or None if the package is not found
719      on the device.
720    """
721    output = self.RunShellCommand(
722        ['dumpsys', 'package', package], check_return=True)
723    if not output:
724      return None
725    for line in output:
726      line = line.strip()
727      if line.startswith('versionName='):
728        return line[len('versionName='):]
729    raise device_errors.CommandFailedError(
730        'Version name for %s not found on dumpsys output' % package, str(self))
731
732  @decorators.WithTimeoutAndRetriesFromInstance()
733  def GetPackageArchitecture(self, package, timeout=None, retries=None):
734    """Get the architecture of a package installed on the device.
735
736    Args:
737      package: Name of the package.
738
739    Returns:
740      A string with the architecture, or None if the package is missing.
741    """
742    lines = self._GetDumpsysOutput(['package', package], 'primaryCpuAbi')
743    if lines:
744      _, _, package_arch = lines[-1].partition('=')
745      return package_arch.strip()
746    return None
747
748  @decorators.WithTimeoutAndRetriesFromInstance()
749  def GetApplicationDataDirectory(self, package, timeout=None, retries=None):
750    """Get the data directory on the device for the given package.
751
752    Args:
753      package: Name of the package.
754
755    Returns:
756      The package's data directory.
757    Raises:
758      CommandFailedError if the package's data directory can't be found,
759        whether because it's not installed or otherwise.
760    """
761    output = self._RunPipedShellCommand(
762        'pm dump %s | grep dataDir=' % cmd_helper.SingleQuote(package))
763    for line in output:
764      _, _, dataDir = line.partition('dataDir=')
765      if dataDir:
766        return dataDir
767    raise device_errors.CommandFailedError(
768        'Could not find data directory for %s', package)
769
770  @decorators.WithTimeoutAndRetriesFromInstance()
771  def GetSecurityContextForPackage(self, package, encrypted=False, timeout=None,
772      retries=None):
773    """Gets the SELinux security context for the given package.
774
775    Args:
776      package: Name of the package.
777      encrypted: Whether to check in the encrypted data directory
778          (/data/user_de/0/) or the unencrypted data directory (/data/data/).
779
780    Returns:
781      The package's security context as a string, or None if not found.
782    """
783    directory = '/data/user_de/0/' if encrypted else '/data/data/'
784    for line in self.RunShellCommand(['ls', '-Z', directory],
785                                     as_root=True, check_return=True):
786      split_line = line.split()
787      # ls -Z output differs between Android versions, but the package is
788      # always last and the context always starts with "u:object"
789      if split_line[-1] == package:
790        for column in split_line:
791          if column.startswith('u:object'):
792            return column
793    return None
794
795  def TakeBugReport(self, path, timeout=60*5, retries=None):
796    """Takes a bug report and dumps it to the specified path.
797
798    This doesn't use adb's bugreport option since its behavior is dependent on
799    both adb version and device OS version. To make it simpler, this directly
800    runs the bugreport command on the device itself and dumps the stdout to a
801    file.
802
803    Args:
804      path: Path on the host to drop the bug report.
805      timeout: (optional) Timeout per try in seconds.
806      retries: (optional) Number of retries to attempt.
807    """
808    with device_temp_file.DeviceTempFile(self.adb) as device_tmp_file:
809      cmd = '( bugreport )>%s 2>&1' % device_tmp_file.name
810      self.RunShellCommand(
811          cmd, check_return=True, shell=True, timeout=timeout, retries=retries)
812      self.PullFile(device_tmp_file.name, path)
813
814  @decorators.WithTimeoutAndRetriesFromInstance()
815  def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None):
816    """Wait for the device to fully boot.
817
818    This means waiting for the device to boot, the package manager to be
819    available, and the SD card to be ready. It can optionally mean waiting
820    for wifi to come up, too.
821
822    Args:
823      wifi: A boolean indicating if we should wait for wifi to come up or not.
824      timeout: timeout in seconds
825      retries: number of retries
826
827    Raises:
828      CommandFailedError on failure.
829      CommandTimeoutError if one of the component waits times out.
830      DeviceUnreachableError if the device becomes unresponsive.
831    """
832    def sd_card_ready():
833      try:
834        self.RunShellCommand(['test', '-d', self.GetExternalStoragePath()],
835                             check_return=True)
836        return True
837      except device_errors.AdbCommandFailedError:
838        return False
839
840    def pm_ready():
841      try:
842        return self._GetApplicationPathsInternal('android', skip_cache=True)
843      except device_errors.CommandFailedError:
844        return False
845
846    def boot_completed():
847      try:
848        return self.GetProp('sys.boot_completed', cache=False) == '1'
849      except device_errors.CommandFailedError:
850        return False
851
852    def wifi_enabled():
853      return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi'],
854                                                        check_return=False)
855
856    self.adb.WaitForDevice()
857    timeout_retry.WaitFor(sd_card_ready)
858    timeout_retry.WaitFor(pm_ready)
859    timeout_retry.WaitFor(boot_completed)
860    if wifi:
861      timeout_retry.WaitFor(wifi_enabled)
862
863  REBOOT_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT
864
865  @decorators.WithTimeoutAndRetriesFromInstance(
866      min_default_timeout=REBOOT_DEFAULT_TIMEOUT)
867  def Reboot(self, block=True, wifi=False, timeout=None, retries=None):
868    """Reboot the device.
869
870    Args:
871      block: A boolean indicating if we should wait for the reboot to complete.
872      wifi: A boolean indicating if we should wait for wifi to be enabled after
873        the reboot. The option has no effect unless |block| is also True.
874      timeout: timeout in seconds
875      retries: number of retries
876
877    Raises:
878      CommandTimeoutError on timeout.
879      DeviceUnreachableError on missing device.
880    """
881    def device_offline():
882      return not self.IsOnline()
883
884    self.adb.Reboot()
885    self.ClearCache()
886    timeout_retry.WaitFor(device_offline, wait_period=1)
887    if block:
888      self.WaitUntilFullyBooted(wifi=wifi)
889
890  INSTALL_DEFAULT_TIMEOUT = 8 * _DEFAULT_TIMEOUT
891
892  @decorators.WithTimeoutAndRetriesFromInstance(
893      min_default_timeout=INSTALL_DEFAULT_TIMEOUT)
894  def Install(self, apk, allow_downgrade=False, reinstall=False,
895              permissions=None, timeout=None, retries=None, modules=None):
896    """Install an APK or app bundle.
897
898    Noop if an identical APK is already installed. If installing a bundle, the
899    bundletools helper script (bin/*_bundle) should be used rather than the .aab
900    file.
901
902    Args:
903      apk: An ApkHelper instance or string containing the path to the APK or
904        bundle.
905      allow_downgrade: A boolean indicating if we should allow downgrades.
906      reinstall: A boolean indicating if we should keep any existing app data.
907        Ignored if |apk| is a bundle.
908      permissions: Set of permissions to set. If not set, finds permissions with
909          apk helper. To set no permissions, pass [].
910      timeout: timeout in seconds
911      retries: number of retries
912      modules: An iterable containing specific bundle modules to install.
913          Error if set and |apk| points to an APK instead of a bundle.
914
915    Raises:
916      CommandFailedError if the installation fails.
917      CommandTimeoutError if the installation times out.
918      DeviceUnreachableError on missing device.
919    """
920    self._InstallInternal(apk, None, allow_downgrade=allow_downgrade,
921                          reinstall=reinstall, permissions=permissions,
922                          modules=modules)
923
924  @decorators.WithTimeoutAndRetriesFromInstance(
925      min_default_timeout=INSTALL_DEFAULT_TIMEOUT)
926  def InstallSplitApk(self, base_apk, split_apks, allow_downgrade=False,
927                      reinstall=False, allow_cached_props=False,
928                      permissions=None, timeout=None, retries=None):
929    """Install a split APK.
930
931    Noop if all of the APK splits are already installed.
932
933    Args:
934      base_apk: An ApkHelper instance or string containing the path to the base
935          APK.
936      split_apks: A list of strings of paths of all of the APK splits.
937      allow_downgrade: A boolean indicating if we should allow downgrades.
938      reinstall: A boolean indicating if we should keep any existing app data.
939      allow_cached_props: Whether to use cached values for device properties.
940      permissions: Set of permissions to set. If not set, finds permissions with
941          apk helper. To set no permissions, pass [].
942      timeout: timeout in seconds
943      retries: number of retries
944
945    Raises:
946      CommandFailedError if the installation fails.
947      CommandTimeoutError if the installation times out.
948      DeviceUnreachableError on missing device.
949      DeviceVersionError if device SDK is less than Android L.
950    """
951    self._InstallInternal(base_apk, split_apks, reinstall=reinstall,
952                          allow_cached_props=allow_cached_props,
953                          permissions=permissions,
954                          allow_downgrade=allow_downgrade)
955
956  def _InstallInternal(self, base_apk, split_apks, allow_downgrade=False,
957                       reinstall=False, allow_cached_props=False,
958                       permissions=None, modules=None):
959    base_apk = apk_helper.ToHelper(base_apk)
960    if base_apk.is_bundle:
961      if split_apks:
962        raise device_errors.CommandFailedError(
963            'Attempted to install a bundle {} while specifying split apks'
964            .format(base_apk))
965      if allow_downgrade:
966        logging.warning('Installation of a bundle requested with '
967                        'allow_downgrade=False. This is not possible with '
968                        'bundletools, no downgrading is possible. This '
969                        'flag will be ignored and installation will proceed.')
970      # |allow_cached_props| is unused and ignored for bundles.
971      self._InstallBundleInternal(base_apk, permissions, modules)
972      return
973
974    if modules:
975      raise device_errors.CommandFailedError(
976          'Attempted to specify modules to install when providing an APK')
977
978    if split_apks:
979      self._CheckSdkLevel(version_codes.LOLLIPOP)
980
981    all_apks = [base_apk.path]
982    if split_apks:
983      all_apks += split_select.SelectSplits(
984        self, base_apk.path, split_apks, allow_cached_props=allow_cached_props)
985      if len(all_apks) == 1:
986        logger.warning('split-select did not select any from %s', split_apks)
987
988    missing_apks = [apk for apk in all_apks if not os.path.exists(apk)]
989    if missing_apks:
990      raise device_errors.CommandFailedError(
991          'Attempted to install non-existent apks: %s'
992              % pprint.pformat(missing_apks))
993
994    package_name = base_apk.GetPackageName()
995    device_apk_paths = self._GetApplicationPathsInternal(package_name)
996
997    apks_to_install = None
998    host_checksums = None
999    if not device_apk_paths:
1000      apks_to_install = all_apks
1001    elif len(device_apk_paths) > 1 and not split_apks:
1002      logger.warning(
1003          'Installing non-split APK when split APK was previously installed')
1004      apks_to_install = all_apks
1005    elif len(device_apk_paths) == 1 and split_apks:
1006      logger.warning(
1007          'Installing split APK when non-split APK was previously installed')
1008      apks_to_install = all_apks
1009    else:
1010      try:
1011        apks_to_install, host_checksums = (
1012            self._ComputeStaleApks(package_name, all_apks))
1013      except EnvironmentError as e:
1014        logger.warning('Error calculating md5: %s', e)
1015        apks_to_install, host_checksums = all_apks, None
1016      if apks_to_install and not reinstall:
1017        apks_to_install = all_apks
1018
1019    if device_apk_paths and apks_to_install and not reinstall:
1020      self.Uninstall(package_name)
1021
1022    if apks_to_install:
1023      # Assume that we won't know the resulting device state.
1024      self._cache['package_apk_paths'].pop(package_name, 0)
1025      self._cache['package_apk_checksums'].pop(package_name, 0)
1026      if split_apks:
1027        partial = package_name if len(apks_to_install) < len(all_apks) else None
1028        self.adb.InstallMultiple(
1029            apks_to_install, partial=partial, reinstall=reinstall,
1030            allow_downgrade=allow_downgrade)
1031      else:
1032        self.adb.Install(
1033            base_apk.path, reinstall=reinstall, allow_downgrade=allow_downgrade)
1034    else:
1035      # Running adb install terminates running instances of the app, so to be
1036      # consistent, we explicitly terminate it when skipping the install.
1037      self.ForceStop(package_name)
1038
1039    if (permissions is None
1040        and self.build_version_sdk >= version_codes.MARSHMALLOW):
1041      permissions = base_apk.GetPermissions()
1042    self.GrantPermissions(package_name, permissions)
1043    # Upon success, we know the device checksums, but not their paths.
1044    if host_checksums is not None:
1045      self._cache['package_apk_checksums'][package_name] = host_checksums
1046
1047  def _InstallBundleInternal(self, bundle, permissions, modules):
1048    cmd = [bundle.path, 'install', '--device', self.serial]
1049    if modules:
1050      for m in modules:
1051        cmd.extend(['-m', m])
1052    status = cmd_helper.RunCmd(cmd)
1053    if status != 0:
1054      raise device_errors.CommandFailedError('Cound not install {}'.format(
1055          bundle.path))
1056    if (permissions is None
1057        and self.build_version_sdk >= version_codes.MARSHMALLOW):
1058      permissions = bundle.GetPermissions()
1059    self.GrantPermissions(bundle.GetPackageName(), permissions)
1060
1061  @decorators.WithTimeoutAndRetriesFromInstance()
1062  def Uninstall(self, package_name, keep_data=False, timeout=None,
1063                retries=None):
1064    """Remove the app |package_name| from the device.
1065
1066    This is a no-op if the app is not already installed.
1067
1068    Args:
1069      package_name: The package to uninstall.
1070      keep_data: (optional) Whether to keep the data and cache directories.
1071      timeout: Timeout in seconds.
1072      retries: Number of retries.
1073
1074    Raises:
1075      CommandFailedError if the uninstallation fails.
1076      CommandTimeoutError if the uninstallation times out.
1077      DeviceUnreachableError on missing device.
1078    """
1079    installed = self._GetApplicationPathsInternal(package_name)
1080    if not installed:
1081      return
1082    # cached package paths are indeterminate due to system apps taking over
1083    # user apps after uninstall, so clear it
1084    self._cache['package_apk_paths'].pop(package_name, 0)
1085    self._cache['package_apk_checksums'].pop(package_name, 0)
1086    self.adb.Uninstall(package_name, keep_data)
1087
1088  def _CheckSdkLevel(self, required_sdk_level):
1089    """Raises an exception if the device does not have the required SDK level.
1090    """
1091    if self.build_version_sdk < required_sdk_level:
1092      raise device_errors.DeviceVersionError(
1093          ('Requires SDK level %s, device is SDK level %s' %
1094           (required_sdk_level, self.build_version_sdk)),
1095           device_serial=self.serial)
1096
1097  @decorators.WithTimeoutAndRetriesFromInstance()
1098  def RunShellCommand(self, cmd, shell=False, check_return=False, cwd=None,
1099                      env=None, run_as=None, as_root=False, single_line=False,
1100                      large_output=False, raw_output=False, timeout=None,
1101                      retries=None):
1102    """Run an ADB shell command.
1103
1104    The command to run |cmd| should be a sequence of program arguments
1105    (preferred) or a single string with a shell script to run.
1106
1107    When |cmd| is a sequence, it is assumed to contain the name of the command
1108    to run followed by its arguments. In this case, arguments are passed to the
1109    command exactly as given, preventing any further processing by the shell.
1110    This allows callers to easily pass arguments with spaces or special
1111    characters without having to worry about quoting rules. Whenever possible,
1112    it is recomended to pass |cmd| as a sequence.
1113
1114    When |cmd| is passed as a single string, |shell| should be set to True.
1115    The command will be interpreted and run by the shell on the device,
1116    allowing the use of shell features such as pipes, wildcards, or variables.
1117    Failing to set shell=True will issue a warning, but this will be changed
1118    to a hard failure in the future (see: catapult:#3242).
1119
1120    This behaviour is consistent with that of command runners in cmd_helper as
1121    well as Python's own subprocess.Popen.
1122
1123    TODO(perezju) Change the default of |check_return| to True when callers
1124      have switched to the new behaviour.
1125
1126    Args:
1127      cmd: A sequence containing the command to run and its arguments, or a
1128        string with a shell script to run (should also set shell=True).
1129      shell: A boolean indicating whether shell features may be used in |cmd|.
1130      check_return: A boolean indicating whether or not the return code should
1131        be checked.
1132      cwd: The device directory in which the command should be run.
1133      env: The environment variables with which the command should be run.
1134      run_as: A string containing the package as which the command should be
1135        run.
1136      as_root: A boolean indicating whether the shell command should be run
1137        with root privileges.
1138      single_line: A boolean indicating if only a single line of output is
1139        expected.
1140      large_output: Uses a work-around for large shell command output. Without
1141        this large output will be truncated.
1142      raw_output: Whether to only return the raw output
1143          (no splitting into lines).
1144      timeout: timeout in seconds
1145      retries: number of retries
1146
1147    Returns:
1148      If single_line is False, the output of the command as a list of lines,
1149      otherwise, a string with the unique line of output emmited by the command
1150      (with the optional newline at the end stripped).
1151
1152    Raises:
1153      AdbCommandFailedError if check_return is True and the exit code of
1154        the command run on the device is non-zero.
1155      CommandFailedError if single_line is True but the output contains two or
1156        more lines.
1157      CommandTimeoutError on timeout.
1158      DeviceUnreachableError on missing device.
1159    """
1160    def env_quote(key, value):
1161      if not DeviceUtils._VALID_SHELL_VARIABLE.match(key):
1162        raise KeyError('Invalid shell variable name %r' % key)
1163      # using double quotes here to allow interpolation of shell variables
1164      return '%s=%s' % (key, cmd_helper.DoubleQuote(value))
1165
1166    def run(cmd):
1167      return self.adb.Shell(cmd)
1168
1169    def handle_check_return(cmd):
1170      try:
1171        return run(cmd)
1172      except device_errors.AdbCommandFailedError as exc:
1173        if check_return:
1174          raise
1175        else:
1176          return exc.output
1177
1178    def handle_large_command(cmd):
1179      if len(cmd) < self._MAX_ADB_COMMAND_LENGTH:
1180        return handle_check_return(cmd)
1181      else:
1182        with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script:
1183          self._WriteFileWithPush(script.name, cmd)
1184          logger.info('Large shell command will be run from file: %s ...',
1185                      cmd[:self._MAX_ADB_COMMAND_LENGTH])
1186          return handle_check_return('sh %s' % script.name_quoted)
1187
1188    def handle_large_output(cmd, large_output_mode):
1189      if large_output_mode:
1190        with device_temp_file.DeviceTempFile(self.adb) as large_output_file:
1191          large_output_cmd = '( %s )>%s 2>&1' % (cmd, large_output_file.name)
1192          logger.debug('Large output mode enabled. Will write output to '
1193                       'device and read results from file.')
1194          try:
1195            handle_large_command(large_output_cmd)
1196            return self.ReadFile(large_output_file.name, force_pull=True)
1197          except device_errors.AdbShellCommandFailedError as exc:
1198            output = self.ReadFile(large_output_file.name, force_pull=True)
1199            raise device_errors.AdbShellCommandFailedError(
1200                cmd, output, exc.status, exc.device_serial)
1201      else:
1202        try:
1203          return handle_large_command(cmd)
1204        except device_errors.AdbCommandFailedError as exc:
1205          if exc.status is None:
1206            logger.error(_FormatPartialOutputError(exc.output))
1207            logger.warning('Attempting to run in large_output mode.')
1208            logger.warning('Use RunShellCommand(..., large_output=True) for '
1209                           'shell commands that expect a lot of output.')
1210            return handle_large_output(cmd, True)
1211          else:
1212            raise
1213
1214    if isinstance(cmd, basestring):
1215      if not shell:
1216        logger.warning(
1217            'The command to run should preferably be passed as a sequence of'
1218            ' args. If shell features are needed (pipes, wildcards, variables)'
1219            ' clients should explicitly set shell=True.')
1220    else:
1221      cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd)
1222    if env:
1223      env = ' '.join(env_quote(k, v) for k, v in env.iteritems())
1224      cmd = '%s %s' % (env, cmd)
1225    if cwd:
1226      cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd)
1227    if run_as:
1228      cmd = 'run-as %s sh -c %s' % (cmd_helper.SingleQuote(run_as),
1229                                    cmd_helper.SingleQuote(cmd))
1230    if (as_root is _FORCE_SU) or (as_root and self.NeedsSU()):
1231      # "su -c sh -c" allows using shell features in |cmd|
1232      cmd = self._Su('sh -c %s' % cmd_helper.SingleQuote(cmd))
1233
1234    output = handle_large_output(cmd, large_output)
1235
1236    if raw_output:
1237      return output
1238
1239    output = output.splitlines()
1240    if single_line:
1241      if not output:
1242        return ''
1243      elif len(output) == 1:
1244        return output[0]
1245      else:
1246        msg = 'one line of output was expected, but got: %s'
1247        raise device_errors.CommandFailedError(msg % output, str(self))
1248    else:
1249      return output
1250
1251  def _RunPipedShellCommand(self, script, **kwargs):
1252    PIPESTATUS_LEADER = 'PIPESTATUS: '
1253
1254    script += '; echo "%s${PIPESTATUS[@]}"' % PIPESTATUS_LEADER
1255    kwargs.update(shell=True, check_return=True)
1256    output = self.RunShellCommand(script, **kwargs)
1257    pipestatus_line = output[-1]
1258
1259    if not pipestatus_line.startswith(PIPESTATUS_LEADER):
1260      logger.error('Pipe exit statuses of shell script missing.')
1261      raise device_errors.AdbShellCommandFailedError(
1262          script, output, status=None,
1263          device_serial=self.serial)
1264
1265    output = output[:-1]
1266    statuses = [
1267        int(s) for s in pipestatus_line[len(PIPESTATUS_LEADER):].split()]
1268    if any(statuses):
1269      raise device_errors.AdbShellCommandFailedError(
1270          script, output, status=statuses,
1271          device_serial=self.serial)
1272    return output
1273
1274  @decorators.WithTimeoutAndRetriesFromInstance()
1275  def KillAll(self, process_name, exact=False, signum=device_signal.SIGKILL,
1276              as_root=False, blocking=False, quiet=False,
1277              timeout=None, retries=None):
1278    """Kill all processes with the given name on the device.
1279
1280    Args:
1281      process_name: A string containing the name of the process to kill.
1282      exact: A boolean indicating whether to kill all processes matching
1283             the string |process_name| exactly, or all of those which contain
1284             |process_name| as a substring. Defaults to False.
1285      signum: An integer containing the signal number to send to kill. Defaults
1286              to SIGKILL (9).
1287      as_root: A boolean indicating whether the kill should be executed with
1288               root privileges.
1289      blocking: A boolean indicating whether we should wait until all processes
1290                with the given |process_name| are dead.
1291      quiet: A boolean indicating whether to ignore the fact that no processes
1292             to kill were found.
1293      timeout: timeout in seconds
1294      retries: number of retries
1295
1296    Returns:
1297      The number of processes attempted to kill.
1298
1299    Raises:
1300      CommandFailedError if no process was killed and |quiet| is False.
1301      CommandTimeoutError on timeout.
1302      DeviceUnreachableError on missing device.
1303    """
1304    processes = self.ListProcesses(process_name)
1305    if exact:
1306      processes = [p for p in processes if p.name == process_name]
1307    if not processes:
1308      if quiet:
1309        return 0
1310      else:
1311        raise device_errors.CommandFailedError(
1312            'No processes matching %r (exact=%r)' % (process_name, exact),
1313            str(self))
1314
1315    logger.info(
1316        'KillAll(%r, ...) attempting to kill the following:', process_name)
1317    for p in processes:
1318      logger.info('  %05d %s', p.pid, p.name)
1319
1320    pids = set(p.pid for p in processes)
1321    cmd = ['kill', '-%d' % signum] + sorted(str(p) for p in pids)
1322    self.RunShellCommand(cmd, as_root=as_root, check_return=True)
1323
1324    def all_pids_killed():
1325      pids_left = (p.pid for p in self.ListProcesses(process_name))
1326      return not pids.intersection(pids_left)
1327
1328    if blocking:
1329      timeout_retry.WaitFor(all_pids_killed, wait_period=0.1)
1330
1331    return len(pids)
1332
1333  @decorators.WithTimeoutAndRetriesFromInstance()
1334  def StartActivity(self, intent_obj, blocking=False, trace_file_name=None,
1335                    force_stop=False, timeout=None, retries=None):
1336    """Start package's activity on the device.
1337
1338    Args:
1339      intent_obj: An Intent object to send.
1340      blocking: A boolean indicating whether we should wait for the activity to
1341                finish launching.
1342      trace_file_name: If present, a string that both indicates that we want to
1343                       profile the activity and contains the path to which the
1344                       trace should be saved.
1345      force_stop: A boolean indicating whether we should stop the activity
1346                  before starting it.
1347      timeout: timeout in seconds
1348      retries: number of retries
1349
1350    Raises:
1351      CommandFailedError if the activity could not be started.
1352      CommandTimeoutError on timeout.
1353      DeviceUnreachableError on missing device.
1354    """
1355    cmd = ['am', 'start']
1356    if blocking:
1357      cmd.append('-W')
1358    if trace_file_name:
1359      cmd.extend(['--start-profiler', trace_file_name])
1360    if force_stop:
1361      cmd.append('-S')
1362    cmd.extend(intent_obj.am_args)
1363    for line in self.RunShellCommand(cmd, check_return=True):
1364      if line.startswith('Error:'):
1365        raise device_errors.CommandFailedError(line, str(self))
1366
1367  @decorators.WithTimeoutAndRetriesFromInstance()
1368  def StartService(self, intent_obj, user_id=None, timeout=None, retries=None):
1369    """Start a service on the device.
1370
1371    Args:
1372      intent_obj: An Intent object to send describing the service to start.
1373      user_id: A specific user to start the service as, defaults to current.
1374      timeout: Timeout in seconds.
1375      retries: Number of retries
1376
1377    Raises:
1378      CommandFailedError if the service could not be started.
1379      CommandTimeoutError on timeout.
1380      DeviceUnreachableError on missing device.
1381    """
1382    # For whatever reason, startservice was changed to start-service on O and
1383    # above.
1384    cmd = ['am', 'startservice']
1385    if self.build_version_sdk >= version_codes.OREO:
1386      cmd[1] = 'start-service'
1387    if user_id:
1388      cmd.extend(['--user', str(user_id)])
1389    cmd.extend(intent_obj.am_args)
1390    for line in self.RunShellCommand(cmd, check_return=True):
1391      if line.startswith('Error:'):
1392        raise device_errors.CommandFailedError(line, str(self))
1393
1394  @decorators.WithTimeoutAndRetriesFromInstance()
1395  def StartInstrumentation(self, component, finish=True, raw=False,
1396                           extras=None, timeout=None, retries=None):
1397    if extras is None:
1398      extras = {}
1399
1400    cmd = ['am', 'instrument']
1401    if finish:
1402      cmd.append('-w')
1403    if raw:
1404      cmd.append('-r')
1405    for k, v in extras.iteritems():
1406      cmd.extend(['-e', str(k), str(v)])
1407    cmd.append(component)
1408
1409    # Store the package name in a shell variable to help the command stay under
1410    # the _MAX_ADB_COMMAND_LENGTH limit.
1411    package = component.split('/')[0]
1412    shell_snippet = 'p=%s;%s' % (package,
1413                                 cmd_helper.ShrinkToSnippet(cmd, 'p', package))
1414    return self.RunShellCommand(shell_snippet, shell=True, check_return=True,
1415                                large_output=True)
1416
1417  @decorators.WithTimeoutAndRetriesFromInstance()
1418  def BroadcastIntent(self, intent_obj, timeout=None, retries=None):
1419    """Send a broadcast intent.
1420
1421    Args:
1422      intent: An Intent to broadcast.
1423      timeout: timeout in seconds
1424      retries: number of retries
1425
1426    Raises:
1427      CommandTimeoutError on timeout.
1428      DeviceUnreachableError on missing device.
1429    """
1430    cmd = ['am', 'broadcast'] + intent_obj.am_args
1431    self.RunShellCommand(cmd, check_return=True)
1432
1433  @decorators.WithTimeoutAndRetriesFromInstance()
1434  def GoHome(self, timeout=None, retries=None):
1435    """Return to the home screen and obtain launcher focus.
1436
1437    This command launches the home screen and attempts to obtain
1438    launcher focus until the timeout is reached.
1439
1440    Args:
1441      timeout: timeout in seconds
1442      retries: number of retries
1443
1444    Raises:
1445      CommandTimeoutError on timeout.
1446      DeviceUnreachableError on missing device.
1447    """
1448    def is_launcher_focused():
1449      output = self.RunShellCommand(['dumpsys', 'window', 'windows'],
1450                                    check_return=True, large_output=True)
1451      return any(self._LAUNCHER_FOCUSED_RE.match(l) for l in output)
1452
1453    def dismiss_popups():
1454      # There is a dialog present; attempt to get rid of it.
1455      # Not all dialogs can be dismissed with back.
1456      self.SendKeyEvent(keyevent.KEYCODE_ENTER)
1457      self.SendKeyEvent(keyevent.KEYCODE_BACK)
1458      return is_launcher_focused()
1459
1460    # If Home is already focused, return early to avoid unnecessary work.
1461    if is_launcher_focused():
1462      return
1463
1464    self.StartActivity(
1465        intent.Intent(action='android.intent.action.MAIN',
1466                      category='android.intent.category.HOME'),
1467        blocking=True)
1468
1469    if not is_launcher_focused():
1470      timeout_retry.WaitFor(dismiss_popups, wait_period=1)
1471
1472  @decorators.WithTimeoutAndRetriesFromInstance()
1473  def ForceStop(self, package, timeout=None, retries=None):
1474    """Close the application.
1475
1476    Args:
1477      package: A string containing the name of the package to stop.
1478      timeout: timeout in seconds
1479      retries: number of retries
1480
1481    Raises:
1482      CommandTimeoutError on timeout.
1483      DeviceUnreachableError on missing device.
1484    """
1485    if self.GetApplicationPids(package):
1486      self.RunShellCommand(['am', 'force-stop', package], check_return=True)
1487
1488  @decorators.WithTimeoutAndRetriesFromInstance()
1489  def ClearApplicationState(
1490      self, package, permissions=None, timeout=None, retries=None):
1491    """Clear all state for the given package.
1492
1493    Args:
1494      package: A string containing the name of the package to stop.
1495      permissions: List of permissions to set after clearing data.
1496      timeout: timeout in seconds
1497      retries: number of retries
1498
1499    Raises:
1500      CommandTimeoutError on timeout.
1501      DeviceUnreachableError on missing device.
1502    """
1503    # Check that the package exists before clearing it for android builds below
1504    # JB MR2. Necessary because calling pm clear on a package that doesn't exist
1505    # may never return.
1506    if ((self.build_version_sdk >= version_codes.JELLY_BEAN_MR2)
1507        or self._GetApplicationPathsInternal(package)):
1508      self.RunShellCommand(['pm', 'clear', package], check_return=True)
1509      self.GrantPermissions(package, permissions)
1510
1511  @decorators.WithTimeoutAndRetriesFromInstance()
1512  def SendKeyEvent(self, keycode, timeout=None, retries=None):
1513    """Sends a keycode to the device.
1514
1515    See the devil.android.sdk.keyevent module for suitable keycode values.
1516
1517    Args:
1518      keycode: A integer keycode to send to the device.
1519      timeout: timeout in seconds
1520      retries: number of retries
1521
1522    Raises:
1523      CommandTimeoutError on timeout.
1524      DeviceUnreachableError on missing device.
1525    """
1526    self.RunShellCommand(['input', 'keyevent', format(keycode, 'd')],
1527                         check_return=True)
1528
1529  PUSH_CHANGED_FILES_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT
1530
1531  @decorators.WithTimeoutAndRetriesFromInstance(
1532      min_default_timeout=PUSH_CHANGED_FILES_DEFAULT_TIMEOUT)
1533  def PushChangedFiles(self, host_device_tuples, timeout=None,
1534                       retries=None, delete_device_stale=False):
1535    """Push files to the device, skipping files that don't need updating.
1536
1537    When a directory is pushed, it is traversed recursively on the host and
1538    all files in it are pushed to the device as needed.
1539    Additionally, if delete_device_stale option is True,
1540    files that exist on the device but don't exist on the host are deleted.
1541
1542    Args:
1543      host_device_tuples: A list of (host_path, device_path) tuples, where
1544        |host_path| is an absolute path of a file or directory on the host
1545        that should be minimially pushed to the device, and |device_path| is
1546        an absolute path of the destination on the device.
1547      timeout: timeout in seconds
1548      retries: number of retries
1549      delete_device_stale: option to delete stale files on device
1550
1551    Raises:
1552      CommandFailedError on failure.
1553      CommandTimeoutError on timeout.
1554      DeviceUnreachableError on missing device.
1555    """
1556
1557    all_changed_files = []
1558    all_stale_files = []
1559    missing_dirs = set()
1560    cache_commit_funcs = []
1561    for h, d in host_device_tuples:
1562      assert os.path.isabs(h) and posixpath.isabs(d)
1563      h = os.path.realpath(h)
1564      changed_files, up_to_date_files, stale_files, cache_commit_func = (
1565          self._GetChangedAndStaleFiles(h, d, delete_device_stale))
1566      all_changed_files += changed_files
1567      all_stale_files += stale_files
1568      cache_commit_funcs.append(cache_commit_func)
1569      if changed_files and not up_to_date_files and not stale_files:
1570        if os.path.isdir(h):
1571          missing_dirs.add(d)
1572        else:
1573          missing_dirs.add(posixpath.dirname(d))
1574
1575    if delete_device_stale and all_stale_files:
1576      self.RemovePath(all_stale_files, force=True, recursive=True)
1577
1578    if all_changed_files:
1579      if missing_dirs:
1580        try:
1581          self.RunShellCommand(['mkdir', '-p'] + list(missing_dirs),
1582                               check_return=True)
1583        except device_errors.AdbShellCommandFailedError as e:
1584          # TODO(crbug.com/739899): This is attempting to diagnose flaky EBUSY
1585          # errors that have been popping up in single-device scenarios.
1586          # Remove it once we've figured out what's causing them and how best
1587          # to handle them.
1588          m = _EBUSY_RE.search(e.output)
1589          if m:
1590            logging.error(
1591                'Hit EBUSY while attempting to make missing directories.')
1592            logging.error('lsof output:')
1593            # Don't check for return below since grep exits with a non-zero when
1594            # no match is found.
1595            for l in self.RunShellCommand(
1596                'lsof | grep %s' % cmd_helper.SingleQuote(m.group(1)),
1597                check_return=False):
1598              logging.error('  %s', l)
1599          raise
1600      self._PushFilesImpl(host_device_tuples, all_changed_files)
1601    for func in cache_commit_funcs:
1602      func()
1603
1604  def _GetChangedAndStaleFiles(self, host_path, device_path, track_stale=False):
1605    """Get files to push and delete
1606
1607    Args:
1608      host_path: an absolute path of a file or directory on the host
1609      device_path: an absolute path of a file or directory on the device
1610      track_stale: whether to bother looking for stale files (slower)
1611
1612    Returns:
1613      a four-element tuple
1614      1st element: a list of (host_files_path, device_files_path) tuples to push
1615      2nd element: a list of host_files_path that are up-to-date
1616      3rd element: a list of stale files under device_path, or [] when
1617        track_stale == False
1618      4th element: a cache commit function.
1619    """
1620    try:
1621      # Length calculations below assume no trailing /.
1622      host_path = host_path.rstrip('/')
1623      device_path = device_path.rstrip('/')
1624
1625      specific_device_paths = [device_path]
1626      ignore_other_files = not track_stale and os.path.isdir(host_path)
1627      if ignore_other_files:
1628        specific_device_paths = []
1629        for root, _, filenames in os.walk(host_path):
1630          relative_dir = root[len(host_path) + 1:]
1631          specific_device_paths.extend(
1632              posixpath.join(device_path, relative_dir, f) for f in filenames)
1633
1634      def calculate_host_checksums():
1635        return md5sum.CalculateHostMd5Sums([host_path])
1636
1637      def calculate_device_checksums():
1638        if self._enable_device_files_cache:
1639          cache_entry = self._cache['device_path_checksums'].get(device_path)
1640          if cache_entry and cache_entry[0] == ignore_other_files:
1641            return dict(cache_entry[1])
1642
1643        sums = md5sum.CalculateDeviceMd5Sums(specific_device_paths, self)
1644
1645        cache_entry = [ignore_other_files, sums]
1646        self._cache['device_path_checksums'][device_path] = cache_entry
1647        return dict(sums)
1648
1649      host_checksums, device_checksums = reraiser_thread.RunAsync((
1650          calculate_host_checksums,
1651          calculate_device_checksums))
1652    except EnvironmentError as e:
1653      logger.warning('Error calculating md5: %s', e)
1654      return ([(host_path, device_path)], [], [], lambda: 0)
1655
1656    to_push = []
1657    up_to_date = []
1658    to_delete = []
1659    if os.path.isfile(host_path):
1660      host_checksum = host_checksums.get(host_path)
1661      device_checksum = device_checksums.get(device_path)
1662      if host_checksum == device_checksum:
1663        up_to_date.append(host_path)
1664      else:
1665        to_push.append((host_path, device_path))
1666    else:
1667      for host_abs_path, host_checksum in host_checksums.iteritems():
1668        device_abs_path = posixpath.join(
1669            device_path, os.path.relpath(host_abs_path, host_path))
1670        device_checksum = device_checksums.pop(device_abs_path, None)
1671        if device_checksum == host_checksum:
1672          up_to_date.append(host_abs_path)
1673        else:
1674          to_push.append((host_abs_path, device_abs_path))
1675      to_delete = device_checksums.keys()
1676    # We can't rely solely on the checksum approach since it does not catch
1677    # stale directories, which can result in empty directories that cause issues
1678    # during copying in efficient_android_directory_copy.sh. So, find any stale
1679    # directories here so they can be removed in addition to stale files.
1680    if track_stale:
1681      to_delete.extend(self._GetStaleDirectories(host_path, device_path))
1682
1683    def cache_commit_func():
1684      # When host_path is a not a directory, the path.join() call below would
1685      # have an '' as the second argument, causing an unwanted / to be appended.
1686      if os.path.isfile(host_path):
1687        assert len(host_checksums) == 1
1688        new_sums = {device_path: host_checksums[host_path]}
1689      else:
1690        new_sums = {posixpath.join(device_path, path[len(host_path) + 1:]): val
1691                    for path, val in host_checksums.iteritems()}
1692      cache_entry = [ignore_other_files, new_sums]
1693      self._cache['device_path_checksums'][device_path] = cache_entry
1694
1695    return (to_push, up_to_date, to_delete, cache_commit_func)
1696
1697  def _GetStaleDirectories(self, host_path, device_path):
1698    """Gets a list of stale directories on the device.
1699
1700    Args:
1701      host_path: an absolute path of a directory on the host
1702      device_path: an absolute path of a directory on the device
1703
1704    Returns:
1705      A list containing absolute paths to directories on the device that are
1706      considered stale.
1707    """
1708    def get_device_dirs(path):
1709      directories = set()
1710      command = _RECURSIVE_DIRECTORY_LIST_SCRIPT % cmd_helper.SingleQuote(path)
1711      # We use shell=True to evaluate the command as a script through the shell,
1712      # otherwise RunShellCommand tries to interpret it as the name of a (non
1713      # existent) command to run.
1714      for line in self.RunShellCommand(
1715          command, shell=True, check_return=True):
1716        directories.add(posixpath.relpath(posixpath.normpath(line), path))
1717      return directories
1718
1719    def get_host_dirs(path):
1720      directories = set()
1721      if not os.path.isdir(path):
1722        return directories
1723      for root, _, _ in os.walk(path):
1724        if root != path:
1725          # Strip off the top level directory so we can compare the device and
1726          # host.
1727          directories.add(
1728              os.path.relpath(root, path).replace(os.sep, posixpath.sep))
1729      return directories
1730
1731    host_dirs = get_host_dirs(host_path)
1732    device_dirs = get_device_dirs(device_path)
1733    stale_dirs = device_dirs - host_dirs
1734    return [posixpath.join(device_path, d) for d in stale_dirs]
1735
1736  def _ComputeDeviceChecksumsForApks(self, package_name):
1737    ret = self._cache['package_apk_checksums'].get(package_name)
1738    if ret is None:
1739      device_paths = self._GetApplicationPathsInternal(package_name)
1740      file_to_checksums = md5sum.CalculateDeviceMd5Sums(device_paths, self)
1741      ret = set(file_to_checksums.values())
1742      self._cache['package_apk_checksums'][package_name] = ret
1743    return ret
1744
1745  def _ComputeStaleApks(self, package_name, host_apk_paths):
1746    def calculate_host_checksums():
1747      return md5sum.CalculateHostMd5Sums(host_apk_paths)
1748
1749    def calculate_device_checksums():
1750      return self._ComputeDeviceChecksumsForApks(package_name)
1751
1752    host_checksums, device_checksums = reraiser_thread.RunAsync((
1753        calculate_host_checksums, calculate_device_checksums))
1754    stale_apks = [k for (k, v) in host_checksums.iteritems()
1755                  if v not in device_checksums]
1756    return stale_apks, set(host_checksums.values())
1757
1758  def _PushFilesImpl(self, host_device_tuples, files):
1759    if not files:
1760      return
1761
1762    size = sum(host_utils.GetRecursiveDiskUsage(h) for h, _ in files)
1763    file_count = len(files)
1764    dir_size = sum(host_utils.GetRecursiveDiskUsage(h)
1765                   for h, _ in host_device_tuples)
1766    dir_file_count = 0
1767    for h, _ in host_device_tuples:
1768      if os.path.isdir(h):
1769        dir_file_count += sum(len(f) for _r, _d, f in os.walk(h))
1770      else:
1771        dir_file_count += 1
1772
1773    push_duration = self._ApproximateDuration(
1774        file_count, file_count, size, False)
1775    dir_push_duration = self._ApproximateDuration(
1776        len(host_device_tuples), dir_file_count, dir_size, False)
1777    zip_duration = self._ApproximateDuration(1, 1, size, True)
1778
1779    if (dir_push_duration < push_duration and dir_push_duration < zip_duration
1780        # TODO(jbudorick): Resume directory pushing once clients have switched
1781        # to 1.0.36-compatible syntax.
1782        and False):
1783      self._PushChangedFilesIndividually(host_device_tuples)
1784    elif push_duration < zip_duration:
1785      self._PushChangedFilesIndividually(files)
1786    elif self._commands_installed is False:
1787      # Already tried and failed to install unzip command.
1788      self._PushChangedFilesIndividually(files)
1789    elif not self._PushChangedFilesZipped(
1790        files, [d for _, d in host_device_tuples]):
1791      self._PushChangedFilesIndividually(files)
1792
1793  def _MaybeInstallCommands(self):
1794    if self._commands_installed is None:
1795      try:
1796        if not install_commands.Installed(self):
1797          install_commands.InstallCommands(self)
1798        self._commands_installed = True
1799      except device_errors.CommandFailedError as e:
1800        logger.warning('unzip not available: %s', str(e))
1801        self._commands_installed = False
1802    return self._commands_installed
1803
1804  @staticmethod
1805  def _ApproximateDuration(adb_calls, file_count, byte_count, is_zipping):
1806    # We approximate the time to push a set of files to a device as:
1807    #   t = c1 * a + c2 * f + c3 + b / c4 + b / (c5 * c6), where
1808    #     t: total time (sec)
1809    #     c1: adb call time delay (sec)
1810    #     a: number of times adb is called (unitless)
1811    #     c2: push time delay (sec)
1812    #     f: number of files pushed via adb (unitless)
1813    #     c3: zip time delay (sec)
1814    #     c4: zip rate (bytes/sec)
1815    #     b: total number of bytes (bytes)
1816    #     c5: transfer rate (bytes/sec)
1817    #     c6: compression ratio (unitless)
1818
1819    # All of these are approximations.
1820    ADB_CALL_PENALTY = 0.1  # seconds
1821    ADB_PUSH_PENALTY = 0.01  # seconds
1822    ZIP_PENALTY = 2.0  # seconds
1823    ZIP_RATE = 10000000.0  # bytes / second
1824    TRANSFER_RATE = 2000000.0  # bytes / second
1825    COMPRESSION_RATIO = 2.0  # unitless
1826
1827    adb_call_time = ADB_CALL_PENALTY * adb_calls
1828    adb_push_setup_time = ADB_PUSH_PENALTY * file_count
1829    if is_zipping:
1830      zip_time = ZIP_PENALTY + byte_count / ZIP_RATE
1831      transfer_time = byte_count / (TRANSFER_RATE * COMPRESSION_RATIO)
1832    else:
1833      zip_time = 0
1834      transfer_time = byte_count / TRANSFER_RATE
1835    return adb_call_time + adb_push_setup_time + zip_time + transfer_time
1836
1837  def _PushChangedFilesIndividually(self, files):
1838    for h, d in files:
1839      self.adb.Push(h, d)
1840
1841  def _PushChangedFilesZipped(self, files, dirs):
1842    if not self._MaybeInstallCommands():
1843      return False
1844
1845    with tempfile_ext.NamedTemporaryDirectory() as working_dir:
1846      zip_path = os.path.join(working_dir, 'tmp.zip')
1847      try:
1848        zip_utils.WriteZipFile(zip_path, files)
1849      except zip_utils.ZipFailedError:
1850        return False
1851
1852      logger.info('Pushing %d files via .zip of size %d', len(files),
1853                  os.path.getsize(zip_path))
1854      self.NeedsSU()
1855      with device_temp_file.DeviceTempFile(
1856          self.adb, suffix='.zip') as device_temp:
1857        self.adb.Push(zip_path, device_temp.name)
1858
1859        quoted_dirs = ' '.join(cmd_helper.SingleQuote(d) for d in dirs)
1860        self.RunShellCommand(
1861            'unzip %s&&chmod -R 777 %s' % (device_temp.name, quoted_dirs),
1862            shell=True, as_root=True,
1863            env={'PATH': '%s:$PATH' % install_commands.BIN_DIR},
1864            check_return=True)
1865
1866    return True
1867
1868  # TODO(nednguyen): remove this and migrate the callsite to PathExists().
1869  @decorators.WithTimeoutAndRetriesFromInstance()
1870  def FileExists(self, device_path, timeout=None, retries=None):
1871    """Checks whether the given file exists on the device.
1872
1873    Arguments are the same as PathExists.
1874    """
1875    return self.PathExists(device_path, timeout=timeout, retries=retries)
1876
1877  @decorators.WithTimeoutAndRetriesFromInstance()
1878  def PathExists(self, device_paths, as_root=False, timeout=None, retries=None):
1879    """Checks whether the given path(s) exists on the device.
1880
1881    Args:
1882      device_path: A string containing the absolute path to the file on the
1883                   device, or an iterable of paths to check.
1884      as_root: Whether root permissions should be use to check for the existence
1885               of the given path(s).
1886      timeout: timeout in seconds
1887      retries: number of retries
1888
1889    Returns:
1890      True if the all given paths exist on the device, False otherwise.
1891
1892    Raises:
1893      CommandTimeoutError on timeout.
1894      DeviceUnreachableError on missing device.
1895    """
1896    paths = device_paths
1897    if isinstance(paths, basestring):
1898      paths = (paths,)
1899    if not paths:
1900      return True
1901    cmd = ['test', '-e', paths[0]]
1902    for p in paths[1:]:
1903      cmd.extend(['-a', '-e', p])
1904    try:
1905      self.RunShellCommand(cmd, as_root=as_root, check_return=True,
1906                           timeout=timeout, retries=retries)
1907      return True
1908    except device_errors.CommandFailedError:
1909      return False
1910
1911  @decorators.WithTimeoutAndRetriesFromInstance()
1912  def RemovePath(self, device_path, force=False, recursive=False,
1913                 as_root=False, rename=False, timeout=None, retries=None):
1914    """Removes the given path(s) from the device.
1915
1916    Args:
1917      device_path: A string containing the absolute path to the file on the
1918                   device, or an iterable of paths to check.
1919      force: Whether to remove the path(s) with force (-f).
1920      recursive: Whether to remove any directories in the path(s) recursively.
1921      as_root: Whether root permissions should be use to remove the given
1922               path(s).
1923      rename: Whether to rename the path(s) before removing to help avoid
1924            filesystem errors. See https://stackoverflow.com/questions/11539657
1925      timeout: timeout in seconds
1926      retries: number of retries
1927    """
1928    def _RenamePath(path):
1929      random_suffix = hex(random.randint(2 ** 12, 2 ** 16 - 1))[2:]
1930      dest = '%s-%s' % (path, random_suffix)
1931      try:
1932        self.RunShellCommand(
1933            ['mv', path, dest], as_root=as_root, check_return=True)
1934        return dest
1935      except device_errors.AdbShellCommandFailedError:
1936        # If it couldn't be moved, just try rm'ing the original path instead.
1937        return path
1938    args = ['rm']
1939    if force:
1940      args.append('-f')
1941    if recursive:
1942      args.append('-r')
1943    if isinstance(device_path, basestring):
1944      args.append(device_path if not rename else _RenamePath(device_path))
1945    else:
1946      args.extend(
1947          device_path if not rename else [_RenamePath(p) for p in device_path])
1948    self.RunShellCommand(args, as_root=as_root, check_return=True)
1949
1950  @contextlib.contextmanager
1951  def _CopyToReadableLocation(self, device_path):
1952    """Context manager to copy a file to a globally readable temp file.
1953
1954    This uses root permission to copy a file to a globally readable named
1955    temporary file. The temp file is removed when this contextmanager is closed.
1956
1957    Args:
1958      device_path: A string containing the absolute path of the file (on the
1959        device) to copy.
1960    Yields:
1961      The globally readable file object.
1962    """
1963    with device_temp_file.DeviceTempFile(self.adb) as device_temp:
1964      cmd = 'SRC=%s DEST=%s;cp "$SRC" "$DEST" && chmod 666 "$DEST"' % (
1965          cmd_helper.SingleQuote(device_path),
1966          cmd_helper.SingleQuote(device_temp.name))
1967      self.RunShellCommand(cmd, shell=True, as_root=True, check_return=True)
1968      yield device_temp
1969
1970  @decorators.WithTimeoutAndRetriesFromInstance()
1971  def PullFile(self, device_path, host_path, as_root=False, timeout=None,
1972               retries=None):
1973    """Pull a file from the device.
1974
1975    Args:
1976      device_path: A string containing the absolute path of the file to pull
1977                   from the device.
1978      host_path: A string containing the absolute path of the destination on
1979                 the host.
1980      as_root: Whether root permissions should be used to pull the file.
1981      timeout: timeout in seconds
1982      retries: number of retries
1983
1984    Raises:
1985      CommandFailedError on failure.
1986      CommandTimeoutError on timeout.
1987    """
1988    # Create the base dir if it doesn't exist already
1989    dirname = os.path.dirname(host_path)
1990    if dirname and not os.path.exists(dirname):
1991      os.makedirs(dirname)
1992    if as_root and self.NeedsSU():
1993      if not self.PathExists(device_path, as_root=True):
1994        raise device_errors.CommandFailedError(
1995            '%r: No such file or directory' % device_path, str(self))
1996      with self._CopyToReadableLocation(device_path) as readable_temp_file:
1997        self.adb.Pull(readable_temp_file.name, host_path)
1998    else:
1999      self.adb.Pull(device_path, host_path)
2000
2001  def _ReadFileWithPull(self, device_path):
2002    try:
2003      d = tempfile.mkdtemp()
2004      host_temp_path = os.path.join(d, 'tmp_ReadFileWithPull')
2005      self.adb.Pull(device_path, host_temp_path)
2006      with open(host_temp_path, 'r') as host_temp:
2007        return host_temp.read()
2008    finally:
2009      if os.path.exists(d):
2010        shutil.rmtree(d)
2011
2012  @decorators.WithTimeoutAndRetriesFromInstance()
2013  def ReadFile(self, device_path, as_root=False, force_pull=False,
2014               timeout=None, retries=None):
2015    """Reads the contents of a file from the device.
2016
2017    Args:
2018      device_path: A string containing the absolute path of the file to read
2019                   from the device.
2020      as_root: A boolean indicating whether the read should be executed with
2021               root privileges.
2022      force_pull: A boolean indicating whether to force the operation to be
2023          performed by pulling a file from the device. The default is, when the
2024          contents are short, to retrieve the contents using cat instead.
2025      timeout: timeout in seconds
2026      retries: number of retries
2027
2028    Returns:
2029      The contents of |device_path| as a string. Contents are intepreted using
2030      universal newlines, so the caller will see them encoded as '\n'. Also,
2031      all lines will be terminated.
2032
2033    Raises:
2034      AdbCommandFailedError if the file can't be read.
2035      CommandTimeoutError on timeout.
2036      DeviceUnreachableError on missing device.
2037    """
2038    def get_size(path):
2039      return self.FileSize(path, as_root=as_root)
2040
2041    if (not force_pull
2042        and 0 < get_size(device_path) <= self._MAX_ADB_OUTPUT_LENGTH):
2043      return _JoinLines(self.RunShellCommand(
2044          ['cat', device_path], as_root=as_root, check_return=True))
2045    elif as_root and self.NeedsSU():
2046      with self._CopyToReadableLocation(device_path) as readable_temp_file:
2047        return self._ReadFileWithPull(readable_temp_file.name)
2048    else:
2049      return self._ReadFileWithPull(device_path)
2050
2051  def _WriteFileWithPush(self, device_path, contents):
2052    with tempfile.NamedTemporaryFile() as host_temp:
2053      host_temp.write(contents)
2054      host_temp.flush()
2055      self.adb.Push(host_temp.name, device_path)
2056
2057  @decorators.WithTimeoutAndRetriesFromInstance()
2058  def WriteFile(self, device_path, contents, as_root=False, force_push=False,
2059                timeout=None, retries=None):
2060    """Writes |contents| to a file on the device.
2061
2062    Args:
2063      device_path: A string containing the absolute path to the file to write
2064          on the device.
2065      contents: A string containing the data to write to the device.
2066      as_root: A boolean indicating whether the write should be executed with
2067          root privileges (if available).
2068      force_push: A boolean indicating whether to force the operation to be
2069          performed by pushing a file to the device. The default is, when the
2070          contents are short, to pass the contents using a shell script instead.
2071      timeout: timeout in seconds
2072      retries: number of retries
2073
2074    Raises:
2075      CommandFailedError if the file could not be written on the device.
2076      CommandTimeoutError on timeout.
2077      DeviceUnreachableError on missing device.
2078    """
2079    if not force_push and len(contents) < self._MAX_ADB_COMMAND_LENGTH:
2080      # If the contents are small, for efficieny we write the contents with
2081      # a shell command rather than pushing a file.
2082      cmd = 'echo -n %s > %s' % (cmd_helper.SingleQuote(contents),
2083                                 cmd_helper.SingleQuote(device_path))
2084      self.RunShellCommand(cmd, shell=True, as_root=as_root, check_return=True)
2085    elif as_root and self.NeedsSU():
2086      # Adb does not allow to "push with su", so we first push to a temp file
2087      # on a safe location, and then copy it to the desired location with su.
2088      with device_temp_file.DeviceTempFile(self.adb) as device_temp:
2089        self._WriteFileWithPush(device_temp.name, contents)
2090        # Here we need 'cp' rather than 'mv' because the temp and
2091        # destination files might be on different file systems (e.g.
2092        # on internal storage and an external sd card).
2093        self.RunShellCommand(['cp', device_temp.name, device_path],
2094                             as_root=True, check_return=True)
2095    else:
2096      # If root is not needed, we can push directly to the desired location.
2097      self._WriteFileWithPush(device_path, contents)
2098
2099  def _ParseLongLsOutput(self, device_path, as_root=False, **kwargs):
2100    """Run and scrape the output of 'ls -a -l' on a device directory."""
2101    device_path = posixpath.join(device_path, '')  # Force trailing '/'.
2102    output = self.RunShellCommand(
2103        ['ls', '-a', '-l', device_path], as_root=as_root,
2104        check_return=True, env={'TZ': 'utc'}, **kwargs)
2105    if output and output[0].startswith('total '):
2106      output.pop(0) # pylint: disable=maybe-no-member
2107
2108    entries = []
2109    for line in output:
2110      m = _LONG_LS_OUTPUT_RE.match(line)
2111      if m:
2112        if m.group('filename') not in ['.', '..']:
2113          item = m.groupdict()
2114          # A change in toybox is causing recent Android versions to escape
2115          # spaces in file names. Here we just unquote those spaces. If we
2116          # later find more essoteric characters in file names, a more careful
2117          # unquoting mechanism may be needed. But hopefully not.
2118          # See: https://goo.gl/JAebZj
2119          item['filename'] = item['filename'].replace('\\ ', ' ')
2120          entries.append(item)
2121      else:
2122        logger.info('Skipping: %s', line)
2123
2124    return entries
2125
2126  def ListDirectory(self, device_path, as_root=False, **kwargs):
2127    """List all files on a device directory.
2128
2129    Mirroring os.listdir (and most client expectations) the resulting list
2130    does not include the special entries '.' and '..' even if they are present
2131    in the directory.
2132
2133    Args:
2134      device_path: A string containing the path of the directory on the device
2135                   to list.
2136      as_root: A boolean indicating whether the to use root privileges to list
2137               the directory contents.
2138      timeout: timeout in seconds
2139      retries: number of retries
2140
2141    Returns:
2142      A list of filenames for all entries contained in the directory.
2143
2144    Raises:
2145      AdbCommandFailedError if |device_path| does not specify a valid and
2146          accessible directory in the device.
2147      CommandTimeoutError on timeout.
2148      DeviceUnreachableError on missing device.
2149    """
2150    entries = self._ParseLongLsOutput(device_path, as_root=as_root, **kwargs)
2151    return [d['filename'] for d in entries]
2152
2153  def StatDirectory(self, device_path, as_root=False, **kwargs):
2154    """List file and stat info for all entries on a device directory.
2155
2156    Implementation notes: this is currently implemented by parsing the output
2157    of 'ls -a -l' on the device. Whether possible and convenient, we attempt to
2158    make parsing strict and return values mirroring those of the standard |os|
2159    and |stat| Python modules.
2160
2161    Mirroring os.listdir (and most client expectations) the resulting list
2162    does not include the special entries '.' and '..' even if they are present
2163    in the directory.
2164
2165    Args:
2166      device_path: A string containing the path of the directory on the device
2167                   to list.
2168      as_root: A boolean indicating whether the to use root privileges to list
2169               the directory contents.
2170      timeout: timeout in seconds
2171      retries: number of retries
2172
2173    Returns:
2174      A list of dictionaries, each containing the following keys:
2175        filename: A string with the file name.
2176        st_mode: File permissions, use the stat module to interpret these.
2177        st_nlink: Number of hard links (may be missing).
2178        st_owner: A string with the user name of the owner.
2179        st_group: A string with the group name of the owner.
2180        st_rdev_pair: Device type as (major, minior) (only if inode device).
2181        st_size: Size of file, in bytes (may be missing for non-regular files).
2182        st_mtime: Time of most recent modification, in seconds since epoch
2183          (although resolution is in minutes).
2184        symbolic_link_to: If entry is a symbolic link, path where it points to;
2185          missing otherwise.
2186
2187    Raises:
2188      AdbCommandFailedError if |device_path| does not specify a valid and
2189          accessible directory in the device.
2190      CommandTimeoutError on timeout.
2191      DeviceUnreachableError on missing device.
2192    """
2193    entries = self._ParseLongLsOutput(device_path, as_root=as_root, **kwargs)
2194    for d in entries:
2195      for key, value in d.items():
2196        if value is None:
2197          del d[key]  # Remove missing fields.
2198      d['st_mode'] = _ParseModeString(d['st_mode'])
2199      d['st_mtime'] = calendar.timegm(
2200          time.strptime(d['st_mtime'], _LS_DATE_FORMAT))
2201      for key in ['st_nlink', 'st_size', 'st_rdev_major', 'st_rdev_minor']:
2202        if key in d:
2203          d[key] = int(d[key])
2204      if 'st_rdev_major' in d and 'st_rdev_minor' in d:
2205        d['st_rdev_pair'] = (d.pop('st_rdev_major'), d.pop('st_rdev_minor'))
2206    return entries
2207
2208  def StatPath(self, device_path, as_root=False, **kwargs):
2209    """Get the stat attributes of a file or directory on the device.
2210
2211    Args:
2212      device_path: A string containing the path of a file or directory from
2213                   which to get attributes.
2214      as_root: A boolean indicating whether the to use root privileges to
2215               access the file information.
2216      timeout: timeout in seconds
2217      retries: number of retries
2218
2219    Returns:
2220      A dictionary with the stat info collected; see StatDirectory for details.
2221
2222    Raises:
2223      CommandFailedError if device_path cannot be found on the device.
2224      CommandTimeoutError on timeout.
2225      DeviceUnreachableError on missing device.
2226    """
2227    dirname, filename = posixpath.split(posixpath.normpath(device_path))
2228    for entry in self.StatDirectory(dirname, as_root=as_root, **kwargs):
2229      if entry['filename'] == filename:
2230        return entry
2231    raise device_errors.CommandFailedError(
2232        'Cannot find file or directory: %r' % device_path, str(self))
2233
2234  def FileSize(self, device_path, as_root=False, **kwargs):
2235    """Get the size of a file on the device.
2236
2237    Note: This is implemented by parsing the output of the 'ls' command on
2238    the device. On some Android versions, when passing a directory or special
2239    file, the size is *not* reported and this function will throw an exception.
2240
2241    Args:
2242      device_path: A string containing the path of a file on the device.
2243      as_root: A boolean indicating whether the to use root privileges to
2244               access the file information.
2245      timeout: timeout in seconds
2246      retries: number of retries
2247
2248    Returns:
2249      The size of the file in bytes.
2250
2251    Raises:
2252      CommandFailedError if device_path cannot be found on the device, or
2253        its size cannot be determited for some reason.
2254      CommandTimeoutError on timeout.
2255      DeviceUnreachableError on missing device.
2256    """
2257    entry = self.StatPath(device_path, as_root=as_root, **kwargs)
2258    try:
2259      return entry['st_size']
2260    except KeyError:
2261      raise device_errors.CommandFailedError(
2262          'Could not determine the size of: %s' % device_path, str(self))
2263
2264  @decorators.WithTimeoutAndRetriesFromInstance()
2265  def SetJavaAsserts(self, enabled, timeout=None, retries=None):
2266    """Enables or disables Java asserts.
2267
2268    Args:
2269      enabled: A boolean indicating whether Java asserts should be enabled
2270               or disabled.
2271      timeout: timeout in seconds
2272      retries: number of retries
2273
2274    Returns:
2275      True if the device-side property changed and a restart is required as a
2276      result, False otherwise.
2277
2278    Raises:
2279      CommandTimeoutError on timeout.
2280    """
2281    def find_property(lines, property_name):
2282      for index, line in enumerate(lines):
2283        if line.strip() == '':
2284          continue
2285        key_value = tuple(s.strip() for s in line.split('=', 1))
2286        if len(key_value) != 2:
2287          continue
2288        key, value = key_value
2289        if key == property_name:
2290          return index, value
2291      return None, ''
2292
2293    new_value = 'all' if enabled else ''
2294
2295    # First ensure the desired property is persisted.
2296    try:
2297      properties = self.ReadFile(self.LOCAL_PROPERTIES_PATH).splitlines()
2298    except device_errors.CommandFailedError:
2299      properties = []
2300    index, value = find_property(properties, self.JAVA_ASSERT_PROPERTY)
2301    if new_value != value:
2302      if new_value:
2303        new_line = '%s=%s' % (self.JAVA_ASSERT_PROPERTY, new_value)
2304        if index is None:
2305          properties.append(new_line)
2306        else:
2307          properties[index] = new_line
2308      else:
2309        assert index is not None  # since new_value == '' and new_value != value
2310        properties.pop(index)
2311      self.WriteFile(self.LOCAL_PROPERTIES_PATH, _JoinLines(properties))
2312
2313    # Next, check the current runtime value is what we need, and
2314    # if not, set it and report that a reboot is required.
2315    value = self.GetProp(self.JAVA_ASSERT_PROPERTY)
2316    if new_value != value:
2317      self.SetProp(self.JAVA_ASSERT_PROPERTY, new_value)
2318      return True
2319    else:
2320      return False
2321
2322  def GetLocale(self, cache=False):
2323    """Returns the locale setting on the device.
2324
2325    Args:
2326      cache: Whether to use cached properties when available.
2327    Returns:
2328      A pair (language, country).
2329    """
2330    locale = self.GetProp('persist.sys.locale', cache=cache)
2331    if locale:
2332      if '-' not in locale:
2333        logging.error('Unparsable locale: %s', locale)
2334        return ('', '')  # Behave as if persist.sys.locale is undefined.
2335      return tuple(locale.split('-', 1))
2336    return (self.GetProp('persist.sys.language', cache=cache),
2337            self.GetProp('persist.sys.country', cache=cache))
2338
2339  def GetLanguage(self, cache=False):
2340    """Returns the language setting on the device.
2341
2342    DEPRECATED: Prefer GetLocale() instead.
2343
2344    Args:
2345      cache: Whether to use cached properties when available.
2346    """
2347    return self.GetLocale(cache=cache)[0]
2348
2349  def GetCountry(self, cache=False):
2350    """Returns the country setting on the device.
2351
2352    DEPRECATED: Prefer GetLocale() instead.
2353
2354    Args:
2355      cache: Whether to use cached properties when available.
2356    """
2357    return self.GetLocale(cache=cache)[1]
2358
2359  @property
2360  def screen_density(self):
2361    """Returns the screen density of the device."""
2362    DPI_TO_DENSITY = {
2363      120: 'ldpi',
2364      160: 'mdpi',
2365      240: 'hdpi',
2366      320: 'xhdpi',
2367      480: 'xxhdpi',
2368      640: 'xxxhdpi',
2369    }
2370    return DPI_TO_DENSITY.get(self.pixel_density, 'tvdpi')
2371
2372  @property
2373  def pixel_density(self):
2374    return int(self.GetProp('ro.sf.lcd_density', cache=True))
2375
2376  @property
2377  def build_description(self):
2378    """Returns the build description of the system.
2379
2380    For example:
2381      nakasi-user 4.4.4 KTU84P 1227136 release-keys
2382    """
2383    return self.GetProp('ro.build.description', cache=True)
2384
2385  @property
2386  def build_fingerprint(self):
2387    """Returns the build fingerprint of the system.
2388
2389    For example:
2390      google/nakasi/grouper:4.4.4/KTU84P/1227136:user/release-keys
2391    """
2392    return self.GetProp('ro.build.fingerprint', cache=True)
2393
2394  @property
2395  def build_id(self):
2396    """Returns the build ID of the system (e.g. 'KTU84P')."""
2397    return self.GetProp('ro.build.id', cache=True)
2398
2399  @property
2400  def build_product(self):
2401    """Returns the build product of the system (e.g. 'grouper')."""
2402    return self.GetProp('ro.build.product', cache=True)
2403
2404  @property
2405  def build_type(self):
2406    """Returns the build type of the system (e.g. 'user')."""
2407    return self.GetProp('ro.build.type', cache=True)
2408
2409  @property
2410  def build_version_sdk(self):
2411    """Returns the build version sdk of the system as a number (e.g. 19).
2412
2413    For version code numbers see:
2414    http://developer.android.com/reference/android/os/Build.VERSION_CODES.html
2415
2416    For named constants see devil.android.sdk.version_codes
2417
2418    Raises:
2419      CommandFailedError if the build version sdk is not a number.
2420    """
2421    value = self.GetProp('ro.build.version.sdk', cache=True)
2422    try:
2423      return int(value)
2424    except ValueError:
2425      raise device_errors.CommandFailedError(
2426          'Invalid build version sdk: %r' % value)
2427
2428  @property
2429  def product_cpu_abi(self):
2430    """Returns the product cpu abi of the device (e.g. 'armeabi-v7a').
2431
2432    For supported ABIs, the return value will be one of the values defined in
2433    devil.android.ndk.abis.
2434    """
2435    return self.GetProp('ro.product.cpu.abi', cache=True)
2436
2437  @property
2438  def product_model(self):
2439    """Returns the name of the product model (e.g. 'Nexus 7')."""
2440    return self.GetProp('ro.product.model', cache=True)
2441
2442  @property
2443  def product_name(self):
2444    """Returns the product name of the device (e.g. 'nakasi')."""
2445    return self.GetProp('ro.product.name', cache=True)
2446
2447  @property
2448  def product_board(self):
2449    """Returns the product board name of the device (e.g. 'shamu')."""
2450    return self.GetProp('ro.product.board', cache=True)
2451
2452  def _EnsureCacheInitialized(self):
2453    """Populates cache token, runs getprop and fetches $EXTERNAL_STORAGE."""
2454    if self._cache['token']:
2455      return
2456    with self._cache_lock:
2457      if self._cache['token']:
2458        return
2459      # Change the token every time to ensure that it will match only the
2460      # previously dumped cache.
2461      token = str(uuid.uuid1())
2462      cmd = (
2463          'c=/data/local/tmp/cache_token;'
2464          'echo $EXTERNAL_STORAGE;'
2465          'cat $c 2>/dev/null||echo;'
2466          'echo "%s">$c &&' % token +
2467          'getprop'
2468      )
2469      output = self.RunShellCommand(
2470          cmd, shell=True, check_return=True, large_output=True)
2471      # Error-checking for this existing is done in GetExternalStoragePath().
2472      self._cache['external_storage'] = output[0]
2473      self._cache['prev_token'] = output[1]
2474      output = output[2:]
2475
2476      prop_cache = self._cache['getprop']
2477      prop_cache.clear()
2478      for key, value in _GETPROP_RE.findall(''.join(output)):
2479        prop_cache[key] = value
2480      self._cache['token'] = token
2481
2482  @decorators.WithTimeoutAndRetriesFromInstance()
2483  def GetProp(self, property_name, cache=False, timeout=None, retries=None):
2484    """Gets a property from the device.
2485
2486    Args:
2487      property_name: A string containing the name of the property to get from
2488                     the device.
2489      cache: Whether to use cached properties when available.
2490      timeout: timeout in seconds
2491      retries: number of retries
2492
2493    Returns:
2494      The value of the device's |property_name| property.
2495
2496    Raises:
2497      CommandTimeoutError on timeout.
2498    """
2499    assert isinstance(property_name, basestring), (
2500        "property_name is not a string: %r" % property_name)
2501
2502    if cache:
2503      # It takes ~120ms to query a single property, and ~130ms to query all
2504      # properties. So, when caching we always query all properties.
2505      self._EnsureCacheInitialized()
2506    else:
2507      # timeout and retries are handled down at run shell, because we don't
2508      # want to apply them in the other branch when reading from the cache
2509      value = self.RunShellCommand(
2510          ['getprop', property_name], single_line=True, check_return=True,
2511          timeout=timeout, retries=retries)
2512      self._cache['getprop'][property_name] = value
2513    # Non-existent properties are treated as empty strings by getprop.
2514    return self._cache['getprop'].get(property_name, '')
2515
2516  @decorators.WithTimeoutAndRetriesFromInstance()
2517  def SetProp(self, property_name, value, check=False, timeout=None,
2518              retries=None):
2519    """Sets a property on the device.
2520
2521    Args:
2522      property_name: A string containing the name of the property to set on
2523                     the device.
2524      value: A string containing the value to set to the property on the
2525             device.
2526      check: A boolean indicating whether to check that the property was
2527             successfully set on the device.
2528      timeout: timeout in seconds
2529      retries: number of retries
2530
2531    Raises:
2532      CommandFailedError if check is true and the property was not correctly
2533        set on the device (e.g. because it is not rooted).
2534      CommandTimeoutError on timeout.
2535    """
2536    assert isinstance(property_name, basestring), (
2537        "property_name is not a string: %r" % property_name)
2538    assert isinstance(value, basestring), "value is not a string: %r" % value
2539
2540    self.RunShellCommand(['setprop', property_name, value], check_return=True)
2541    prop_cache = self._cache['getprop']
2542    if property_name in prop_cache:
2543      del prop_cache[property_name]
2544    # TODO(perezju) remove the option and make the check mandatory, but using a
2545    # single shell script to both set- and getprop.
2546    if check and value != self.GetProp(property_name, cache=False):
2547      raise device_errors.CommandFailedError(
2548          'Unable to set property %r on the device to %r'
2549          % (property_name, value), str(self))
2550
2551  @decorators.WithTimeoutAndRetriesFromInstance()
2552  def GetABI(self, timeout=None, retries=None):
2553    """Gets the device main ABI.
2554
2555    Args:
2556      timeout: timeout in seconds
2557      retries: number of retries
2558
2559    Returns:
2560      The device's main ABI name. For supported ABIs, the return value will be
2561      one of the values defined in devil.android.ndk.abis.
2562
2563    Raises:
2564      CommandTimeoutError on timeout.
2565    """
2566    return self.GetProp('ro.product.cpu.abi', cache=True)
2567
2568  def _GetPsOutput(self, pattern):
2569    """Runs |ps| command on the device and returns its output,
2570
2571    This private method abstracts away differences between Android verions for
2572    calling |ps|, and implements support for filtering the output by a given
2573    |pattern|, but does not do any output parsing.
2574    """
2575    try:
2576      ps_cmd = 'ps'
2577      # ps behavior was changed in Android O and above, http://crbug.com/686716
2578      if self.build_version_sdk >= version_codes.OREO:
2579        ps_cmd = 'ps -e'
2580      if pattern:
2581        return self._RunPipedShellCommand(
2582            '%s | grep -F %s' % (ps_cmd, cmd_helper.SingleQuote(pattern)))
2583      else:
2584        return self.RunShellCommand(
2585            ps_cmd.split(), check_return=True, large_output=True)
2586    except device_errors.AdbShellCommandFailedError as e:
2587      if e.status and isinstance(e.status, list) and not e.status[0]:
2588        # If ps succeeded but grep failed, there were no processes with the
2589        # given name.
2590        return []
2591      else:
2592        raise
2593
2594  @decorators.WithTimeoutAndRetriesFromInstance()
2595  def ListProcesses(self, process_name=None, timeout=None, retries=None):
2596    """Returns a list of tuples with info about processes on the device.
2597
2598    This essentially parses the output of the |ps| command into convenient
2599    ProcessInfo tuples.
2600
2601    Args:
2602      process_name: A string used to filter the returned processes. If given,
2603                    only processes whose name have this value as a substring
2604                    will be returned.
2605      timeout: timeout in seconds
2606      retries: number of retries
2607
2608    Returns:
2609      A list of ProcessInfo tuples with |name|, |pid|, and |ppid| fields.
2610    """
2611    process_name = process_name or ''
2612    processes = []
2613    for line in self._GetPsOutput(process_name):
2614      row = line.split()
2615      try:
2616        row = {k: row[i] for k, i in _PS_COLUMNS.iteritems()}
2617        if row['pid'] == 'PID' or process_name not in row['name']:
2618          # Skip over header and non-matching processes.
2619          continue
2620        row['pid'] = int(row['pid'])
2621        row['ppid'] = int(row['ppid'])
2622      except StandardError:  # e.g. IndexError, TypeError, ValueError.
2623        logging.warning('failed to parse ps line: %r', line)
2624        continue
2625      processes.append(ProcessInfo(**row))
2626    return processes
2627
2628  def _GetDumpsysOutput(self, extra_args, pattern=None):
2629    """Runs |dumpsys| command on the device and returns its output.
2630
2631    This private method implements support for filtering the output by a given
2632    |pattern|, but does not do any output parsing.
2633    """
2634    try:
2635      cmd = ['dumpsys'] + extra_args
2636      if pattern:
2637        cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd)
2638        return self._RunPipedShellCommand(
2639            '%s | grep -F %s' % (cmd, cmd_helper.SingleQuote(pattern)))
2640      else:
2641        cmd = ['dumpsys'] + extra_args
2642        return self.RunShellCommand(cmd, check_return=True, large_output=True)
2643    except device_errors.AdbShellCommandFailedError as e:
2644      if e.status and isinstance(e.status, list) and not e.status[0]:
2645        # If dumpsys succeeded but grep failed, there were no lines matching
2646        # the given pattern.
2647        return []
2648      else:
2649        raise
2650
2651  # TODO(#4103): Remove after migrating clients to ListProcesses.
2652  @decorators.WithTimeoutAndRetriesFromInstance()
2653  def GetPids(self, process_name=None, timeout=None, retries=None):
2654    """Returns the PIDs of processes containing the given name as substring.
2655
2656    DEPRECATED
2657
2658    Note that the |process_name| is often the package name.
2659
2660    Args:
2661      process_name: A string containing the process name to get the PIDs for.
2662                    If missing returns PIDs for all processes.
2663      timeout: timeout in seconds
2664      retries: number of retries
2665
2666    Returns:
2667      A dict mapping process name to a list of PIDs for each process that
2668      contained the provided |process_name|.
2669
2670    Raises:
2671      CommandTimeoutError on timeout.
2672      DeviceUnreachableError on missing device.
2673    """
2674    procs_pids = collections.defaultdict(list)
2675    for p in self.ListProcesses(process_name):
2676      procs_pids[p.name].append(str(p.pid))
2677    return procs_pids
2678
2679  @decorators.WithTimeoutAndRetriesFromInstance()
2680  def GetApplicationPids(self, process_name, at_most_one=False,
2681                         timeout=None, retries=None):
2682    """Returns the PID or PIDs of a given process name.
2683
2684    Note that the |process_name|, often the package name, must match exactly.
2685
2686    Args:
2687      process_name: A string containing the process name to get the PIDs for.
2688      at_most_one: A boolean indicating that at most one PID is expected to
2689                   be found.
2690      timeout: timeout in seconds
2691      retries: number of retries
2692
2693    Returns:
2694      A list of the PIDs for the named process. If at_most_one=True returns
2695      the single PID found or None otherwise.
2696
2697    Raises:
2698      CommandFailedError if at_most_one=True and more than one PID is found
2699          for the named process.
2700      CommandTimeoutError on timeout.
2701      DeviceUnreachableError on missing device.
2702    """
2703    pids = [p.pid for p in self.ListProcesses(process_name)
2704            if p.name == process_name]
2705    if at_most_one:
2706      if len(pids) > 1:
2707        raise device_errors.CommandFailedError(
2708            'Expected a single PID for %r but found: %r.' % (
2709                process_name, pids),
2710            device_serial=str(self))
2711      return pids[0] if pids else None
2712    else:
2713      return pids
2714
2715  @decorators.WithTimeoutAndRetriesFromInstance()
2716  def GetEnforce(self, timeout=None, retries=None):
2717    """Get the current mode of SELinux.
2718
2719    Args:
2720      timeout: timeout in seconds
2721      retries: number of retries
2722
2723    Returns:
2724      True (enforcing), False (permissive), or None (disabled).
2725
2726    Raises:
2727      CommandFailedError on failure.
2728      CommandTimeoutError on timeout.
2729      DeviceUnreachableError on missing device.
2730    """
2731    output = self.RunShellCommand(
2732        ['getenforce'], check_return=True, single_line=True).lower()
2733    if output not in _SELINUX_MODE:
2734      raise device_errors.CommandFailedError(
2735          'Unexpected getenforce output: %s' % output)
2736    return _SELINUX_MODE[output]
2737
2738  @decorators.WithTimeoutAndRetriesFromInstance()
2739  def SetEnforce(self, enabled, timeout=None, retries=None):
2740    """Modify the mode SELinux is running in.
2741
2742    Args:
2743      enabled: a boolean indicating whether to put SELinux in encorcing mode
2744               (if True), or permissive mode (otherwise).
2745      timeout: timeout in seconds
2746      retries: number of retries
2747
2748    Raises:
2749      CommandFailedError on failure.
2750      CommandTimeoutError on timeout.
2751      DeviceUnreachableError on missing device.
2752    """
2753    self.RunShellCommand(
2754        ['setenforce', '1' if int(enabled) else '0'], as_root=True,
2755        check_return=True)
2756
2757  @decorators.WithTimeoutAndRetriesFromInstance()
2758  def GetWebViewUpdateServiceDump(self, timeout=None, retries=None):
2759    """Get the WebView update command sysdump on the device.
2760
2761    Returns:
2762      A dictionary with these possible entries:
2763        FallbackLogicEnabled: True|False
2764        CurrentWebViewPackage: "package name" or None
2765        MinimumWebViewVersionCode: int
2766        WebViewPackages: Dict of installed WebView providers, mapping "package
2767            name" to "reason it's valid/invalid."
2768
2769    It may return an empty dictionary if device does not
2770    support the "dumpsys webviewupdate" command.
2771
2772    Raises:
2773      CommandFailedError on failure.
2774      CommandTimeoutError on timeout.
2775      DeviceUnreachableError on missing device.
2776    """
2777    result = {}
2778
2779    # Command was implemented starting in Oreo
2780    if self.build_version_sdk < version_codes.OREO:
2781      return result
2782
2783    output = self.RunShellCommand(
2784        ['dumpsys', 'webviewupdate'], check_return=True)
2785    webview_packages = {}
2786    for line in output:
2787      match = re.search(_WEBVIEW_SYSUPDATE_CURRENT_PKG_RE, line)
2788      if match:
2789        result['CurrentWebViewPackage'] = match.group(1)
2790      match = re.search(_WEBVIEW_SYSUPDATE_NULL_PKG_RE, line)
2791      if match:
2792        result['CurrentWebViewPackage'] = None
2793      match = re.search(_WEBVIEW_SYSUPDATE_FALLBACK_LOGIC_RE, line)
2794      if match:
2795        result['FallbackLogicEnabled'] = \
2796            True if match.group(1) == 'true' else False
2797      match = re.search(_WEBVIEW_SYSUPDATE_PACKAGE_INSTALLED_RE, line)
2798      if match:
2799        package_name = match.group(1)
2800        reason = match.group(2)
2801        webview_packages[package_name] = reason
2802      match = re.search(_WEBVIEW_SYSUPDATE_PACKAGE_NOT_INSTALLED_RE, line)
2803      if match:
2804        package_name = match.group(1)
2805        reason = match.group(2)
2806        webview_packages[package_name] = reason
2807      match = re.search(_WEBVIEW_SYSUPDATE_MIN_VERSION_CODE, line)
2808      if match:
2809        result['MinimumWebViewVersionCode'] = int(match.group(1))
2810    if webview_packages:
2811      result['WebViewPackages'] = webview_packages
2812
2813    missing_fields = set(['CurrentWebViewPackage', 'FallbackLogicEnabled']) - \
2814                     set(result.keys())
2815    if len(missing_fields) > 0:
2816      raise device_errors.CommandFailedError(
2817          '%s not found in dumpsys webviewupdate' % str(list(missing_fields)))
2818    return result
2819
2820  @decorators.WithTimeoutAndRetriesFromInstance()
2821  def SetWebViewImplementation(self, package_name, timeout=None, retries=None):
2822    """Select the WebView implementation to the specified package.
2823
2824    Args:
2825      package_name: The package name of a WebView implementation. The package
2826        must be already installed on the device.
2827      timeout: timeout in seconds
2828      retries: number of retries
2829
2830    Raises:
2831      CommandFailedError on failure.
2832      CommandTimeoutError on timeout.
2833      DeviceUnreachableError on missing device.
2834    """
2835    installed = self.GetApplicationPaths(package_name)
2836    if not installed:
2837      raise device_errors.CommandFailedError(
2838          '%s is not installed' % package_name, str(self))
2839    output = self.RunShellCommand(
2840        ['cmd', 'webviewupdate', 'set-webview-implementation', package_name],
2841        single_line=True,
2842        check_return=False)
2843    if output == 'Success':
2844      logging.info('WebView provider set to: %s', package_name)
2845    else:
2846      dumpsys_output = self.GetWebViewUpdateServiceDump()
2847      webview_packages = dumpsys_output.get('WebViewPackages')
2848      if webview_packages:
2849        reason = webview_packages.get(package_name)
2850        if not reason:
2851          all_provider_package_names = webview_packages.keys()
2852          raise device_errors.CommandFailedError(
2853              '%s is not in the system WebView provider list. Must choose one '
2854              'of %r.' % (package_name, all_provider_package_names), str(self))
2855        if re.search(r'is\s+NOT\s+installed/enabled for all users', reason):
2856          raise device_errors.CommandFailedError(
2857              '%s is disabled, make sure to disable WebView fallback logic' %
2858              package_name, str(self))
2859        if re.search(r'No WebView-library manifest flag', reason):
2860          raise device_errors.CommandFailedError(
2861              '%s does not declare a WebView native library, so it cannot '
2862              'be a WebView provider' % package_name, str(self))
2863        if re.search(r'SDK version too low', reason):
2864          raise device_errors.CommandFailedError(
2865              '%s needs a higher targetSdkVersion (must be >= %d)' %
2866              (package_name, self.build_version_sdk), str(self))
2867        if re.search(r'Version code too low', reason):
2868          raise device_errors.CommandFailedError(
2869              '%s needs a higher versionCode (must be >= %d)' %
2870              (package_name, dumpsys_output.get('MinimumWebViewVersionCode')),
2871              str(self))
2872        if re.search(r'Incorrect signature', reason):
2873          raise device_errors.CommandFailedError(
2874              '%s is not signed with release keys (but user builds require '
2875              'this for WebView providers)' % package_name, str(self))
2876      raise device_errors.CommandFailedError(
2877          'Error setting WebView provider: %s' % output, str(self))
2878
2879  @decorators.WithTimeoutAndRetriesFromInstance()
2880  def SetWebViewFallbackLogic(self, enabled, timeout=None, retries=None):
2881    """Set whether WebViewUpdateService's "fallback logic" should be enabled.
2882
2883    WebViewUpdateService has nonintuitive "fallback logic" for devices where
2884    Monochrome (Chrome Stable) is preinstalled as the WebView provider, with a
2885    "stub" (little-to-no code) implementation of standalone WebView.
2886
2887    "Fallback logic" (enabled by default) is designed, in the case where the
2888    user has disabled Chrome, to fall back to the stub standalone WebView by
2889    enabling the package. The implementation plumbs through the Chrome APK until
2890    Play Store installs an update with the full implementation.
2891
2892    A surprising side-effect of "fallback logic" is that, immediately after
2893    sideloading WebView, WebViewUpdateService re-disables the package and
2894    uninstalls the update. This can prevent successfully using standalone
2895    WebView for development, although "fallback logic" can be disabled on
2896    userdebug/eng devices.
2897
2898    Because this is only relevant for devices with the standalone WebView stub,
2899    this command is only relevant on N-P (inclusive).
2900
2901    You can determine if "fallback logic" is currently enabled by checking
2902    FallbackLogicEnabled in the dictionary returned by
2903    GetWebViewUpdateServiceDump.
2904
2905    Args:
2906      enabled: bool - True for enabled, False for disabled
2907      timeout: timeout in seconds
2908      retries: number of retries
2909
2910    Raises:
2911      CommandFailedError on failure.
2912      CommandTimeoutError on timeout.
2913      DeviceUnreachableError on missing device.
2914    """
2915
2916    # Command is only available on devices which preinstall stub WebView.
2917    if not version_codes.NOUGAT <= self.build_version_sdk <= version_codes.PIE:
2918      return
2919
2920    # redundant-packages is the opposite of fallback logic
2921    enable_string = 'disable' if enabled else 'enable'
2922    output = self.RunShellCommand(
2923        ['cmd', 'webviewupdate', '%s-redundant-packages' % enable_string],
2924        single_line=True, check_return=True)
2925    if output == 'Success':
2926      logging.info('WebView Fallback Logic is %s',
2927                   'enabled' if enabled else 'disabled')
2928    else:
2929      raise device_errors.CommandFailedError(
2930          'Error setting WebView Fallback Logic: %s' % output, str(self))
2931
2932  @decorators.WithTimeoutAndRetriesFromInstance()
2933  def TakeScreenshot(self, host_path=None, timeout=None, retries=None):
2934    """Takes a screenshot of the device.
2935
2936    Args:
2937      host_path: A string containing the path on the host to save the
2938                 screenshot to. If None, a file name in the current
2939                 directory will be generated.
2940      timeout: timeout in seconds
2941      retries: number of retries
2942
2943    Returns:
2944      The name of the file on the host to which the screenshot was saved.
2945
2946    Raises:
2947      CommandFailedError on failure.
2948      CommandTimeoutError on timeout.
2949      DeviceUnreachableError on missing device.
2950    """
2951    if not host_path:
2952      host_path = os.path.abspath('screenshot-%s-%s.png' % (
2953          self.serial, _GetTimeStamp()))
2954    with device_temp_file.DeviceTempFile(self.adb, suffix='.png') as device_tmp:
2955      self.RunShellCommand(['/system/bin/screencap', '-p', device_tmp.name],
2956                           check_return=True)
2957      self.PullFile(device_tmp.name, host_path)
2958    return host_path
2959
2960  @decorators.WithTimeoutAndRetriesFromInstance()
2961  def DismissCrashDialogIfNeeded(self, timeout=None, retries=None):
2962    """Dismiss the error/ANR dialog if present.
2963
2964    Returns: Name of the crashed package if a dialog is focused,
2965             None otherwise.
2966    """
2967    def _FindFocusedWindow():
2968      match = None
2969      # TODO(jbudorick): Try to grep the output on the device instead of using
2970      # large_output if/when DeviceUtils exposes a public interface for piped
2971      # shell command handling.
2972      for line in self.RunShellCommand(['dumpsys', 'window', 'windows'],
2973                                       check_return=True, large_output=True):
2974        match = re.match(_CURRENT_FOCUS_CRASH_RE, line)
2975        if match:
2976          break
2977      return match
2978
2979    match = _FindFocusedWindow()
2980    if not match:
2981      return None
2982    package = match.group(2)
2983    logger.warning('Trying to dismiss %s dialog for %s', *match.groups())
2984    self.SendKeyEvent(keyevent.KEYCODE_DPAD_RIGHT)
2985    self.SendKeyEvent(keyevent.KEYCODE_DPAD_RIGHT)
2986    self.SendKeyEvent(keyevent.KEYCODE_ENTER)
2987    match = _FindFocusedWindow()
2988    if match:
2989      logger.error('Still showing a %s dialog for %s', *match.groups())
2990    return package
2991
2992  def GetLogcatMonitor(self, *args, **kwargs):
2993    """Returns a new LogcatMonitor associated with this device.
2994
2995    Parameters passed to this function are passed directly to
2996    |logcat_monitor.LogcatMonitor| and are documented there.
2997    """
2998    return logcat_monitor.LogcatMonitor(self.adb, *args, **kwargs)
2999
3000  def GetClientCache(self, client_name):
3001    """Returns client cache."""
3002    if client_name not in self._client_caches:
3003      self._client_caches[client_name] = {}
3004    return self._client_caches[client_name]
3005
3006  def ClearCache(self):
3007    """Clears all caches."""
3008    for client in self._client_caches:
3009      self._client_caches[client].clear()
3010    self._cache = {
3011        # Map of packageId -> list of on-device .apk paths
3012        'package_apk_paths': {},
3013        # Set of packageId that were loaded from LoadCacheData and not yet
3014        # verified.
3015        'package_apk_paths_to_verify': set(),
3016        # Map of packageId -> set of on-device .apk checksums
3017        'package_apk_checksums': {},
3018        # Map of property_name -> value
3019        'getprop': {},
3020        # Map of device_path -> [ignore_other_files, map of path->checksum]
3021        'device_path_checksums': {},
3022        # Location of sdcard ($EXTERNAL_STORAGE).
3023        'external_storage': None,
3024        # Token used to detect when LoadCacheData is stale.
3025        'token': None,
3026        'prev_token': None,
3027    }
3028
3029  @decorators.WithTimeoutAndRetriesFromInstance()
3030  def LoadCacheData(self, data, timeout=None, retries=None):
3031    """Initializes the cache from data created using DumpCacheData.
3032
3033    The cache is used only if its token matches the one found on the device.
3034    This prevents a stale cache from being used (which can happen when sharing
3035    devices).
3036
3037    Args:
3038      data: A previously serialized cache (string).
3039      timeout: timeout in seconds
3040      retries: number of retries
3041
3042    Returns:
3043      Whether the cache was loaded.
3044    """
3045    obj = json.loads(data)
3046    self._EnsureCacheInitialized()
3047    given_token = obj.get('token')
3048    if not given_token or self._cache['prev_token'] != given_token:
3049      logger.warning('Stale cache detected. Not using it.')
3050      return False
3051
3052    self._cache['package_apk_paths'] = obj.get('package_apk_paths', {})
3053    # When using a cache across script invokations, verify that apps have
3054    # not been uninstalled.
3055    self._cache['package_apk_paths_to_verify'] = set(
3056        self._cache['package_apk_paths'].iterkeys())
3057
3058    package_apk_checksums = obj.get('package_apk_checksums', {})
3059    for k, v in package_apk_checksums.iteritems():
3060      package_apk_checksums[k] = set(v)
3061    self._cache['package_apk_checksums'] = package_apk_checksums
3062    device_path_checksums = obj.get('device_path_checksums', {})
3063    self._cache['device_path_checksums'] = device_path_checksums
3064    return True
3065
3066  @decorators.WithTimeoutAndRetriesFromInstance()
3067  def DumpCacheData(self, timeout=None, retries=None):
3068    """Dumps the current cache state to a string.
3069
3070    Args:
3071      timeout: timeout in seconds
3072      retries: number of retries
3073
3074    Returns:
3075      A serialized cache as a string.
3076    """
3077    self._EnsureCacheInitialized()
3078    obj = {}
3079    obj['token'] = self._cache['token']
3080    obj['package_apk_paths'] = self._cache['package_apk_paths']
3081    obj['package_apk_checksums'] = self._cache['package_apk_checksums']
3082    # JSON can't handle sets.
3083    for k, v in obj['package_apk_checksums'].iteritems():
3084      obj['package_apk_checksums'][k] = list(v)
3085    obj['device_path_checksums'] = self._cache['device_path_checksums']
3086    return json.dumps(obj, separators=(',', ':'))
3087
3088  @classmethod
3089  def parallel(cls, devices, async=False):
3090    """Creates a Parallelizer to operate over the provided list of devices.
3091
3092    Args:
3093      devices: A list of either DeviceUtils instances or objects from
3094               from which DeviceUtils instances can be constructed. If None,
3095               all attached devices will be used.
3096      async: If true, returns a Parallelizer that runs operations
3097             asynchronously.
3098
3099    Returns:
3100      A Parallelizer operating over |devices|.
3101    """
3102    devices = [d if isinstance(d, cls) else cls(d) for d in devices]
3103    if async:
3104      return parallelizer.Parallelizer(devices)
3105    else:
3106      return parallelizer.SyncParallelizer(devices)
3107
3108  @classmethod
3109  def HealthyDevices(cls, blacklist=None, device_arg='default', retries=1,
3110                     enable_usb_resets=False, abis=None, **kwargs):
3111    """Returns a list of DeviceUtils instances.
3112
3113    Returns a list of DeviceUtils instances that are attached, not blacklisted,
3114    and optionally filtered by --device flags or ANDROID_SERIAL environment
3115    variable.
3116
3117    Args:
3118      blacklist: A DeviceBlacklist instance (optional). Device serials in this
3119          blacklist will never be returned, but a warning will be logged if they
3120          otherwise would have been.
3121      device_arg: The value of the --device flag. This can be:
3122          'default' -> Same as [], but returns an empty list rather than raise a
3123              NoDevicesError.
3124          [] -> Returns all devices, unless $ANDROID_SERIAL is set.
3125          None -> Use $ANDROID_SERIAL if set, otherwise looks for a single
3126              attached device. Raises an exception if multiple devices are
3127              attached.
3128          'serial' -> Returns an instance for the given serial, if not
3129              blacklisted.
3130          ['A', 'B', ...] -> Returns instances for the subset that is not
3131              blacklisted.
3132      retries: Number of times to restart adb server and query it again if no
3133          devices are found on the previous attempts, with exponential backoffs
3134          up to 60s between each retry.
3135      enable_usb_resets: If true, will attempt to trigger a USB reset prior to
3136          the last attempt if there are no available devices. It will only reset
3137          those that appear to be android devices.
3138      abis: A list of ABIs for which the device needs to support at least one of
3139          (optional). See devil.android.ndk.abis for valid values.
3140      A device serial, or a list of device serials (optional).
3141
3142    Returns:
3143      A list of DeviceUtils instances.
3144
3145    Raises:
3146      NoDevicesError: Raised when no non-blacklisted devices exist and
3147          device_arg is passed.
3148      MultipleDevicesError: Raise when multiple devices exist, but |device_arg|
3149          is None.
3150    """
3151    allow_no_devices = False
3152    if device_arg == 'default':
3153      allow_no_devices = True
3154      device_arg = ()
3155
3156    select_multiple = True
3157    if not (isinstance(device_arg, tuple) or isinstance(device_arg, list)):
3158      select_multiple = False
3159      if device_arg:
3160        device_arg = (device_arg,)
3161
3162    blacklisted_devices = blacklist.Read() if blacklist else []
3163
3164    # adb looks for ANDROID_SERIAL, so support it as well.
3165    android_serial = os.environ.get('ANDROID_SERIAL')
3166    if not device_arg and android_serial:
3167      device_arg = (android_serial,)
3168
3169    def blacklisted(serial):
3170      if serial in blacklisted_devices:
3171        logger.warning('Device %s is blacklisted.', serial)
3172        return True
3173      return False
3174
3175    def supports_abi(abi, serial):
3176      if abis and abi not in abis:
3177        logger.warning("Device %s doesn't support required ABIs.", serial)
3178        return False
3179      return True
3180
3181    def _get_devices():
3182      if device_arg:
3183        devices = [cls(x, **kwargs) for x in device_arg if not blacklisted(x)]
3184      else:
3185        devices = []
3186        for adb in adb_wrapper.AdbWrapper.Devices():
3187          serial = adb.GetDeviceSerial()
3188          if not blacklisted(serial):
3189            device = cls(_CreateAdbWrapper(adb), **kwargs)
3190            if supports_abi(device.GetABI(), serial):
3191              devices.append(device)
3192
3193      if len(devices) == 0 and not allow_no_devices:
3194        raise device_errors.NoDevicesError()
3195      if len(devices) > 1 and not select_multiple:
3196        raise device_errors.MultipleDevicesError(devices)
3197      return sorted(devices)
3198
3199    def _reset_devices():
3200      if not reset_usb:
3201        logging.error(
3202            'reset_usb.py not supported on this platform (%s). Skipping usb '
3203            'resets.', sys.platform)
3204        return
3205      if device_arg:
3206        for serial in device_arg:
3207          reset_usb.reset_android_usb(serial)
3208      else:
3209        reset_usb.reset_all_android_devices()
3210
3211    for attempt in xrange(retries+1):
3212      try:
3213        return _get_devices()
3214      except device_errors.NoDevicesError:
3215        if attempt == retries:
3216          logging.error('No devices found after exhausting all retries.')
3217          raise
3218        elif attempt == retries - 1 and enable_usb_resets:
3219          logging.warning(
3220              'Attempting to reset relevant USB devices prior to the last '
3221              'attempt.')
3222          _reset_devices()
3223        # math.pow returns floats, so cast to int for easier testing
3224        sleep_s = min(int(math.pow(2, attempt + 1)), 60)
3225        logger.warning(
3226            'No devices found. Will try again after restarting adb server '
3227            'and a short nap of %d s.', sleep_s)
3228        time.sleep(sleep_s)
3229        RestartServer()
3230
3231  @decorators.WithTimeoutAndRetriesFromInstance()
3232  def RestartAdbd(self, timeout=None, retries=None):
3233    logger.info('Restarting adbd on device.')
3234    with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script:
3235      self.WriteFile(script.name, _RESTART_ADBD_SCRIPT)
3236      self.RunShellCommand(
3237          ['source', script.name], check_return=True, as_root=True)
3238      self.adb.WaitForDevice()
3239
3240  @decorators.WithTimeoutAndRetriesFromInstance()
3241  def GrantPermissions(self, package, permissions, timeout=None, retries=None):
3242    # Permissions only need to be set on M and above because of the changes to
3243    # the permission model.
3244    if not permissions or self.build_version_sdk < version_codes.MARSHMALLOW:
3245      return
3246
3247    permissions = set(
3248        p for p in permissions if not _PERMISSIONS_BLACKLIST_RE.match(p))
3249
3250    if ('android.permission.WRITE_EXTERNAL_STORAGE' in permissions
3251        and 'android.permission.READ_EXTERNAL_STORAGE' not in permissions):
3252      permissions.add('android.permission.READ_EXTERNAL_STORAGE')
3253
3254    script = ';'.join([
3255      'p={package}',
3256      'for q in {permissions}',
3257      'do pm grant "$p" "$q"',
3258      'echo "{sep}$q{sep}$?{sep}"',
3259      'done'
3260    ]).format(
3261        package=cmd_helper.SingleQuote(package),
3262        permissions=' '.join(
3263            cmd_helper.SingleQuote(p) for p in sorted(permissions)),
3264        sep=_SHELL_OUTPUT_SEPARATOR)
3265
3266    logger.info('Setting permissions for %s.', package)
3267    res = self.RunShellCommand(
3268        script, shell=True, raw_output=True, large_output=True,
3269        check_return=True)
3270    res = res.split(_SHELL_OUTPUT_SEPARATOR)
3271    failures = [
3272      (permission, output.strip())
3273      for permission, status, output in zip(res[1::3], res[2::3], res[0::3])
3274      if int(status)]
3275
3276    if failures:
3277      logger.warning(
3278          'Failed to grant some permissions. Blacklist may need to be updated?')
3279      for permission, output in failures:
3280        # Try to grab the relevant error message from the output.
3281        m = _PERMISSIONS_EXCEPTION_RE.search(output)
3282        if m:
3283          error_msg = m.group(0)
3284        elif len(output) > 200:
3285          error_msg = repr(output[:200]) + ' (truncated)'
3286        else:
3287          error_msg = repr(output)
3288        logger.warning('- %s: %s', permission, error_msg)
3289
3290  @decorators.WithTimeoutAndRetriesFromInstance()
3291  def IsScreenOn(self, timeout=None, retries=None):
3292    """Determines if screen is on.
3293
3294    Dumpsys input_method exposes screen on/off state. Below is an explination of
3295    the states.
3296
3297    Pre-L:
3298      On: mScreenOn=true
3299      Off: mScreenOn=false
3300    L+:
3301      On: mInteractive=true
3302      Off: mInteractive=false
3303
3304    Returns:
3305      True if screen is on, false if it is off.
3306
3307    Raises:
3308      device_errors.CommandFailedError: If screen state cannot be found.
3309    """
3310    if self.build_version_sdk < version_codes.LOLLIPOP:
3311      input_check = 'mScreenOn'
3312      check_value = 'mScreenOn=true'
3313    else:
3314      input_check = 'mInteractive'
3315      check_value = 'mInteractive=true'
3316    dumpsys_out = self._RunPipedShellCommand(
3317        'dumpsys input_method | grep %s' % input_check)
3318    if not dumpsys_out:
3319      raise device_errors.CommandFailedError(
3320          'Unable to detect screen state', str(self))
3321    return check_value in dumpsys_out[0]
3322
3323  @decorators.WithTimeoutAndRetriesFromInstance()
3324  def SetScreen(self, on, timeout=None, retries=None):
3325    """Turns screen on and off.
3326
3327    Args:
3328      on: bool to decide state to switch to. True = on False = off.
3329    """
3330    def screen_test():
3331      return self.IsScreenOn() == on
3332
3333    if screen_test():
3334      logger.info('Screen already in expected state.')
3335      return
3336    self.SendKeyEvent(keyevent.KEYCODE_POWER)
3337    timeout_retry.WaitFor(screen_test, wait_period=1)
3338
3339  @decorators.WithTimeoutAndRetriesFromInstance()
3340  def ChangeOwner(self, owner_group, paths, timeout=None, retries=None):
3341    """Changes file system ownership for permissions.
3342
3343    Args:
3344      owner_group: New owner and group to assign. Note that this should be a
3345        string in the form user[.group] where the group is option.
3346      paths: Paths to change ownership of.
3347
3348      Note that the -R recursive option is not supported by all Android
3349      versions.
3350    """
3351    if not paths:
3352      return
3353    self.RunShellCommand(['chown', owner_group] + paths, check_return=True)
3354
3355  @decorators.WithTimeoutAndRetriesFromInstance()
3356  def ChangeSecurityContext(self, security_context, paths, timeout=None,
3357                            retries=None):
3358    """Changes the SELinux security context for files.
3359
3360    Args:
3361      security_context: The new security context as a string
3362      paths: Paths to change the security context of.
3363
3364      Note that the -R recursive option is not supported by all Android
3365      versions.
3366    """
3367    if not paths:
3368      return
3369    command = ['chcon', security_context] + paths
3370
3371    # Note, need to force su because chcon can fail with permission errors even
3372    # if the device is rooted.
3373    self.RunShellCommand(command, as_root=_FORCE_SU, check_return=True)
3374