• 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 itertools
14import json
15import logging
16import multiprocessing
17import os
18import posixpath
19import pprint
20import re
21import shutil
22import stat
23import tempfile
24import time
25import threading
26import uuid
27import zipfile
28
29from devil import base_error
30from devil import devil_env
31from devil.utils import cmd_helper
32from devil.android import apk_helper
33from devil.android import device_signal
34from devil.android import decorators
35from devil.android import device_errors
36from devil.android import device_temp_file
37from devil.android import install_commands
38from devil.android import logcat_monitor
39from devil.android import md5sum
40from devil.android.constants import chrome
41from devil.android.sdk import adb_wrapper
42from devil.android.sdk import intent
43from devil.android.sdk import keyevent
44from devil.android.sdk import split_select
45from devil.android.sdk import version_codes
46from devil.utils import host_utils
47from devil.utils import parallelizer
48from devil.utils import reraiser_thread
49from devil.utils import timeout_retry
50from devil.utils import zip_utils
51
52logger = logging.getLogger(__name__)
53
54_DEFAULT_TIMEOUT = 30
55_DEFAULT_RETRIES = 3
56
57# A sentinel object for default values
58# TODO(jbudorick,perezju): revisit how default values are handled by
59# the timeout_retry decorators.
60DEFAULT = object()
61
62_RESTART_ADBD_SCRIPT = """
63  trap '' HUP
64  trap '' TERM
65  trap '' PIPE
66  function restart() {
67    stop adbd
68    start adbd
69  }
70  restart &
71"""
72
73# Not all permissions can be set.
74_PERMISSIONS_BLACKLIST = [
75    'android.permission.ACCESS_LOCATION_EXTRA_COMMANDS',
76    'android.permission.ACCESS_MOCK_LOCATION',
77    'android.permission.ACCESS_NETWORK_STATE',
78    'android.permission.ACCESS_NOTIFICATION_POLICY',
79    'android.permission.ACCESS_WIFI_STATE',
80    'android.permission.AUTHENTICATE_ACCOUNTS',
81    'android.permission.BLUETOOTH',
82    'android.permission.BLUETOOTH_ADMIN',
83    'android.permission.BROADCAST_STICKY',
84    'android.permission.CHANGE_NETWORK_STATE',
85    'android.permission.CHANGE_WIFI_MULTICAST_STATE',
86    'android.permission.CHANGE_WIFI_STATE',
87    'android.permission.DISABLE_KEYGUARD',
88    'android.permission.DOWNLOAD_WITHOUT_NOTIFICATION',
89    'android.permission.EXPAND_STATUS_BAR',
90    'android.permission.GET_PACKAGE_SIZE',
91    'android.permission.INSTALL_SHORTCUT',
92    'android.permission.INTERNET',
93    'android.permission.KILL_BACKGROUND_PROCESSES',
94    'android.permission.MANAGE_ACCOUNTS',
95    'android.permission.MODIFY_AUDIO_SETTINGS',
96    'android.permission.NFC',
97    'android.permission.READ_SYNC_SETTINGS',
98    'android.permission.READ_SYNC_STATS',
99    'android.permission.RECEIVE_BOOT_COMPLETED',
100    'android.permission.RECORD_VIDEO',
101    'android.permission.REORDER_TASKS',
102    'android.permission.REQUEST_INSTALL_PACKAGES',
103    'android.permission.RUN_INSTRUMENTATION',
104    'android.permission.SET_ALARM',
105    'android.permission.SET_TIME_ZONE',
106    'android.permission.SET_WALLPAPER',
107    'android.permission.SET_WALLPAPER_HINTS',
108    'android.permission.TRANSMIT_IR',
109    'android.permission.USE_CREDENTIALS',
110    'android.permission.USE_FINGERPRINT',
111    'android.permission.VIBRATE',
112    'android.permission.WAKE_LOCK',
113    'android.permission.WRITE_SYNC_SETTINGS',
114    'com.android.browser.permission.READ_HISTORY_BOOKMARKS',
115    'com.android.browser.permission.WRITE_HISTORY_BOOKMARKS',
116    'com.android.launcher.permission.INSTALL_SHORTCUT',
117    'com.chrome.permission.DEVICE_EXTRAS',
118    'com.google.android.apps.now.CURRENT_ACCOUNT_ACCESS',
119    'com.google.android.c2dm.permission.RECEIVE',
120    'com.google.android.providers.gsf.permission.READ_GSERVICES',
121    'com.sec.enterprise.knox.MDM_CONTENT_PROVIDER',
122]
123for package_info in chrome.PACKAGE_INFO.itervalues():
124  _PERMISSIONS_BLACKLIST.extend([
125      '%s.permission.C2D_MESSAGE' % package_info.package,
126      '%s.permission.READ_WRITE_BOOKMARK_FOLDERS' % package_info.package,
127      '%s.TOS_ACKED' % package_info.package])
128
129_CURRENT_FOCUS_CRASH_RE = re.compile(
130    r'\s*mCurrentFocus.*Application (Error|Not Responding): (\S+)}')
131
132_GETPROP_RE = re.compile(r'\[(.*?)\]: \[(.*?)\]')
133
134# Regex to parse the long (-l) output of 'ls' command, c.f.
135# https://github.com/landley/toybox/blob/master/toys/posix/ls.c#L446
136_LONG_LS_OUTPUT_RE = re.compile(
137    r'(?P<st_mode>[\w-]{10})\s+'                  # File permissions
138    r'(?:(?P<st_nlink>\d+)\s+)?'                  # Number of links (optional)
139    r'(?P<st_owner>\w+)\s+'                       # Name of owner
140    r'(?P<st_group>\w+)\s+'                       # Group of owner
141    r'(?:'                                        # Either ...
142      r'(?P<st_rdev_major>\d+),\s+'                 # Device major, and
143      r'(?P<st_rdev_minor>\d+)\s+'                  # Device minor
144    r'|'                                          # .. or
145      r'(?P<st_size>\d+)\s+'                        # Size in bytes
146    r')?'                                         # .. or nothing
147    r'(?P<st_mtime>\d{4}-\d\d-\d\d \d\d:\d\d)\s+' # Modification date/time
148    r'(?P<filename>.+?)'                          # File name
149    r'(?: -> (?P<symbolic_link_to>.+))?'          # Symbolic link (optional)
150    r'$'                                          # End of string
151)
152_LS_DATE_FORMAT = '%Y-%m-%d %H:%M'
153_FILE_MODE_RE = re.compile(r'[dbclps-](?:[r-][w-][xSs-]){2}[r-][w-][xTt-]$')
154_FILE_MODE_KIND = {
155    'd': stat.S_IFDIR, 'b': stat.S_IFBLK, 'c': stat.S_IFCHR,
156    'l': stat.S_IFLNK, 'p': stat.S_IFIFO, 's': stat.S_IFSOCK,
157    '-': stat.S_IFREG}
158_FILE_MODE_PERMS = [
159    stat.S_IRUSR, stat.S_IWUSR, stat.S_IXUSR,
160    stat.S_IRGRP, stat.S_IWGRP, stat.S_IXGRP,
161    stat.S_IROTH, stat.S_IWOTH, stat.S_IXOTH,
162]
163_FILE_MODE_SPECIAL = [
164    ('s', stat.S_ISUID),
165    ('s', stat.S_ISGID),
166    ('t', stat.S_ISVTX),
167]
168_SELINUX_MODE = {
169    'enforcing': True,
170    'permissive': False,
171    'disabled': None
172}
173# Some devices require different logic for checking if root is necessary
174_SPECIAL_ROOT_DEVICE_LIST = [
175    'marlin',
176    'sailfish',
177]
178
179
180@decorators.WithExplicitTimeoutAndRetries(
181    _DEFAULT_TIMEOUT, _DEFAULT_RETRIES)
182def GetAVDs():
183  """Returns a list of Android Virtual Devices.
184
185  Returns:
186    A list containing the configured AVDs.
187  """
188  lines = cmd_helper.GetCmdOutput([
189      os.path.join(devil_env.config.LocalPath('android_sdk'),
190                   'tools', 'android'),
191      'list', 'avd']).splitlines()
192  avds = []
193  for line in lines:
194    if 'Name:' not in line:
195      continue
196    key, value = (s.strip() for s in line.split(':', 1))
197    if key == 'Name':
198      avds.append(value)
199  return avds
200
201
202@decorators.WithExplicitTimeoutAndRetries(
203    _DEFAULT_TIMEOUT, _DEFAULT_RETRIES)
204def RestartServer():
205  """Restarts the adb server.
206
207  Raises:
208    CommandFailedError if we fail to kill or restart the server.
209  """
210  def adb_killed():
211    return not adb_wrapper.AdbWrapper.IsServerOnline()
212
213  def adb_started():
214    return adb_wrapper.AdbWrapper.IsServerOnline()
215
216  adb_wrapper.AdbWrapper.KillServer()
217  if not timeout_retry.WaitFor(adb_killed, wait_period=1, max_tries=5):
218    # TODO(perezju): raise an exception after fixng http://crbug.com/442319
219    logger.warning('Failed to kill adb server')
220  adb_wrapper.AdbWrapper.StartServer()
221  if not timeout_retry.WaitFor(adb_started, wait_period=1, max_tries=5):
222    raise device_errors.CommandFailedError('Failed to start adb server')
223
224
225def _ParseModeString(mode_str):
226  """Parse a mode string, e.g. 'drwxrwxrwx', into a st_mode value.
227
228  Effectively the reverse of |mode_to_string| in, e.g.:
229  https://github.com/landley/toybox/blob/master/lib/lib.c#L896
230  """
231  if not _FILE_MODE_RE.match(mode_str):
232    raise ValueError('Unexpected file mode %r', mode_str)
233  mode = _FILE_MODE_KIND[mode_str[0]]
234  for c, flag in zip(mode_str[1:], _FILE_MODE_PERMS):
235    if c != '-' and c.islower():
236      mode |= flag
237  for c, (t, flag) in zip(mode_str[3::3], _FILE_MODE_SPECIAL):
238    if c.lower() == t:
239      mode |= flag
240  return mode
241
242
243def _GetTimeStamp():
244  """Return a basic ISO 8601 time stamp with the current local time."""
245  return time.strftime('%Y%m%dT%H%M%S', time.localtime())
246
247
248def _JoinLines(lines):
249  # makes sure that the last line is also terminated, and is more memory
250  # efficient than first appending an end-line to each line and then joining
251  # all of them together.
252  return ''.join(s for line in lines for s in (line, '\n'))
253
254
255def _CreateAdbWrapper(device):
256  if isinstance(device, adb_wrapper.AdbWrapper):
257    return device
258  else:
259    return adb_wrapper.AdbWrapper(device)
260
261
262def _FormatPartialOutputError(output):
263  lines = output.splitlines() if isinstance(output, basestring) else output
264  message = ['Partial output found:']
265  if len(lines) > 11:
266    message.extend('- %s' % line for line in lines[:5])
267    message.extend('<snip>')
268    message.extend('- %s' % line for line in lines[-5:])
269  else:
270    message.extend('- %s' % line for line in lines)
271  return '\n'.join(message)
272
273
274class DeviceUtils(object):
275
276  _MAX_ADB_COMMAND_LENGTH = 512
277  _MAX_ADB_OUTPUT_LENGTH = 32768
278  _LAUNCHER_FOCUSED_RE = re.compile(
279      r'\s*mCurrentFocus.*(Launcher|launcher).*')
280  _VALID_SHELL_VARIABLE = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$')
281
282  LOCAL_PROPERTIES_PATH = posixpath.join('/', 'data', 'local.prop')
283
284  # Property in /data/local.prop that controls Java assertions.
285  JAVA_ASSERT_PROPERTY = 'dalvik.vm.enableassertions'
286
287  def __init__(self, device, enable_device_files_cache=False,
288               default_timeout=_DEFAULT_TIMEOUT,
289               default_retries=_DEFAULT_RETRIES):
290    """DeviceUtils constructor.
291
292    Args:
293      device: Either a device serial, an existing AdbWrapper instance, or an
294        an existing AndroidCommands instance.
295      enable_device_files_cache: For PushChangedFiles(), cache checksums of
296        pushed files rather than recomputing them on a subsequent call.
297      default_timeout: An integer containing the default number of seconds to
298        wait for an operation to complete if no explicit value is provided.
299      default_retries: An integer containing the default number or times an
300        operation should be retried on failure if no explicit value is provided.
301    """
302    self.adb = None
303    if isinstance(device, basestring):
304      self.adb = _CreateAdbWrapper(device)
305    elif isinstance(device, adb_wrapper.AdbWrapper):
306      self.adb = device
307    else:
308      raise ValueError('Unsupported device value: %r' % device)
309    self._commands_installed = None
310    self._default_timeout = default_timeout
311    self._default_retries = default_retries
312    self._enable_device_files_cache = enable_device_files_cache
313    self._cache = {}
314    self._client_caches = {}
315    self._cache_lock = threading.RLock()
316    assert hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR)
317    assert hasattr(self, decorators.DEFAULT_RETRIES_ATTR)
318
319    self._ClearCache()
320
321  @property
322  def serial(self):
323    """Returns the device serial."""
324    return self.adb.GetDeviceSerial()
325
326  def __eq__(self, other):
327    """Checks whether |other| refers to the same device as |self|.
328
329    Args:
330      other: The object to compare to. This can be a basestring, an instance
331        of adb_wrapper.AdbWrapper, or an instance of DeviceUtils.
332    Returns:
333      Whether |other| refers to the same device as |self|.
334    """
335    return self.serial == str(other)
336
337  def __lt__(self, other):
338    """Compares two instances of DeviceUtils.
339
340    This merely compares their serial numbers.
341
342    Args:
343      other: The instance of DeviceUtils to compare to.
344    Returns:
345      Whether |self| is less than |other|.
346    """
347    return self.serial < other.serial
348
349  def __str__(self):
350    """Returns the device serial."""
351    return self.serial
352
353  @decorators.WithTimeoutAndRetriesFromInstance()
354  def IsOnline(self, timeout=None, retries=None):
355    """Checks whether the device is online.
356
357    Args:
358      timeout: timeout in seconds
359      retries: number of retries
360
361    Returns:
362      True if the device is online, False otherwise.
363
364    Raises:
365      CommandTimeoutError on timeout.
366    """
367    try:
368      return self.adb.GetState() == 'device'
369    except base_error.BaseError as exc:
370      logger.info('Failed to get state: %s', exc)
371      return False
372
373  @decorators.WithTimeoutAndRetriesFromInstance()
374  def HasRoot(self, timeout=None, retries=None):
375    """Checks whether or not adbd has root privileges.
376
377    Args:
378      timeout: timeout in seconds
379      retries: number of retries
380
381    Returns:
382      True if adbd has root privileges, False otherwise.
383
384    Raises:
385      CommandTimeoutError on timeout.
386      DeviceUnreachableError on missing device.
387    """
388    try:
389      if self.product_name in _SPECIAL_ROOT_DEVICE_LIST:
390        return self.GetProp('service.adb.root') == '1'
391      self.RunShellCommand(['ls', '/root'], check_return=True)
392      return True
393    except device_errors.AdbCommandFailedError:
394      return False
395
396  def NeedsSU(self, timeout=DEFAULT, retries=DEFAULT):
397    """Checks whether 'su' is needed to access protected resources.
398
399    Args:
400      timeout: timeout in seconds
401      retries: number of retries
402
403    Returns:
404      True if 'su' is available on the device and is needed to to access
405        protected resources; False otherwise if either 'su' is not available
406        (e.g. because the device has a user build), or not needed (because adbd
407        already has root privileges).
408
409    Raises:
410      CommandTimeoutError on timeout.
411      DeviceUnreachableError on missing device.
412    """
413    if 'needs_su' not in self._cache:
414      cmd = '%s && ! ls /root' % self._Su('ls /root')
415      if self.product_name in _SPECIAL_ROOT_DEVICE_LIST:
416        if self.HasRoot():
417          self._cache['needs_su'] = False
418          return False
419        cmd = 'which which && which su'
420      try:
421        self.RunShellCommand(cmd, shell=True, check_return=True,
422            timeout=self._default_timeout if timeout is DEFAULT else timeout,
423            retries=self._default_retries if retries is DEFAULT else retries)
424        self._cache['needs_su'] = True
425      except device_errors.AdbCommandFailedError:
426        self._cache['needs_su'] = False
427    return self._cache['needs_su']
428
429
430  def _Su(self, command):
431    if self.build_version_sdk >= version_codes.MARSHMALLOW:
432      return 'su 0 %s' % command
433    return 'su -c %s' % command
434
435  @decorators.WithTimeoutAndRetriesFromInstance()
436  def EnableRoot(self, timeout=None, retries=None):
437    """Restarts adbd with root privileges.
438
439    Args:
440      timeout: timeout in seconds
441      retries: number of retries
442
443    Raises:
444      CommandFailedError if root could not be enabled.
445      CommandTimeoutError on timeout.
446    """
447    if 'needs_su' in self._cache:
448      del self._cache['needs_su']
449
450    try:
451      self.adb.Root()
452    except device_errors.AdbCommandFailedError:
453      if self.IsUserBuild():
454        raise device_errors.CommandFailedError(
455            'Unable to root device with user build.', str(self))
456      else:
457        raise  # Failed probably due to some other reason.
458
459    def device_online_with_root():
460      try:
461        self.adb.WaitForDevice()
462        return self.GetProp('service.adb.root', cache=False) == '1'
463      except (device_errors.AdbCommandFailedError,
464              device_errors.DeviceUnreachableError):
465        return False
466
467    timeout_retry.WaitFor(device_online_with_root, wait_period=1)
468
469  @decorators.WithTimeoutAndRetriesFromInstance()
470  def IsUserBuild(self, timeout=None, retries=None):
471    """Checks whether or not the device is running a user build.
472
473    Args:
474      timeout: timeout in seconds
475      retries: number of retries
476
477    Returns:
478      True if the device is running a user build, False otherwise (i.e. if
479        it's running a userdebug build).
480
481    Raises:
482      CommandTimeoutError on timeout.
483      DeviceUnreachableError on missing device.
484    """
485    return self.build_type == 'user'
486
487  @decorators.WithTimeoutAndRetriesFromInstance()
488  def GetExternalStoragePath(self, timeout=None, retries=None):
489    """Get the device's path to its SD card.
490
491    Args:
492      timeout: timeout in seconds
493      retries: number of retries
494
495    Returns:
496      The device's path to its SD card.
497
498    Raises:
499      CommandFailedError if the external storage path could not be determined.
500      CommandTimeoutError on timeout.
501      DeviceUnreachableError on missing device.
502    """
503    self._EnsureCacheInitialized()
504    if not self._cache['external_storage']:
505      raise device_errors.CommandFailedError('$EXTERNAL_STORAGE is not set',
506                                             str(self))
507    return self._cache['external_storage']
508
509  @decorators.WithTimeoutAndRetriesFromInstance()
510  def GetApplicationPaths(self, package, timeout=None, retries=None):
511    """Get the paths of the installed apks on the device for the given package.
512
513    Args:
514      package: Name of the package.
515
516    Returns:
517      List of paths to the apks on the device for the given package.
518    """
519    return self._GetApplicationPathsInternal(package)
520
521  def _GetApplicationPathsInternal(self, package, skip_cache=False):
522    cached_result = self._cache['package_apk_paths'].get(package)
523    if cached_result is not None and not skip_cache:
524      if package in self._cache['package_apk_paths_to_verify']:
525        self._cache['package_apk_paths_to_verify'].remove(package)
526        # Don't verify an app that is not thought to be installed. We are
527        # concerned only with apps we think are installed having been
528        # uninstalled manually.
529        if cached_result and not self.PathExists(cached_result):
530          cached_result = None
531          self._cache['package_apk_checksums'].pop(package, 0)
532      if cached_result is not None:
533        return list(cached_result)
534    # 'pm path' is liable to incorrectly exit with a nonzero number starting
535    # in Lollipop.
536    # TODO(jbudorick): Check if this is fixed as new Android versions are
537    # released to put an upper bound on this.
538    should_check_return = (self.build_version_sdk < version_codes.LOLLIPOP)
539    output = self.RunShellCommand(
540        ['pm', 'path', package], check_return=should_check_return)
541    apks = []
542    for line in output:
543      if not line.startswith('package:'):
544        continue
545      apks.append(line[len('package:'):])
546    if not apks and output:
547      raise device_errors.CommandFailedError(
548          'pm path returned: %r' % '\n'.join(output), str(self))
549    self._cache['package_apk_paths'][package] = list(apks)
550    return apks
551
552  @decorators.WithTimeoutAndRetriesFromInstance()
553  def GetApplicationVersion(self, package, timeout=None, retries=None):
554    """Get the version name of a package installed on the device.
555
556    Args:
557      package: Name of the package.
558
559    Returns:
560      A string with the version name or None if the package is not found
561      on the device.
562    """
563    output = self.RunShellCommand(
564        ['dumpsys', 'package', package], check_return=True)
565    if not output:
566      return None
567    for line in output:
568      line = line.strip()
569      if line.startswith('versionName='):
570        return line[len('versionName='):]
571    raise device_errors.CommandFailedError(
572        'Version name for %s not found on dumpsys output' % package, str(self))
573
574  @decorators.WithTimeoutAndRetriesFromInstance()
575  def GetApplicationDataDirectory(self, package, timeout=None, retries=None):
576    """Get the data directory on the device for the given package.
577
578    Args:
579      package: Name of the package.
580
581    Returns:
582      The package's data directory.
583    Raises:
584      CommandFailedError if the package's data directory can't be found,
585        whether because it's not installed or otherwise.
586    """
587    output = self._RunPipedShellCommand(
588        'pm dump %s | grep dataDir=' % cmd_helper.SingleQuote(package))
589    for line in output:
590      _, _, dataDir = line.partition('dataDir=')
591      if dataDir:
592        return dataDir
593    raise device_errors.CommandFailedError(
594        'Could not find data directory for %s', package)
595
596  @decorators.WithTimeoutAndRetriesFromInstance()
597  def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None):
598    """Wait for the device to fully boot.
599
600    This means waiting for the device to boot, the package manager to be
601    available, and the SD card to be ready. It can optionally mean waiting
602    for wifi to come up, too.
603
604    Args:
605      wifi: A boolean indicating if we should wait for wifi to come up or not.
606      timeout: timeout in seconds
607      retries: number of retries
608
609    Raises:
610      CommandFailedError on failure.
611      CommandTimeoutError if one of the component waits times out.
612      DeviceUnreachableError if the device becomes unresponsive.
613    """
614    def sd_card_ready():
615      try:
616        self.RunShellCommand(['test', '-d', self.GetExternalStoragePath()],
617                             check_return=True)
618        return True
619      except device_errors.AdbCommandFailedError:
620        return False
621
622    def pm_ready():
623      try:
624        return self._GetApplicationPathsInternal('android', skip_cache=True)
625      except device_errors.CommandFailedError:
626        return False
627
628    def boot_completed():
629      try:
630        return self.GetProp('sys.boot_completed', cache=False) == '1'
631      except device_errors.CommandFailedError:
632        return False
633
634    def wifi_enabled():
635      return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi'],
636                                                        check_return=False)
637
638    self.adb.WaitForDevice()
639    timeout_retry.WaitFor(sd_card_ready)
640    timeout_retry.WaitFor(pm_ready)
641    timeout_retry.WaitFor(boot_completed)
642    if wifi:
643      timeout_retry.WaitFor(wifi_enabled)
644
645  REBOOT_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT
646
647  @decorators.WithTimeoutAndRetriesFromInstance(
648      min_default_timeout=REBOOT_DEFAULT_TIMEOUT)
649  def Reboot(self, block=True, wifi=False, timeout=None, retries=None):
650    """Reboot the device.
651
652    Args:
653      block: A boolean indicating if we should wait for the reboot to complete.
654      wifi: A boolean indicating if we should wait for wifi to be enabled after
655        the reboot. The option has no effect unless |block| is also True.
656      timeout: timeout in seconds
657      retries: number of retries
658
659    Raises:
660      CommandTimeoutError on timeout.
661      DeviceUnreachableError on missing device.
662    """
663    def device_offline():
664      return not self.IsOnline()
665
666    self.adb.Reboot()
667    self._ClearCache()
668    timeout_retry.WaitFor(device_offline, wait_period=1)
669    if block:
670      self.WaitUntilFullyBooted(wifi=wifi)
671
672  INSTALL_DEFAULT_TIMEOUT = 4 * _DEFAULT_TIMEOUT
673
674  @decorators.WithTimeoutAndRetriesFromInstance(
675      min_default_timeout=INSTALL_DEFAULT_TIMEOUT)
676  def Install(self, apk, allow_downgrade=False, reinstall=False,
677              permissions=None, timeout=None, retries=None):
678    """Install an APK.
679
680    Noop if an identical APK is already installed.
681
682    Args:
683      apk: An ApkHelper instance or string containing the path to the APK.
684      allow_downgrade: A boolean indicating if we should allow downgrades.
685      reinstall: A boolean indicating if we should keep any existing app data.
686      permissions: Set of permissions to set. If not set, finds permissions with
687          apk helper. To set no permissions, pass [].
688      timeout: timeout in seconds
689      retries: number of retries
690
691    Raises:
692      CommandFailedError if the installation fails.
693      CommandTimeoutError if the installation times out.
694      DeviceUnreachableError on missing device.
695    """
696    self._InstallInternal(apk, None, allow_downgrade=allow_downgrade,
697                          reinstall=reinstall, permissions=permissions)
698
699  @decorators.WithTimeoutAndRetriesFromInstance(
700      min_default_timeout=INSTALL_DEFAULT_TIMEOUT)
701  def InstallSplitApk(self, base_apk, split_apks, allow_downgrade=False,
702                      reinstall=False, allow_cached_props=False,
703                      permissions=None, timeout=None, retries=None):
704    """Install a split APK.
705
706    Noop if all of the APK splits are already installed.
707
708    Args:
709      base_apk: An ApkHelper instance or string containing the path to the base
710          APK.
711      split_apks: A list of strings of paths of all of the APK splits.
712      allow_downgrade: A boolean indicating if we should allow downgrades.
713      reinstall: A boolean indicating if we should keep any existing app data.
714      allow_cached_props: Whether to use cached values for device properties.
715      permissions: Set of permissions to set. If not set, finds permissions with
716          apk helper. To set no permissions, pass [].
717      timeout: timeout in seconds
718      retries: number of retries
719
720    Raises:
721      CommandFailedError if the installation fails.
722      CommandTimeoutError if the installation times out.
723      DeviceUnreachableError on missing device.
724      DeviceVersionError if device SDK is less than Android L.
725    """
726    self._InstallInternal(base_apk, split_apks, reinstall=reinstall,
727                          allow_cached_props=allow_cached_props,
728                          permissions=permissions,
729                          allow_downgrade=allow_downgrade)
730
731  def _InstallInternal(self, base_apk, split_apks, allow_downgrade=False,
732                       reinstall=False, allow_cached_props=False,
733                       permissions=None):
734    if split_apks:
735      self._CheckSdkLevel(version_codes.LOLLIPOP)
736
737    base_apk = apk_helper.ToHelper(base_apk)
738
739    all_apks = [base_apk.path]
740    if split_apks:
741      all_apks += split_select.SelectSplits(
742        self, base_apk.path, split_apks, allow_cached_props=allow_cached_props)
743      if len(all_apks) == 1:
744        logger.warning('split-select did not select any from %s', split_apks)
745
746    missing_apks = [apk for apk in all_apks if not os.path.exists(apk)]
747    if missing_apks:
748      raise device_errors.CommandFailedError(
749          'Attempted to install non-existent apks: %s'
750              % pprint.pformat(missing_apks))
751
752    package_name = base_apk.GetPackageName()
753    device_apk_paths = self._GetApplicationPathsInternal(package_name)
754
755    apks_to_install = None
756    host_checksums = None
757    if not device_apk_paths:
758      apks_to_install = all_apks
759    elif len(device_apk_paths) > 1 and not split_apks:
760      logger.warning(
761          'Installing non-split APK when split APK was previously installed')
762      apks_to_install = all_apks
763    elif len(device_apk_paths) == 1 and split_apks:
764      logger.warning(
765          'Installing split APK when non-split APK was previously installed')
766      apks_to_install = all_apks
767    else:
768      try:
769        apks_to_install, host_checksums = (
770            self._ComputeStaleApks(package_name, all_apks))
771      except EnvironmentError as e:
772        logger.warning('Error calculating md5: %s', e)
773        apks_to_install, host_checksums = all_apks, None
774      if apks_to_install and not reinstall:
775        self.Uninstall(package_name)
776        apks_to_install = all_apks
777
778    if apks_to_install:
779      # Assume that we won't know the resulting device state.
780      self._cache['package_apk_paths'].pop(package_name, 0)
781      self._cache['package_apk_checksums'].pop(package_name, 0)
782      if split_apks:
783        partial = package_name if len(apks_to_install) < len(all_apks) else None
784        self.adb.InstallMultiple(
785            apks_to_install, partial=partial, reinstall=reinstall,
786            allow_downgrade=allow_downgrade)
787      else:
788        self.adb.Install(
789            base_apk.path, reinstall=reinstall, allow_downgrade=allow_downgrade)
790      if (permissions is None
791          and self.build_version_sdk >= version_codes.MARSHMALLOW):
792        permissions = base_apk.GetPermissions()
793      self.GrantPermissions(package_name, permissions)
794      # Upon success, we know the device checksums, but not their paths.
795      if host_checksums is not None:
796        self._cache['package_apk_checksums'][package_name] = host_checksums
797    else:
798      # Running adb install terminates running instances of the app, so to be
799      # consistent, we explicitly terminate it when skipping the install.
800      self.ForceStop(package_name)
801
802  @decorators.WithTimeoutAndRetriesFromInstance()
803  def Uninstall(self, package_name, keep_data=False, timeout=None,
804                retries=None):
805    """Remove the app |package_name| from the device.
806
807    This is a no-op if the app is not already installed.
808
809    Args:
810      package_name: The package to uninstall.
811      keep_data: (optional) Whether to keep the data and cache directories.
812      timeout: Timeout in seconds.
813      retries: Number of retries.
814
815    Raises:
816      CommandFailedError if the uninstallation fails.
817      CommandTimeoutError if the uninstallation times out.
818      DeviceUnreachableError on missing device.
819    """
820    installed = self._GetApplicationPathsInternal(package_name)
821    if not installed:
822      return
823    try:
824      self.adb.Uninstall(package_name, keep_data)
825      self._cache['package_apk_paths'][package_name] = []
826      self._cache['package_apk_checksums'][package_name] = set()
827    except:
828      # Clear cache since we can't be sure of the state.
829      self._cache['package_apk_paths'].pop(package_name, 0)
830      self._cache['package_apk_checksums'].pop(package_name, 0)
831      raise
832
833  def _CheckSdkLevel(self, required_sdk_level):
834    """Raises an exception if the device does not have the required SDK level.
835    """
836    if self.build_version_sdk < required_sdk_level:
837      raise device_errors.DeviceVersionError(
838          ('Requires SDK level %s, device is SDK level %s' %
839           (required_sdk_level, self.build_version_sdk)),
840           device_serial=self.serial)
841
842  @decorators.WithTimeoutAndRetriesFromInstance()
843  def RunShellCommand(self, cmd, shell=False, check_return=False, cwd=None,
844                      env=None, run_as=None, as_root=False, single_line=False,
845                      large_output=False, raw_output=False, timeout=None,
846                      retries=None):
847    """Run an ADB shell command.
848
849    The command to run |cmd| should be a sequence of program arguments
850    (preferred) or a single string with a shell script to run.
851
852    When |cmd| is a sequence, it is assumed to contain the name of the command
853    to run followed by its arguments. In this case, arguments are passed to the
854    command exactly as given, preventing any further processing by the shell.
855    This allows callers to easily pass arguments with spaces or special
856    characters without having to worry about quoting rules. Whenever possible,
857    it is recomended to pass |cmd| as a sequence.
858
859    When |cmd| is passed as a single string, |shell| should be set to True.
860    The command will be interpreted and run by the shell on the device,
861    allowing the use of shell features such as pipes, wildcards, or variables.
862    Failing to set shell=True will issue a warning, but this will be changed
863    to a hard failure in the future (see: catapult:#3242).
864
865    This behaviour is consistent with that of command runners in cmd_helper as
866    well as Python's own subprocess.Popen.
867
868    TODO(perezju) Change the default of |check_return| to True when callers
869      have switched to the new behaviour.
870
871    Args:
872      cmd: A sequence containing the command to run and its arguments, or a
873        string with a shell script to run (should also set shell=True).
874      shell: A boolean indicating whether shell features may be used in |cmd|.
875      check_return: A boolean indicating whether or not the return code should
876        be checked.
877      cwd: The device directory in which the command should be run.
878      env: The environment variables with which the command should be run.
879      run_as: A string containing the package as which the command should be
880        run.
881      as_root: A boolean indicating whether the shell command should be run
882        with root privileges.
883      single_line: A boolean indicating if only a single line of output is
884        expected.
885      large_output: Uses a work-around for large shell command output. Without
886        this large output will be truncated.
887      raw_output: Whether to only return the raw output
888          (no splitting into lines).
889      timeout: timeout in seconds
890      retries: number of retries
891
892    Returns:
893      If single_line is False, the output of the command as a list of lines,
894      otherwise, a string with the unique line of output emmited by the command
895      (with the optional newline at the end stripped).
896
897    Raises:
898      AdbCommandFailedError if check_return is True and the exit code of
899        the command run on the device is non-zero.
900      CommandFailedError if single_line is True but the output contains two or
901        more lines.
902      CommandTimeoutError on timeout.
903      DeviceUnreachableError on missing device.
904    """
905    def env_quote(key, value):
906      if not DeviceUtils._VALID_SHELL_VARIABLE.match(key):
907        raise KeyError('Invalid shell variable name %r' % key)
908      # using double quotes here to allow interpolation of shell variables
909      return '%s=%s' % (key, cmd_helper.DoubleQuote(value))
910
911    def run(cmd):
912      return self.adb.Shell(cmd)
913
914    def handle_check_return(cmd):
915      try:
916        return run(cmd)
917      except device_errors.AdbCommandFailedError as exc:
918        if check_return:
919          raise
920        else:
921          return exc.output
922
923    def handle_large_command(cmd):
924      if len(cmd) < self._MAX_ADB_COMMAND_LENGTH:
925        return handle_check_return(cmd)
926      else:
927        with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script:
928          self._WriteFileWithPush(script.name, cmd)
929          logger.info('Large shell command will be run from file: %s ...',
930                      cmd[:self._MAX_ADB_COMMAND_LENGTH])
931          return handle_check_return('sh %s' % script.name_quoted)
932
933    def handle_large_output(cmd, large_output_mode):
934      if large_output_mode:
935        with device_temp_file.DeviceTempFile(self.adb) as large_output_file:
936          cmd = '( %s )>%s' % (cmd, large_output_file.name)
937          logger.debug('Large output mode enabled. Will write output to '
938                       'device and read results from file.')
939          handle_large_command(cmd)
940          return self.ReadFile(large_output_file.name, force_pull=True)
941      else:
942        try:
943          return handle_large_command(cmd)
944        except device_errors.AdbCommandFailedError as exc:
945          if exc.status is None:
946            logger.error(_FormatPartialOutputError(exc.output))
947            logger.warning('Attempting to run in large_output mode.')
948            logger.warning('Use RunShellCommand(..., large_output=True) for '
949                           'shell commands that expect a lot of output.')
950            return handle_large_output(cmd, True)
951          else:
952            raise
953
954    if isinstance(cmd, basestring):
955      if not shell:
956        logging.warning(
957            'The command to run should preferably be passed as a sequence of'
958            ' args. If shell features are needed (pipes, wildcards, variables)'
959            ' clients should explicitly set shell=True.')
960    else:
961      cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd)
962    if env:
963      env = ' '.join(env_quote(k, v) for k, v in env.iteritems())
964      cmd = '%s %s' % (env, cmd)
965    if cwd:
966      cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd)
967    if run_as:
968      cmd = 'run-as %s sh -c %s' % (cmd_helper.SingleQuote(run_as),
969                                    cmd_helper.SingleQuote(cmd))
970    if as_root and self.NeedsSU():
971      # "su -c sh -c" allows using shell features in |cmd|
972      cmd = self._Su('sh -c %s' % cmd_helper.SingleQuote(cmd))
973
974    output = handle_large_output(cmd, large_output)
975
976    if raw_output:
977      return output
978
979    output = output.splitlines()
980    if single_line:
981      if not output:
982        return ''
983      elif len(output) == 1:
984        return output[0]
985      else:
986        msg = 'one line of output was expected, but got: %s'
987        raise device_errors.CommandFailedError(msg % output, str(self))
988    else:
989      return output
990
991  def _RunPipedShellCommand(self, script, **kwargs):
992    PIPESTATUS_LEADER = 'PIPESTATUS: '
993
994    script += '; echo "%s${PIPESTATUS[@]}"' % PIPESTATUS_LEADER
995    kwargs.update(shell=True, check_return=True)
996    output = self.RunShellCommand(script, **kwargs)
997    pipestatus_line = output[-1]
998
999    if not pipestatus_line.startswith(PIPESTATUS_LEADER):
1000      logger.error('Pipe exit statuses of shell script missing.')
1001      raise device_errors.AdbShellCommandFailedError(
1002          script, output, status=None,
1003          device_serial=self.serial)
1004
1005    output = output[:-1]
1006    statuses = [
1007        int(s) for s in pipestatus_line[len(PIPESTATUS_LEADER):].split()]
1008    if any(statuses):
1009      raise device_errors.AdbShellCommandFailedError(
1010          script, output, status=statuses,
1011          device_serial=self.serial)
1012    return output
1013
1014  @decorators.WithTimeoutAndRetriesFromInstance()
1015  def KillAll(self, process_name, exact=False, signum=device_signal.SIGKILL,
1016              as_root=False, blocking=False, quiet=False,
1017              timeout=None, retries=None):
1018    """Kill all processes with the given name on the device.
1019
1020    Args:
1021      process_name: A string containing the name of the process to kill.
1022      exact: A boolean indicating whether to kill all processes matching
1023             the string |process_name| exactly, or all of those which contain
1024             |process_name| as a substring. Defaults to False.
1025      signum: An integer containing the signal number to send to kill. Defaults
1026              to SIGKILL (9).
1027      as_root: A boolean indicating whether the kill should be executed with
1028               root privileges.
1029      blocking: A boolean indicating whether we should wait until all processes
1030                with the given |process_name| are dead.
1031      quiet: A boolean indicating whether to ignore the fact that no processes
1032             to kill were found.
1033      timeout: timeout in seconds
1034      retries: number of retries
1035
1036    Returns:
1037      The number of processes attempted to kill.
1038
1039    Raises:
1040      CommandFailedError if no process was killed and |quiet| is False.
1041      CommandTimeoutError on timeout.
1042      DeviceUnreachableError on missing device.
1043    """
1044    procs_pids = self.GetPids(process_name)
1045    if exact:
1046      procs_pids = {process_name: procs_pids.get(process_name, [])}
1047    pids = set(itertools.chain(*procs_pids.values()))
1048    if not pids:
1049      if quiet:
1050        return 0
1051      else:
1052        raise device_errors.CommandFailedError(
1053            'No process "%s"' % process_name, str(self))
1054
1055    logger.info(
1056        'KillAll(%r, ...) attempting to kill the following:', process_name)
1057    for name, ids in procs_pids.iteritems():
1058      for i in ids:
1059        logger.info('  %05s %s', str(i), name)
1060
1061    cmd = ['kill', '-%d' % signum] + sorted(pids)
1062    self.RunShellCommand(cmd, as_root=as_root, check_return=True)
1063
1064    def all_pids_killed():
1065      procs_pids_remain = self.GetPids(process_name)
1066      return not pids.intersection(itertools.chain(*procs_pids_remain.values()))
1067
1068    if blocking:
1069      timeout_retry.WaitFor(all_pids_killed, wait_period=0.1)
1070
1071    return len(pids)
1072
1073  @decorators.WithTimeoutAndRetriesFromInstance()
1074  def StartActivity(self, intent_obj, blocking=False, trace_file_name=None,
1075                    force_stop=False, timeout=None, retries=None):
1076    """Start package's activity on the device.
1077
1078    Args:
1079      intent_obj: An Intent object to send.
1080      blocking: A boolean indicating whether we should wait for the activity to
1081                finish launching.
1082      trace_file_name: If present, a string that both indicates that we want to
1083                       profile the activity and contains the path to which the
1084                       trace should be saved.
1085      force_stop: A boolean indicating whether we should stop the activity
1086                  before starting it.
1087      timeout: timeout in seconds
1088      retries: number of retries
1089
1090    Raises:
1091      CommandFailedError if the activity could not be started.
1092      CommandTimeoutError on timeout.
1093      DeviceUnreachableError on missing device.
1094    """
1095    cmd = ['am', 'start']
1096    if blocking:
1097      cmd.append('-W')
1098    if trace_file_name:
1099      cmd.extend(['--start-profiler', trace_file_name])
1100    if force_stop:
1101      cmd.append('-S')
1102    cmd.extend(intent_obj.am_args)
1103    for line in self.RunShellCommand(cmd, check_return=True):
1104      if line.startswith('Error:'):
1105        raise device_errors.CommandFailedError(line, str(self))
1106
1107  @decorators.WithTimeoutAndRetriesFromInstance()
1108  def StartInstrumentation(self, component, finish=True, raw=False,
1109                           extras=None, timeout=None, retries=None):
1110    if extras is None:
1111      extras = {}
1112
1113    cmd = ['am', 'instrument']
1114    if finish:
1115      cmd.append('-w')
1116    if raw:
1117      cmd.append('-r')
1118    for k, v in extras.iteritems():
1119      cmd.extend(['-e', str(k), str(v)])
1120    cmd.append(component)
1121
1122    # Store the package name in a shell variable to help the command stay under
1123    # the _MAX_ADB_COMMAND_LENGTH limit.
1124    package = component.split('/')[0]
1125    shell_snippet = 'p=%s;%s' % (package,
1126                                 cmd_helper.ShrinkToSnippet(cmd, 'p', package))
1127    return self.RunShellCommand(shell_snippet, shell=True, check_return=True,
1128                                large_output=True)
1129
1130  @decorators.WithTimeoutAndRetriesFromInstance()
1131  def BroadcastIntent(self, intent_obj, timeout=None, retries=None):
1132    """Send a broadcast intent.
1133
1134    Args:
1135      intent: An Intent to broadcast.
1136      timeout: timeout in seconds
1137      retries: number of retries
1138
1139    Raises:
1140      CommandTimeoutError on timeout.
1141      DeviceUnreachableError on missing device.
1142    """
1143    cmd = ['am', 'broadcast'] + intent_obj.am_args
1144    self.RunShellCommand(cmd, check_return=True)
1145
1146  @decorators.WithTimeoutAndRetriesFromInstance()
1147  def GoHome(self, timeout=None, retries=None):
1148    """Return to the home screen and obtain launcher focus.
1149
1150    This command launches the home screen and attempts to obtain
1151    launcher focus until the timeout is reached.
1152
1153    Args:
1154      timeout: timeout in seconds
1155      retries: number of retries
1156
1157    Raises:
1158      CommandTimeoutError on timeout.
1159      DeviceUnreachableError on missing device.
1160    """
1161    def is_launcher_focused():
1162      output = self.RunShellCommand(['dumpsys', 'window', 'windows'],
1163                                    check_return=True, large_output=True)
1164      return any(self._LAUNCHER_FOCUSED_RE.match(l) for l in output)
1165
1166    def dismiss_popups():
1167      # There is a dialog present; attempt to get rid of it.
1168      # Not all dialogs can be dismissed with back.
1169      self.SendKeyEvent(keyevent.KEYCODE_ENTER)
1170      self.SendKeyEvent(keyevent.KEYCODE_BACK)
1171      return is_launcher_focused()
1172
1173    # If Home is already focused, return early to avoid unnecessary work.
1174    if is_launcher_focused():
1175      return
1176
1177    self.StartActivity(
1178        intent.Intent(action='android.intent.action.MAIN',
1179                      category='android.intent.category.HOME'),
1180        blocking=True)
1181
1182    if not is_launcher_focused():
1183      timeout_retry.WaitFor(dismiss_popups, wait_period=1)
1184
1185  @decorators.WithTimeoutAndRetriesFromInstance()
1186  def ForceStop(self, package, timeout=None, retries=None):
1187    """Close the application.
1188
1189    Args:
1190      package: A string containing the name of the package to stop.
1191      timeout: timeout in seconds
1192      retries: number of retries
1193
1194    Raises:
1195      CommandTimeoutError on timeout.
1196      DeviceUnreachableError on missing device.
1197    """
1198    if self.GetPids(package):
1199      self.RunShellCommand(['am', 'force-stop', package], check_return=True)
1200
1201  @decorators.WithTimeoutAndRetriesFromInstance()
1202  def ClearApplicationState(
1203      self, package, permissions=None, timeout=None, retries=None):
1204    """Clear all state for the given package.
1205
1206    Args:
1207      package: A string containing the name of the package to stop.
1208      permissions: List of permissions to set after clearing data.
1209      timeout: timeout in seconds
1210      retries: number of retries
1211
1212    Raises:
1213      CommandTimeoutError on timeout.
1214      DeviceUnreachableError on missing device.
1215    """
1216    # Check that the package exists before clearing it for android builds below
1217    # JB MR2. Necessary because calling pm clear on a package that doesn't exist
1218    # may never return.
1219    if ((self.build_version_sdk >= version_codes.JELLY_BEAN_MR2)
1220        or self._GetApplicationPathsInternal(package)):
1221      self.RunShellCommand(['pm', 'clear', package], check_return=True)
1222      self.GrantPermissions(package, permissions)
1223
1224  @decorators.WithTimeoutAndRetriesFromInstance()
1225  def SendKeyEvent(self, keycode, timeout=None, retries=None):
1226    """Sends a keycode to the device.
1227
1228    See the devil.android.sdk.keyevent module for suitable keycode values.
1229
1230    Args:
1231      keycode: A integer keycode to send to the device.
1232      timeout: timeout in seconds
1233      retries: number of retries
1234
1235    Raises:
1236      CommandTimeoutError on timeout.
1237      DeviceUnreachableError on missing device.
1238    """
1239    self.RunShellCommand(['input', 'keyevent', format(keycode, 'd')],
1240                         check_return=True)
1241
1242  PUSH_CHANGED_FILES_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT
1243
1244  @decorators.WithTimeoutAndRetriesFromInstance(
1245      min_default_timeout=PUSH_CHANGED_FILES_DEFAULT_TIMEOUT)
1246  def PushChangedFiles(self, host_device_tuples, timeout=None,
1247                       retries=None, delete_device_stale=False):
1248    """Push files to the device, skipping files that don't need updating.
1249
1250    When a directory is pushed, it is traversed recursively on the host and
1251    all files in it are pushed to the device as needed.
1252    Additionally, if delete_device_stale option is True,
1253    files that exist on the device but don't exist on the host are deleted.
1254
1255    Args:
1256      host_device_tuples: A list of (host_path, device_path) tuples, where
1257        |host_path| is an absolute path of a file or directory on the host
1258        that should be minimially pushed to the device, and |device_path| is
1259        an absolute path of the destination on the device.
1260      timeout: timeout in seconds
1261      retries: number of retries
1262      delete_device_stale: option to delete stale files on device
1263
1264    Raises:
1265      CommandFailedError on failure.
1266      CommandTimeoutError on timeout.
1267      DeviceUnreachableError on missing device.
1268    """
1269
1270    all_changed_files = []
1271    all_stale_files = []
1272    missing_dirs = []
1273    cache_commit_funcs = []
1274    for h, d in host_device_tuples:
1275      assert os.path.isabs(h) and posixpath.isabs(d)
1276      h = os.path.realpath(h)
1277      changed_files, up_to_date_files, stale_files, cache_commit_func = (
1278          self._GetChangedAndStaleFiles(h, d, delete_device_stale))
1279      all_changed_files += changed_files
1280      all_stale_files += stale_files
1281      cache_commit_funcs.append(cache_commit_func)
1282      if changed_files and not up_to_date_files and not stale_files:
1283        if os.path.isdir(h):
1284          missing_dirs.append(d)
1285        else:
1286          missing_dirs.append(posixpath.dirname(d))
1287
1288    if delete_device_stale and all_stale_files:
1289      self.RunShellCommand(['rm', '-f'] + all_stale_files, check_return=True)
1290
1291    if all_changed_files:
1292      if missing_dirs:
1293        self.RunShellCommand(['mkdir', '-p'] + missing_dirs, check_return=True)
1294      self._PushFilesImpl(host_device_tuples, all_changed_files)
1295    for func in cache_commit_funcs:
1296      func()
1297
1298  def _GetChangedAndStaleFiles(self, host_path, device_path, track_stale=False):
1299    """Get files to push and delete
1300
1301    Args:
1302      host_path: an absolute path of a file or directory on the host
1303      device_path: an absolute path of a file or directory on the device
1304      track_stale: whether to bother looking for stale files (slower)
1305
1306    Returns:
1307      a four-element tuple
1308      1st element: a list of (host_files_path, device_files_path) tuples to push
1309      2nd element: a list of host_files_path that are up-to-date
1310      3rd element: a list of stale files under device_path, or [] when
1311        track_stale == False
1312      4th element: a cache commit function.
1313    """
1314    try:
1315      # Length calculations below assume no trailing /.
1316      host_path = host_path.rstrip('/')
1317      device_path = device_path.rstrip('/')
1318
1319      specific_device_paths = [device_path]
1320      ignore_other_files = not track_stale and os.path.isdir(host_path)
1321      if ignore_other_files:
1322        specific_device_paths = []
1323        for root, _, filenames in os.walk(host_path):
1324          relative_dir = root[len(host_path) + 1:]
1325          specific_device_paths.extend(
1326              posixpath.join(device_path, relative_dir, f) for f in filenames)
1327
1328      def calculate_host_checksums():
1329        return md5sum.CalculateHostMd5Sums([host_path])
1330
1331      def calculate_device_checksums():
1332        if self._enable_device_files_cache:
1333          cache_entry = self._cache['device_path_checksums'].get(device_path)
1334          if cache_entry and cache_entry[0] == ignore_other_files:
1335            return dict(cache_entry[1])
1336
1337        sums = md5sum.CalculateDeviceMd5Sums(specific_device_paths, self)
1338
1339        cache_entry = [ignore_other_files, sums]
1340        self._cache['device_path_checksums'][device_path] = cache_entry
1341        return dict(sums)
1342
1343      host_checksums, device_checksums = reraiser_thread.RunAsync((
1344          calculate_host_checksums,
1345          calculate_device_checksums))
1346    except EnvironmentError as e:
1347      logger.warning('Error calculating md5: %s', e)
1348      return ([(host_path, device_path)], [], [], lambda: 0)
1349
1350    to_push = []
1351    up_to_date = []
1352    to_delete = []
1353    if os.path.isfile(host_path):
1354      host_checksum = host_checksums.get(host_path)
1355      device_checksum = device_checksums.get(device_path)
1356      if host_checksum == device_checksum:
1357        up_to_date.append(host_path)
1358      else:
1359        to_push.append((host_path, device_path))
1360    else:
1361      for host_abs_path, host_checksum in host_checksums.iteritems():
1362        device_abs_path = posixpath.join(
1363            device_path, os.path.relpath(host_abs_path, host_path))
1364        device_checksum = device_checksums.pop(device_abs_path, None)
1365        if device_checksum == host_checksum:
1366          up_to_date.append(host_abs_path)
1367        else:
1368          to_push.append((host_abs_path, device_abs_path))
1369      to_delete = device_checksums.keys()
1370
1371    def cache_commit_func():
1372      new_sums = {posixpath.join(device_path, path[len(host_path) + 1:]): val
1373                  for path, val in host_checksums.iteritems()}
1374      cache_entry = [ignore_other_files, new_sums]
1375      self._cache['device_path_checksums'][device_path] = cache_entry
1376
1377    return (to_push, up_to_date, to_delete, cache_commit_func)
1378
1379  def _ComputeDeviceChecksumsForApks(self, package_name):
1380    ret = self._cache['package_apk_checksums'].get(package_name)
1381    if ret is None:
1382      device_paths = self._GetApplicationPathsInternal(package_name)
1383      file_to_checksums = md5sum.CalculateDeviceMd5Sums(device_paths, self)
1384      ret = set(file_to_checksums.values())
1385      self._cache['package_apk_checksums'][package_name] = ret
1386    return ret
1387
1388  def _ComputeStaleApks(self, package_name, host_apk_paths):
1389    def calculate_host_checksums():
1390      return md5sum.CalculateHostMd5Sums(host_apk_paths)
1391
1392    def calculate_device_checksums():
1393      return self._ComputeDeviceChecksumsForApks(package_name)
1394
1395    host_checksums, device_checksums = reraiser_thread.RunAsync((
1396        calculate_host_checksums, calculate_device_checksums))
1397    stale_apks = [k for (k, v) in host_checksums.iteritems()
1398                  if v not in device_checksums]
1399    return stale_apks, set(host_checksums.values())
1400
1401  def _PushFilesImpl(self, host_device_tuples, files):
1402    if not files:
1403      return
1404
1405    size = sum(host_utils.GetRecursiveDiskUsage(h) for h, _ in files)
1406    file_count = len(files)
1407    dir_size = sum(host_utils.GetRecursiveDiskUsage(h)
1408                   for h, _ in host_device_tuples)
1409    dir_file_count = 0
1410    for h, _ in host_device_tuples:
1411      if os.path.isdir(h):
1412        dir_file_count += sum(len(f) for _r, _d, f in os.walk(h))
1413      else:
1414        dir_file_count += 1
1415
1416    push_duration = self._ApproximateDuration(
1417        file_count, file_count, size, False)
1418    dir_push_duration = self._ApproximateDuration(
1419        len(host_device_tuples), dir_file_count, dir_size, False)
1420    zip_duration = self._ApproximateDuration(1, 1, size, True)
1421
1422    if (dir_push_duration < push_duration and dir_push_duration < zip_duration
1423        # TODO(jbudorick): Resume directory pushing once clients have switched
1424        # to 1.0.36-compatible syntax.
1425        and False):
1426      self._PushChangedFilesIndividually(host_device_tuples)
1427    elif push_duration < zip_duration:
1428      self._PushChangedFilesIndividually(files)
1429    elif self._commands_installed is False:
1430      # Already tried and failed to install unzip command.
1431      self._PushChangedFilesIndividually(files)
1432    elif not self._PushChangedFilesZipped(
1433        files, [d for _, d in host_device_tuples]):
1434      self._PushChangedFilesIndividually(files)
1435
1436  def _MaybeInstallCommands(self):
1437    if self._commands_installed is None:
1438      try:
1439        if not install_commands.Installed(self):
1440          install_commands.InstallCommands(self)
1441        self._commands_installed = True
1442      except device_errors.CommandFailedError as e:
1443        logger.warning('unzip not available: %s', str(e))
1444        self._commands_installed = False
1445    return self._commands_installed
1446
1447  @staticmethod
1448  def _ApproximateDuration(adb_calls, file_count, byte_count, is_zipping):
1449    # We approximate the time to push a set of files to a device as:
1450    #   t = c1 * a + c2 * f + c3 + b / c4 + b / (c5 * c6), where
1451    #     t: total time (sec)
1452    #     c1: adb call time delay (sec)
1453    #     a: number of times adb is called (unitless)
1454    #     c2: push time delay (sec)
1455    #     f: number of files pushed via adb (unitless)
1456    #     c3: zip time delay (sec)
1457    #     c4: zip rate (bytes/sec)
1458    #     b: total number of bytes (bytes)
1459    #     c5: transfer rate (bytes/sec)
1460    #     c6: compression ratio (unitless)
1461
1462    # All of these are approximations.
1463    ADB_CALL_PENALTY = 0.1  # seconds
1464    ADB_PUSH_PENALTY = 0.01  # seconds
1465    ZIP_PENALTY = 2.0  # seconds
1466    ZIP_RATE = 10000000.0  # bytes / second
1467    TRANSFER_RATE = 2000000.0  # bytes / second
1468    COMPRESSION_RATIO = 2.0  # unitless
1469
1470    adb_call_time = ADB_CALL_PENALTY * adb_calls
1471    adb_push_setup_time = ADB_PUSH_PENALTY * file_count
1472    if is_zipping:
1473      zip_time = ZIP_PENALTY + byte_count / ZIP_RATE
1474      transfer_time = byte_count / (TRANSFER_RATE * COMPRESSION_RATIO)
1475    else:
1476      zip_time = 0
1477      transfer_time = byte_count / TRANSFER_RATE
1478    return adb_call_time + adb_push_setup_time + zip_time + transfer_time
1479
1480  def _PushChangedFilesIndividually(self, files):
1481    for h, d in files:
1482      self.adb.Push(h, d)
1483
1484  def _PushChangedFilesZipped(self, files, dirs):
1485    with tempfile.NamedTemporaryFile(suffix='.zip') as zip_file:
1486      zip_proc = multiprocessing.Process(
1487          target=DeviceUtils._CreateDeviceZip,
1488          args=(zip_file.name, files))
1489      zip_proc.start()
1490      try:
1491        # While it's zipping, ensure the unzip command exists on the device.
1492        if not self._MaybeInstallCommands():
1493          zip_proc.terminate()
1494          return False
1495
1496        # Warm up NeedsSU cache while we're still zipping.
1497        self.NeedsSU()
1498        with device_temp_file.DeviceTempFile(
1499            self.adb, suffix='.zip') as device_temp:
1500          zip_proc.join()
1501          self.adb.Push(zip_file.name, device_temp.name)
1502          quoted_dirs = ' '.join(cmd_helper.SingleQuote(d) for d in dirs)
1503          self.RunShellCommand(
1504              'unzip %s&&chmod -R 777 %s' % (device_temp.name, quoted_dirs),
1505              shell=True, as_root=True,
1506              env={'PATH': '%s:$PATH' % install_commands.BIN_DIR},
1507              check_return=True)
1508      finally:
1509        if zip_proc.is_alive():
1510          zip_proc.terminate()
1511    return True
1512
1513  @staticmethod
1514  def _CreateDeviceZip(zip_path, host_device_tuples):
1515    with zipfile.ZipFile(zip_path, 'w') as zip_file:
1516      for host_path, device_path in host_device_tuples:
1517        zip_utils.WriteToZipFile(zip_file, host_path, device_path)
1518
1519  # TODO(nednguyen): remove this and migrate the callsite to PathExists().
1520  @decorators.WithTimeoutAndRetriesFromInstance()
1521  def FileExists(self, device_path, timeout=None, retries=None):
1522    """Checks whether the given file exists on the device.
1523
1524    Arguments are the same as PathExists.
1525    """
1526    return self.PathExists(device_path, timeout=timeout, retries=retries)
1527
1528  @decorators.WithTimeoutAndRetriesFromInstance()
1529  def PathExists(self, device_paths, as_root=False, timeout=None, retries=None):
1530    """Checks whether the given path(s) exists on the device.
1531
1532    Args:
1533      device_path: A string containing the absolute path to the file on the
1534                   device, or an iterable of paths to check.
1535      as_root: Whether root permissions should be use to check for the existence
1536               of the given path(s).
1537      timeout: timeout in seconds
1538      retries: number of retries
1539
1540    Returns:
1541      True if the all given paths exist on the device, False otherwise.
1542
1543    Raises:
1544      CommandTimeoutError on timeout.
1545      DeviceUnreachableError on missing device.
1546    """
1547    paths = device_paths
1548    if isinstance(paths, basestring):
1549      paths = (paths,)
1550    if not paths:
1551      return True
1552    cmd = ['test', '-e', paths[0]]
1553    for p in paths[1:]:
1554      cmd.extend(['-a', '-e', p])
1555    try:
1556      self.RunShellCommand(cmd, as_root=as_root, check_return=True,
1557                           timeout=timeout, retries=retries)
1558      return True
1559    except device_errors.CommandFailedError:
1560      return False
1561
1562  @decorators.WithTimeoutAndRetriesFromInstance()
1563  def RemovePath(self, device_path, force=False, recursive=False,
1564                 as_root=False, timeout=None, retries=None):
1565    """Removes the given path(s) from the device.
1566
1567    Args:
1568      device_path: A string containing the absolute path to the file on the
1569                   device, or an iterable of paths to check.
1570      force: Whether to remove the path(s) with force (-f).
1571      recursive: Whether to remove any directories in the path(s) recursively.
1572      as_root: Whether root permissions should be use to remove the given
1573               path(s).
1574      timeout: timeout in seconds
1575      retries: number of retries
1576    """
1577    args = ['rm']
1578    if force:
1579      args.append('-f')
1580    if recursive:
1581      args.append('-r')
1582    if isinstance(device_path, basestring):
1583      args.append(device_path)
1584    else:
1585      args.extend(device_path)
1586    self.RunShellCommand(args, as_root=as_root, check_return=True)
1587
1588
1589  @decorators.WithTimeoutAndRetriesFromInstance()
1590  def PullFile(self, device_path, host_path, timeout=None, retries=None):
1591    """Pull a file from the device.
1592
1593    Args:
1594      device_path: A string containing the absolute path of the file to pull
1595                   from the device.
1596      host_path: A string containing the absolute path of the destination on
1597                 the host.
1598      timeout: timeout in seconds
1599      retries: number of retries
1600
1601    Raises:
1602      CommandFailedError on failure.
1603      CommandTimeoutError on timeout.
1604    """
1605    # Create the base dir if it doesn't exist already
1606    dirname = os.path.dirname(host_path)
1607    if dirname and not os.path.exists(dirname):
1608      os.makedirs(dirname)
1609    self.adb.Pull(device_path, host_path)
1610
1611  def _ReadFileWithPull(self, device_path):
1612    try:
1613      d = tempfile.mkdtemp()
1614      host_temp_path = os.path.join(d, 'tmp_ReadFileWithPull')
1615      self.adb.Pull(device_path, host_temp_path)
1616      with open(host_temp_path, 'r') as host_temp:
1617        return host_temp.read()
1618    finally:
1619      if os.path.exists(d):
1620        shutil.rmtree(d)
1621
1622  @decorators.WithTimeoutAndRetriesFromInstance()
1623  def ReadFile(self, device_path, as_root=False, force_pull=False,
1624               timeout=None, retries=None):
1625    """Reads the contents of a file from the device.
1626
1627    Args:
1628      device_path: A string containing the absolute path of the file to read
1629                   from the device.
1630      as_root: A boolean indicating whether the read should be executed with
1631               root privileges.
1632      force_pull: A boolean indicating whether to force the operation to be
1633          performed by pulling a file from the device. The default is, when the
1634          contents are short, to retrieve the contents using cat instead.
1635      timeout: timeout in seconds
1636      retries: number of retries
1637
1638    Returns:
1639      The contents of |device_path| as a string. Contents are intepreted using
1640      universal newlines, so the caller will see them encoded as '\n'. Also,
1641      all lines will be terminated.
1642
1643    Raises:
1644      AdbCommandFailedError if the file can't be read.
1645      CommandTimeoutError on timeout.
1646      DeviceUnreachableError on missing device.
1647    """
1648    def get_size(path):
1649      return self.FileSize(path, as_root=as_root)
1650
1651    if (not force_pull
1652        and 0 < get_size(device_path) <= self._MAX_ADB_OUTPUT_LENGTH):
1653      return _JoinLines(self.RunShellCommand(
1654          ['cat', device_path], as_root=as_root, check_return=True))
1655    elif as_root and self.NeedsSU():
1656      with device_temp_file.DeviceTempFile(self.adb) as device_temp:
1657        cmd = 'SRC=%s DEST=%s;cp "$SRC" "$DEST" && chmod 666 "$DEST"' % (
1658            cmd_helper.SingleQuote(device_path),
1659            cmd_helper.SingleQuote(device_temp.name))
1660        self.RunShellCommand(cmd, shell=True, as_root=True, check_return=True)
1661        return self._ReadFileWithPull(device_temp.name)
1662    else:
1663      return self._ReadFileWithPull(device_path)
1664
1665  def _WriteFileWithPush(self, device_path, contents):
1666    with tempfile.NamedTemporaryFile() as host_temp:
1667      host_temp.write(contents)
1668      host_temp.flush()
1669      self.adb.Push(host_temp.name, device_path)
1670
1671  @decorators.WithTimeoutAndRetriesFromInstance()
1672  def WriteFile(self, device_path, contents, as_root=False, force_push=False,
1673                timeout=None, retries=None):
1674    """Writes |contents| to a file on the device.
1675
1676    Args:
1677      device_path: A string containing the absolute path to the file to write
1678          on the device.
1679      contents: A string containing the data to write to the device.
1680      as_root: A boolean indicating whether the write should be executed with
1681          root privileges (if available).
1682      force_push: A boolean indicating whether to force the operation to be
1683          performed by pushing a file to the device. The default is, when the
1684          contents are short, to pass the contents using a shell script instead.
1685      timeout: timeout in seconds
1686      retries: number of retries
1687
1688    Raises:
1689      CommandFailedError if the file could not be written on the device.
1690      CommandTimeoutError on timeout.
1691      DeviceUnreachableError on missing device.
1692    """
1693    if not force_push and len(contents) < self._MAX_ADB_COMMAND_LENGTH:
1694      # If the contents are small, for efficieny we write the contents with
1695      # a shell command rather than pushing a file.
1696      cmd = 'echo -n %s > %s' % (cmd_helper.SingleQuote(contents),
1697                                 cmd_helper.SingleQuote(device_path))
1698      self.RunShellCommand(cmd, shell=True, as_root=as_root, check_return=True)
1699    elif as_root and self.NeedsSU():
1700      # Adb does not allow to "push with su", so we first push to a temp file
1701      # on a safe location, and then copy it to the desired location with su.
1702      with device_temp_file.DeviceTempFile(self.adb) as device_temp:
1703        self._WriteFileWithPush(device_temp.name, contents)
1704        # Here we need 'cp' rather than 'mv' because the temp and
1705        # destination files might be on different file systems (e.g.
1706        # on internal storage and an external sd card).
1707        self.RunShellCommand(['cp', device_temp.name, device_path],
1708                             as_root=True, check_return=True)
1709    else:
1710      # If root is not needed, we can push directly to the desired location.
1711      self._WriteFileWithPush(device_path, contents)
1712
1713  def _ParseLongLsOutput(self, device_path, as_root=False, **kwargs):
1714    """Run and scrape the output of 'ls -a -l' on a device directory."""
1715    device_path = posixpath.join(device_path, '')  # Force trailing '/'.
1716    output = self.RunShellCommand(
1717        ['ls', '-a', '-l', device_path], as_root=as_root,
1718        check_return=True, env={'TZ': 'utc'}, **kwargs)
1719    if output and output[0].startswith('total '):
1720      output.pop(0) # pylint: disable=maybe-no-member
1721
1722    entries = []
1723    for line in output:
1724      m = _LONG_LS_OUTPUT_RE.match(line)
1725      if m:
1726        if m.group('filename') not in ['.', '..']:
1727          entries.append(m.groupdict())
1728      else:
1729        logger.info('Skipping: %s', line)
1730
1731    return entries
1732
1733  def ListDirectory(self, device_path, as_root=False, **kwargs):
1734    """List all files on a device directory.
1735
1736    Mirroring os.listdir (and most client expectations) the resulting list
1737    does not include the special entries '.' and '..' even if they are present
1738    in the directory.
1739
1740    Args:
1741      device_path: A string containing the path of the directory on the device
1742                   to list.
1743      as_root: A boolean indicating whether the to use root privileges to list
1744               the directory contents.
1745      timeout: timeout in seconds
1746      retries: number of retries
1747
1748    Returns:
1749      A list of filenames for all entries contained in the directory.
1750
1751    Raises:
1752      AdbCommandFailedError if |device_path| does not specify a valid and
1753          accessible directory in the device.
1754      CommandTimeoutError on timeout.
1755      DeviceUnreachableError on missing device.
1756    """
1757    entries = self._ParseLongLsOutput(device_path, as_root=as_root, **kwargs)
1758    return [d['filename'] for d in entries]
1759
1760  def StatDirectory(self, device_path, as_root=False, **kwargs):
1761    """List file and stat info for all entries on a device directory.
1762
1763    Implementation notes: this is currently implemented by parsing the output
1764    of 'ls -a -l' on the device. Whether possible and convenient, we attempt to
1765    make parsing strict and return values mirroring those of the standard |os|
1766    and |stat| Python modules.
1767
1768    Mirroring os.listdir (and most client expectations) the resulting list
1769    does not include the special entries '.' and '..' even if they are present
1770    in the directory.
1771
1772    Args:
1773      device_path: A string containing the path of the directory on the device
1774                   to list.
1775      as_root: A boolean indicating whether the to use root privileges to list
1776               the directory contents.
1777      timeout: timeout in seconds
1778      retries: number of retries
1779
1780    Returns:
1781      A list of dictionaries, each containing the following keys:
1782        filename: A string with the file name.
1783        st_mode: File permissions, use the stat module to interpret these.
1784        st_nlink: Number of hard links (may be missing).
1785        st_owner: A string with the user name of the owner.
1786        st_group: A string with the group name of the owner.
1787        st_rdev_pair: Device type as (major, minior) (only if inode device).
1788        st_size: Size of file, in bytes (may be missing for non-regular files).
1789        st_mtime: Time of most recent modification, in seconds since epoch
1790          (although resolution is in minutes).
1791        symbolic_link_to: If entry is a symbolic link, path where it points to;
1792          missing otherwise.
1793
1794    Raises:
1795      AdbCommandFailedError if |device_path| does not specify a valid and
1796          accessible directory in the device.
1797      CommandTimeoutError on timeout.
1798      DeviceUnreachableError on missing device.
1799    """
1800    entries = self._ParseLongLsOutput(device_path, as_root=as_root, **kwargs)
1801    for d in entries:
1802      for key, value in d.items():
1803        if value is None:
1804          del d[key]  # Remove missing fields.
1805      d['st_mode'] = _ParseModeString(d['st_mode'])
1806      d['st_mtime'] = calendar.timegm(
1807          time.strptime(d['st_mtime'], _LS_DATE_FORMAT))
1808      for key in ['st_nlink', 'st_size', 'st_rdev_major', 'st_rdev_minor']:
1809        if key in d:
1810          d[key] = int(d[key])
1811      if 'st_rdev_major' in d and 'st_rdev_minor' in d:
1812        d['st_rdev_pair'] = (d.pop('st_rdev_major'), d.pop('st_rdev_minor'))
1813    return entries
1814
1815  def StatPath(self, device_path, as_root=False, **kwargs):
1816    """Get the stat attributes of a file or directory on the device.
1817
1818    Args:
1819      device_path: A string containing the path of a file or directory from
1820                   which to get attributes.
1821      as_root: A boolean indicating whether the to use root privileges to
1822               access the file information.
1823      timeout: timeout in seconds
1824      retries: number of retries
1825
1826    Returns:
1827      A dictionary with the stat info collected; see StatDirectory for details.
1828
1829    Raises:
1830      CommandFailedError if device_path cannot be found on the device.
1831      CommandTimeoutError on timeout.
1832      DeviceUnreachableError on missing device.
1833    """
1834    dirname, filename = posixpath.split(posixpath.normpath(device_path))
1835    for entry in self.StatDirectory(dirname, as_root=as_root, **kwargs):
1836      if entry['filename'] == filename:
1837        return entry
1838    raise device_errors.CommandFailedError(
1839        'Cannot find file or directory: %r' % device_path, str(self))
1840
1841  def FileSize(self, device_path, as_root=False, **kwargs):
1842    """Get the size of a file on the device.
1843
1844    Note: This is implemented by parsing the output of the 'ls' command on
1845    the device. On some Android versions, when passing a directory or special
1846    file, the size is *not* reported and this function will throw an exception.
1847
1848    Args:
1849      device_path: A string containing the path of a file on the device.
1850      as_root: A boolean indicating whether the to use root privileges to
1851               access the file information.
1852      timeout: timeout in seconds
1853      retries: number of retries
1854
1855    Returns:
1856      The size of the file in bytes.
1857
1858    Raises:
1859      CommandFailedError if device_path cannot be found on the device, or
1860        its size cannot be determited for some reason.
1861      CommandTimeoutError on timeout.
1862      DeviceUnreachableError on missing device.
1863    """
1864    entry = self.StatPath(device_path, as_root=as_root, **kwargs)
1865    try:
1866      return entry['st_size']
1867    except KeyError:
1868      raise device_errors.CommandFailedError(
1869          'Could not determine the size of: %s' % device_path, str(self))
1870
1871  @decorators.WithTimeoutAndRetriesFromInstance()
1872  def SetJavaAsserts(self, enabled, timeout=None, retries=None):
1873    """Enables or disables Java asserts.
1874
1875    Args:
1876      enabled: A boolean indicating whether Java asserts should be enabled
1877               or disabled.
1878      timeout: timeout in seconds
1879      retries: number of retries
1880
1881    Returns:
1882      True if the device-side property changed and a restart is required as a
1883      result, False otherwise.
1884
1885    Raises:
1886      CommandTimeoutError on timeout.
1887    """
1888    def find_property(lines, property_name):
1889      for index, line in enumerate(lines):
1890        if line.strip() == '':
1891          continue
1892        key_value = tuple(s.strip() for s in line.split('=', 1))
1893        if len(key_value) != 2:
1894          continue
1895        key, value = key_value
1896        if key == property_name:
1897          return index, value
1898      return None, ''
1899
1900    new_value = 'all' if enabled else ''
1901
1902    # First ensure the desired property is persisted.
1903    try:
1904      properties = self.ReadFile(self.LOCAL_PROPERTIES_PATH).splitlines()
1905    except device_errors.CommandFailedError:
1906      properties = []
1907    index, value = find_property(properties, self.JAVA_ASSERT_PROPERTY)
1908    if new_value != value:
1909      if new_value:
1910        new_line = '%s=%s' % (self.JAVA_ASSERT_PROPERTY, new_value)
1911        if index is None:
1912          properties.append(new_line)
1913        else:
1914          properties[index] = new_line
1915      else:
1916        assert index is not None  # since new_value == '' and new_value != value
1917        properties.pop(index)
1918      self.WriteFile(self.LOCAL_PROPERTIES_PATH, _JoinLines(properties))
1919
1920    # Next, check the current runtime value is what we need, and
1921    # if not, set it and report that a reboot is required.
1922    value = self.GetProp(self.JAVA_ASSERT_PROPERTY)
1923    if new_value != value:
1924      self.SetProp(self.JAVA_ASSERT_PROPERTY, new_value)
1925      return True
1926    else:
1927      return False
1928
1929  def GetLanguage(self, cache=False):
1930    """Returns the language setting on the device.
1931    Args:
1932      cache: Whether to use cached properties when available.
1933    """
1934    return self.GetProp('persist.sys.language', cache=cache)
1935
1936  def GetCountry(self, cache=False):
1937    """Returns the country setting on the device.
1938
1939    Args:
1940      cache: Whether to use cached properties when available.
1941    """
1942    return self.GetProp('persist.sys.country', cache=cache)
1943
1944  @property
1945  def screen_density(self):
1946    """Returns the screen density of the device."""
1947    DPI_TO_DENSITY = {
1948      120: 'ldpi',
1949      160: 'mdpi',
1950      240: 'hdpi',
1951      320: 'xhdpi',
1952      480: 'xxhdpi',
1953      640: 'xxxhdpi',
1954    }
1955    return DPI_TO_DENSITY.get(self.pixel_density, 'tvdpi')
1956
1957  @property
1958  def pixel_density(self):
1959    return int(self.GetProp('ro.sf.lcd_density', cache=True))
1960
1961  @property
1962  def build_description(self):
1963    """Returns the build description of the system.
1964
1965    For example:
1966      nakasi-user 4.4.4 KTU84P 1227136 release-keys
1967    """
1968    return self.GetProp('ro.build.description', cache=True)
1969
1970  @property
1971  def build_fingerprint(self):
1972    """Returns the build fingerprint of the system.
1973
1974    For example:
1975      google/nakasi/grouper:4.4.4/KTU84P/1227136:user/release-keys
1976    """
1977    return self.GetProp('ro.build.fingerprint', cache=True)
1978
1979  @property
1980  def build_id(self):
1981    """Returns the build ID of the system (e.g. 'KTU84P')."""
1982    return self.GetProp('ro.build.id', cache=True)
1983
1984  @property
1985  def build_product(self):
1986    """Returns the build product of the system (e.g. 'grouper')."""
1987    return self.GetProp('ro.build.product', cache=True)
1988
1989  @property
1990  def build_type(self):
1991    """Returns the build type of the system (e.g. 'user')."""
1992    return self.GetProp('ro.build.type', cache=True)
1993
1994  @property
1995  def build_version_sdk(self):
1996    """Returns the build version sdk of the system as a number (e.g. 19).
1997
1998    For version code numbers see:
1999    http://developer.android.com/reference/android/os/Build.VERSION_CODES.html
2000
2001    For named constants see devil.android.sdk.version_codes
2002
2003    Raises:
2004      CommandFailedError if the build version sdk is not a number.
2005    """
2006    value = self.GetProp('ro.build.version.sdk', cache=True)
2007    try:
2008      return int(value)
2009    except ValueError:
2010      raise device_errors.CommandFailedError(
2011          'Invalid build version sdk: %r' % value)
2012
2013  @property
2014  def product_cpu_abi(self):
2015    """Returns the product cpu abi of the device (e.g. 'armeabi-v7a')."""
2016    return self.GetProp('ro.product.cpu.abi', cache=True)
2017
2018  @property
2019  def product_model(self):
2020    """Returns the name of the product model (e.g. 'Nexus 7')."""
2021    return self.GetProp('ro.product.model', cache=True)
2022
2023  @property
2024  def product_name(self):
2025    """Returns the product name of the device (e.g. 'nakasi')."""
2026    return self.GetProp('ro.product.name', cache=True)
2027
2028  @property
2029  def product_board(self):
2030    """Returns the product board name of the device (e.g. 'shamu')."""
2031    return self.GetProp('ro.product.board', cache=True)
2032
2033  def _EnsureCacheInitialized(self):
2034    """Populates cache token, runs getprop and fetches $EXTERNAL_STORAGE."""
2035    if self._cache['token']:
2036      return
2037    with self._cache_lock:
2038      if self._cache['token']:
2039        return
2040      # Change the token every time to ensure that it will match only the
2041      # previously dumped cache.
2042      token = str(uuid.uuid1())
2043      cmd = (
2044          'c=/data/local/tmp/cache_token;'
2045          'echo $EXTERNAL_STORAGE;'
2046          'cat $c 2>/dev/null||echo;'
2047          'echo "%s">$c &&' % token +
2048          'getprop'
2049      )
2050      output = self.RunShellCommand(
2051          cmd, shell=True, check_return=True, large_output=True)
2052      # Error-checking for this existing is done in GetExternalStoragePath().
2053      self._cache['external_storage'] = output[0]
2054      self._cache['prev_token'] = output[1]
2055      output = output[2:]
2056
2057      prop_cache = self._cache['getprop']
2058      prop_cache.clear()
2059      for key, value in _GETPROP_RE.findall(''.join(output)):
2060        prop_cache[key] = value
2061      self._cache['token'] = token
2062
2063  @decorators.WithTimeoutAndRetriesFromInstance()
2064  def GetProp(self, property_name, cache=False, timeout=None, retries=None):
2065    """Gets a property from the device.
2066
2067    Args:
2068      property_name: A string containing the name of the property to get from
2069                     the device.
2070      cache: Whether to use cached properties when available.
2071      timeout: timeout in seconds
2072      retries: number of retries
2073
2074    Returns:
2075      The value of the device's |property_name| property.
2076
2077    Raises:
2078      CommandTimeoutError on timeout.
2079    """
2080    assert isinstance(property_name, basestring), (
2081        "property_name is not a string: %r" % property_name)
2082
2083    if cache:
2084      # It takes ~120ms to query a single property, and ~130ms to query all
2085      # properties. So, when caching we always query all properties.
2086      self._EnsureCacheInitialized()
2087    else:
2088      # timeout and retries are handled down at run shell, because we don't
2089      # want to apply them in the other branch when reading from the cache
2090      value = self.RunShellCommand(
2091          ['getprop', property_name], single_line=True, check_return=True,
2092          timeout=timeout, retries=retries)
2093      self._cache['getprop'][property_name] = value
2094    # Non-existent properties are treated as empty strings by getprop.
2095    return self._cache['getprop'].get(property_name, '')
2096
2097  @decorators.WithTimeoutAndRetriesFromInstance()
2098  def SetProp(self, property_name, value, check=False, timeout=None,
2099              retries=None):
2100    """Sets a property on the device.
2101
2102    Args:
2103      property_name: A string containing the name of the property to set on
2104                     the device.
2105      value: A string containing the value to set to the property on the
2106             device.
2107      check: A boolean indicating whether to check that the property was
2108             successfully set on the device.
2109      timeout: timeout in seconds
2110      retries: number of retries
2111
2112    Raises:
2113      CommandFailedError if check is true and the property was not correctly
2114        set on the device (e.g. because it is not rooted).
2115      CommandTimeoutError on timeout.
2116    """
2117    assert isinstance(property_name, basestring), (
2118        "property_name is not a string: %r" % property_name)
2119    assert isinstance(value, basestring), "value is not a string: %r" % value
2120
2121    self.RunShellCommand(['setprop', property_name, value], check_return=True)
2122    prop_cache = self._cache['getprop']
2123    if property_name in prop_cache:
2124      del prop_cache[property_name]
2125    # TODO(perezju) remove the option and make the check mandatory, but using a
2126    # single shell script to both set- and getprop.
2127    if check and value != self.GetProp(property_name, cache=False):
2128      raise device_errors.CommandFailedError(
2129          'Unable to set property %r on the device to %r'
2130          % (property_name, value), str(self))
2131
2132  @decorators.WithTimeoutAndRetriesFromInstance()
2133  def GetABI(self, timeout=None, retries=None):
2134    """Gets the device main ABI.
2135
2136    Args:
2137      timeout: timeout in seconds
2138      retries: number of retries
2139
2140    Returns:
2141      The device's main ABI name.
2142
2143    Raises:
2144      CommandTimeoutError on timeout.
2145    """
2146    return self.GetProp('ro.product.cpu.abi', cache=True)
2147
2148  @decorators.WithTimeoutAndRetriesFromInstance()
2149  def GetPids(self, process_name=None, timeout=None, retries=None):
2150    """Returns the PIDs of processes containing the given name as substring.
2151
2152    Note that the |process_name| is often the package name.
2153
2154    Args:
2155      process_name: A string containing the process name to get the PIDs for.
2156                    If missing returns PIDs for all processes.
2157      timeout: timeout in seconds
2158      retries: number of retries
2159
2160    Returns:
2161      A dict mapping process name to a list of PIDs for each process that
2162      contained the provided |process_name|.
2163
2164    Raises:
2165      CommandTimeoutError on timeout.
2166      DeviceUnreachableError on missing device.
2167    """
2168    procs_pids = collections.defaultdict(list)
2169    try:
2170      ps_cmd = 'ps'
2171      # ps behavior was changed in Android above N, http://crbug.com/686716
2172      if (self.build_version_sdk >= version_codes.NOUGAT_MR1
2173          and self.build_id[0] > 'N'):
2174        ps_cmd = 'ps -e'
2175      if process_name:
2176        ps_output = self._RunPipedShellCommand(
2177            '%s | grep -F %s' % (ps_cmd, cmd_helper.SingleQuote(process_name)))
2178      else:
2179        ps_output = self.RunShellCommand(
2180            ps_cmd.split(), check_return=True, large_output=True)
2181    except device_errors.AdbShellCommandFailedError as e:
2182      if e.status and isinstance(e.status, list) and not e.status[0]:
2183        # If ps succeeded but grep failed, there were no processes with the
2184        # given name.
2185        return procs_pids
2186      else:
2187        raise
2188
2189    process_name = process_name or ''
2190    for line in ps_output:
2191      try:
2192        ps_data = line.split()
2193        pid, process = ps_data[1], ps_data[-1]
2194        if process_name in process and pid != 'PID':
2195          procs_pids[process].append(pid)
2196      except IndexError:
2197        pass
2198    return procs_pids
2199
2200  def GetApplicationPids(self, process_name, at_most_one=False, **kwargs):
2201    """Returns the PID or PIDs of a given process name.
2202
2203    Note that the |process_name|, often the package name, must match exactly.
2204
2205    Args:
2206      process_name: A string containing the process name to get the PIDs for.
2207      at_most_one: A boolean indicating that at most one PID is expected to
2208                   be found.
2209      timeout: timeout in seconds
2210      retries: number of retries
2211
2212    Returns:
2213      A list of the PIDs for the named process. If at_most_one=True returns
2214      the single PID found or None otherwise.
2215
2216    Raises:
2217      CommandFailedError if at_most_one=True and more than one PID is found
2218          for the named process.
2219      CommandTimeoutError on timeout.
2220      DeviceUnreachableError on missing device.
2221    """
2222    pids = self.GetPids(process_name, **kwargs).get(process_name, [])
2223    if at_most_one:
2224      if len(pids) > 1:
2225        raise device_errors.CommandFailedError(
2226            'Expected a single process but found PIDs: %s.' % ', '.join(pids),
2227            device_serial=str(self))
2228      return pids[0] if pids else None
2229    else:
2230      return pids
2231
2232  @decorators.WithTimeoutAndRetriesFromInstance()
2233  def GetEnforce(self, timeout=None, retries=None):
2234    """Get the current mode of SELinux.
2235
2236    Args:
2237      timeout: timeout in seconds
2238      retries: number of retries
2239
2240    Returns:
2241      True (enforcing), False (permissive), or None (disabled).
2242
2243    Raises:
2244      CommandFailedError on failure.
2245      CommandTimeoutError on timeout.
2246      DeviceUnreachableError on missing device.
2247    """
2248    output = self.RunShellCommand(
2249        ['getenforce'], check_return=True, single_line=True).lower()
2250    if output not in _SELINUX_MODE:
2251      raise device_errors.CommandFailedError(
2252          'Unexpected getenforce output: %s' % output)
2253    return _SELINUX_MODE[output]
2254
2255  @decorators.WithTimeoutAndRetriesFromInstance()
2256  def SetEnforce(self, enabled, timeout=None, retries=None):
2257    """Modify the mode SELinux is running in.
2258
2259    Args:
2260      enabled: a boolean indicating whether to put SELinux in encorcing mode
2261               (if True), or permissive mode (otherwise).
2262      timeout: timeout in seconds
2263      retries: number of retries
2264
2265    Raises:
2266      CommandFailedError on failure.
2267      CommandTimeoutError on timeout.
2268      DeviceUnreachableError on missing device.
2269    """
2270    self.RunShellCommand(
2271        ['setenforce', '1' if int(enabled) else '0'], as_root=True,
2272        check_return=True)
2273
2274  @decorators.WithTimeoutAndRetriesFromInstance()
2275  def TakeScreenshot(self, host_path=None, timeout=None, retries=None):
2276    """Takes a screenshot of the device.
2277
2278    Args:
2279      host_path: A string containing the path on the host to save the
2280                 screenshot to. If None, a file name in the current
2281                 directory will be generated.
2282      timeout: timeout in seconds
2283      retries: number of retries
2284
2285    Returns:
2286      The name of the file on the host to which the screenshot was saved.
2287
2288    Raises:
2289      CommandFailedError on failure.
2290      CommandTimeoutError on timeout.
2291      DeviceUnreachableError on missing device.
2292    """
2293    if not host_path:
2294      host_path = os.path.abspath('screenshot-%s-%s.png' % (
2295          self.serial, _GetTimeStamp()))
2296    with device_temp_file.DeviceTempFile(self.adb, suffix='.png') as device_tmp:
2297      self.RunShellCommand(['/system/bin/screencap', '-p', device_tmp.name],
2298                           check_return=True)
2299      self.PullFile(device_tmp.name, host_path)
2300    return host_path
2301
2302  @decorators.WithTimeoutAndRetriesFromInstance()
2303  def GetMemoryUsageForPid(self, pid, timeout=None, retries=None):
2304    """Gets the memory usage for the given PID.
2305
2306    Args:
2307      pid: PID of the process.
2308      timeout: timeout in seconds
2309      retries: number of retries
2310
2311    Returns:
2312      A dict containing memory usage statistics for the PID. May include:
2313        Size, Rss, Pss, Shared_Clean, Shared_Dirty, Private_Clean,
2314        Private_Dirty, VmHWM
2315
2316    Raises:
2317      CommandTimeoutError on timeout.
2318    """
2319    result = collections.defaultdict(int)
2320
2321    try:
2322      result.update(self._GetMemoryUsageForPidFromSmaps(pid))
2323    except device_errors.CommandFailedError:
2324      logger.exception('Error getting memory usage from smaps')
2325
2326    try:
2327      result.update(self._GetMemoryUsageForPidFromStatus(pid))
2328    except device_errors.CommandFailedError:
2329      logger.exception('Error getting memory usage from status')
2330
2331    return result
2332
2333  @decorators.WithTimeoutAndRetriesFromInstance()
2334  def DismissCrashDialogIfNeeded(self, timeout=None, retries=None):
2335    """Dismiss the error/ANR dialog if present.
2336
2337    Returns: Name of the crashed package if a dialog is focused,
2338             None otherwise.
2339    """
2340    def _FindFocusedWindow():
2341      match = None
2342      # TODO(jbudorick): Try to grep the output on the device instead of using
2343      # large_output if/when DeviceUtils exposes a public interface for piped
2344      # shell command handling.
2345      for line in self.RunShellCommand(['dumpsys', 'window', 'windows'],
2346                                       check_return=True, large_output=True):
2347        match = re.match(_CURRENT_FOCUS_CRASH_RE, line)
2348        if match:
2349          break
2350      return match
2351
2352    match = _FindFocusedWindow()
2353    if not match:
2354      return None
2355    package = match.group(2)
2356    logger.warning('Trying to dismiss %s dialog for %s', *match.groups())
2357    self.SendKeyEvent(keyevent.KEYCODE_DPAD_RIGHT)
2358    self.SendKeyEvent(keyevent.KEYCODE_DPAD_RIGHT)
2359    self.SendKeyEvent(keyevent.KEYCODE_ENTER)
2360    match = _FindFocusedWindow()
2361    if match:
2362      logger.error('Still showing a %s dialog for %s', *match.groups())
2363    return package
2364
2365  def _GetMemoryUsageForPidFromSmaps(self, pid):
2366    SMAPS_COLUMNS = (
2367        'Size', 'Rss', 'Pss', 'Shared_Clean', 'Shared_Dirty', 'Private_Clean',
2368        'Private_Dirty')
2369
2370    showmap_out = self._RunPipedShellCommand(
2371        'showmap %d | grep TOTAL' % int(pid), as_root=True)
2372
2373    split_totals = showmap_out[-1].split()
2374    if (not split_totals
2375        or len(split_totals) != 9
2376        or split_totals[-1] != 'TOTAL'):
2377      raise device_errors.CommandFailedError(
2378          'Invalid output from showmap: %s' % '\n'.join(showmap_out))
2379
2380    return dict(itertools.izip(SMAPS_COLUMNS, (int(n) for n in split_totals)))
2381
2382  def _GetMemoryUsageForPidFromStatus(self, pid):
2383    for line in self.ReadFile(
2384        '/proc/%s/status' % str(pid), as_root=True).splitlines():
2385      if line.startswith('VmHWM:'):
2386        return {'VmHWM': int(line.split()[1])}
2387    raise device_errors.CommandFailedError(
2388        'Could not find memory peak value for pid %s', str(pid))
2389
2390  def GetLogcatMonitor(self, *args, **kwargs):
2391    """Returns a new LogcatMonitor associated with this device.
2392
2393    Parameters passed to this function are passed directly to
2394    |logcat_monitor.LogcatMonitor| and are documented there.
2395    """
2396    return logcat_monitor.LogcatMonitor(self.adb, *args, **kwargs)
2397
2398  def GetClientCache(self, client_name):
2399    """Returns client cache."""
2400    if client_name not in self._client_caches:
2401      self._client_caches[client_name] = {}
2402    return self._client_caches[client_name]
2403
2404  def _ClearCache(self):
2405    """Clears all caches."""
2406    for client in self._client_caches:
2407      self._client_caches[client].clear()
2408    self._cache = {
2409        # Map of packageId -> list of on-device .apk paths
2410        'package_apk_paths': {},
2411        # Set of packageId that were loaded from LoadCacheData and not yet
2412        # verified.
2413        'package_apk_paths_to_verify': set(),
2414        # Map of packageId -> set of on-device .apk checksums
2415        'package_apk_checksums': {},
2416        # Map of property_name -> value
2417        'getprop': {},
2418        # Map of device_path -> [ignore_other_files, map of path->checksum]
2419        'device_path_checksums': {},
2420        # Location of sdcard ($EXTERNAL_STORAGE).
2421        'external_storage': None,
2422        # Token used to detect when LoadCacheData is stale.
2423        'token': None,
2424        'prev_token': None,
2425    }
2426
2427  @decorators.WithTimeoutAndRetriesFromInstance()
2428  def LoadCacheData(self, data, timeout=None, retries=None):
2429    """Initializes the cache from data created using DumpCacheData.
2430
2431    The cache is used only if its token matches the one found on the device.
2432    This prevents a stale cache from being used (which can happen when sharing
2433    devices).
2434
2435    Args:
2436      data: A previously serialized cache (string).
2437      timeout: timeout in seconds
2438      retries: number of retries
2439
2440    Returns:
2441      Whether the cache was loaded.
2442    """
2443    obj = json.loads(data)
2444    self._EnsureCacheInitialized()
2445    given_token = obj.get('token')
2446    if not given_token or self._cache['prev_token'] != given_token:
2447      logger.warning('Stale cache detected. Not using it.')
2448      return False
2449
2450    self._cache['package_apk_paths'] = obj.get('package_apk_paths', {})
2451    # When using a cache across script invokations, verify that apps have
2452    # not been uninstalled.
2453    self._cache['package_apk_paths_to_verify'] = set(
2454        self._cache['package_apk_paths'].iterkeys())
2455
2456    package_apk_checksums = obj.get('package_apk_checksums', {})
2457    for k, v in package_apk_checksums.iteritems():
2458      package_apk_checksums[k] = set(v)
2459    self._cache['package_apk_checksums'] = package_apk_checksums
2460    device_path_checksums = obj.get('device_path_checksums', {})
2461    self._cache['device_path_checksums'] = device_path_checksums
2462    return True
2463
2464  @decorators.WithTimeoutAndRetriesFromInstance()
2465  def DumpCacheData(self, timeout=None, retries=None):
2466    """Dumps the current cache state to a string.
2467
2468    Args:
2469      timeout: timeout in seconds
2470      retries: number of retries
2471
2472    Returns:
2473      A serialized cache as a string.
2474    """
2475    self._EnsureCacheInitialized()
2476    obj = {}
2477    obj['token'] = self._cache['token']
2478    obj['package_apk_paths'] = self._cache['package_apk_paths']
2479    obj['package_apk_checksums'] = self._cache['package_apk_checksums']
2480    # JSON can't handle sets.
2481    for k, v in obj['package_apk_checksums'].iteritems():
2482      obj['package_apk_checksums'][k] = list(v)
2483    obj['device_path_checksums'] = self._cache['device_path_checksums']
2484    return json.dumps(obj, separators=(',', ':'))
2485
2486  @classmethod
2487  def parallel(cls, devices, async=False):
2488    """Creates a Parallelizer to operate over the provided list of devices.
2489
2490    Args:
2491      devices: A list of either DeviceUtils instances or objects from
2492               from which DeviceUtils instances can be constructed. If None,
2493               all attached devices will be used.
2494      async: If true, returns a Parallelizer that runs operations
2495             asynchronously.
2496
2497    Returns:
2498      A Parallelizer operating over |devices|.
2499    """
2500    devices = [d if isinstance(d, cls) else cls(d) for d in devices]
2501    if async:
2502      return parallelizer.Parallelizer(devices)
2503    else:
2504      return parallelizer.SyncParallelizer(devices)
2505
2506  @classmethod
2507  def HealthyDevices(cls, blacklist=None, device_arg='default', **kwargs):
2508    """Returns a list of DeviceUtils instances.
2509
2510    Returns a list of DeviceUtils instances that are attached, not blacklisted,
2511    and optionally filtered by --device flags or ANDROID_SERIAL environment
2512    variable.
2513
2514    Args:
2515      blacklist: A DeviceBlacklist instance (optional). Device serials in this
2516          blacklist will never be returned, but a warning will be logged if they
2517          otherwise would have been.
2518      device_arg: The value of the --device flag. This can be:
2519          'default' -> Same as [], but returns an empty list rather than raise a
2520              NoDevicesError.
2521          [] -> Returns all devices, unless $ANDROID_SERIAL is set.
2522          None -> Use $ANDROID_SERIAL if set, otherwise looks for a single
2523              attached device. Raises an exception if multiple devices are
2524              attached.
2525          'serial' -> Returns an instance for the given serial, if not
2526              blacklisted.
2527          ['A', 'B', ...] -> Returns instances for the subset that is not
2528              blacklisted.
2529      A device serial, or a list of device serials (optional).
2530
2531    Returns:
2532      A list of DeviceUtils instances.
2533
2534    Raises:
2535      NoDevicesError: Raised when no non-blacklisted devices exist and
2536          device_arg is passed.
2537      MultipleDevicesError: Raise when multiple devices exist, but |device_arg|
2538          is None.
2539    """
2540    allow_no_devices = False
2541    if device_arg == 'default':
2542      allow_no_devices = True
2543      device_arg = ()
2544
2545    select_multiple = True
2546    if not (isinstance(device_arg, tuple) or isinstance(device_arg, list)):
2547      select_multiple = False
2548      if device_arg:
2549        device_arg = (device_arg,)
2550
2551    blacklisted_devices = blacklist.Read() if blacklist else []
2552
2553    # adb looks for ANDROID_SERIAL, so support it as well.
2554    android_serial = os.environ.get('ANDROID_SERIAL')
2555    if not device_arg and android_serial:
2556      device_arg = (android_serial,)
2557
2558    def blacklisted(serial):
2559      if serial in blacklisted_devices:
2560        logger.warning('Device %s is blacklisted.', serial)
2561        return True
2562      return False
2563
2564    if device_arg:
2565      devices = [cls(x, **kwargs) for x in device_arg if not blacklisted(x)]
2566    else:
2567      devices = []
2568      for adb in adb_wrapper.AdbWrapper.Devices():
2569        if not blacklisted(adb.GetDeviceSerial()):
2570          devices.append(cls(_CreateAdbWrapper(adb), **kwargs))
2571
2572    if len(devices) == 0 and not allow_no_devices:
2573      raise device_errors.NoDevicesError()
2574    if len(devices) > 1 and not select_multiple:
2575      raise device_errors.MultipleDevicesError(devices)
2576    return sorted(devices)
2577
2578  @decorators.WithTimeoutAndRetriesFromInstance()
2579  def RestartAdbd(self, timeout=None, retries=None):
2580    logger.info('Restarting adbd on device.')
2581    with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script:
2582      self.WriteFile(script.name, _RESTART_ADBD_SCRIPT)
2583      self.RunShellCommand(
2584          ['source', script.name], check_return=True, as_root=True)
2585      self.adb.WaitForDevice()
2586
2587  @decorators.WithTimeoutAndRetriesFromInstance()
2588  def GrantPermissions(self, package, permissions, timeout=None, retries=None):
2589    # Permissions only need to be set on M and above because of the changes to
2590    # the permission model.
2591    if not permissions or self.build_version_sdk < version_codes.MARSHMALLOW:
2592      return
2593    logger.info('Setting permissions for %s.', package)
2594    permissions = [p for p in permissions if p not in _PERMISSIONS_BLACKLIST]
2595    if ('android.permission.WRITE_EXTERNAL_STORAGE' in permissions
2596        and 'android.permission.READ_EXTERNAL_STORAGE' not in permissions):
2597      permissions.append('android.permission.READ_EXTERNAL_STORAGE')
2598    cmd = '&&'.join('pm grant %s %s' % (package, p) for p in permissions)
2599    if cmd:
2600      output = self.RunShellCommand(cmd, shell=True, check_return=True)
2601      if output:
2602        logger.warning('Possible problem when granting permissions. Blacklist '
2603                       'may need to be updated.')
2604        for line in output:
2605          logger.warning('  %s', line)
2606
2607  @decorators.WithTimeoutAndRetriesFromInstance()
2608  def IsScreenOn(self, timeout=None, retries=None):
2609    """Determines if screen is on.
2610
2611    Dumpsys input_method exposes screen on/off state. Below is an explination of
2612    the states.
2613
2614    Pre-L:
2615      On: mScreenOn=true
2616      Off: mScreenOn=false
2617    L+:
2618      On: mInteractive=true
2619      Off: mInteractive=false
2620
2621    Returns:
2622      True if screen is on, false if it is off.
2623
2624    Raises:
2625      device_errors.CommandFailedError: If screen state cannot be found.
2626    """
2627    if self.build_version_sdk < version_codes.LOLLIPOP:
2628      input_check = 'mScreenOn'
2629      check_value = 'mScreenOn=true'
2630    else:
2631      input_check = 'mInteractive'
2632      check_value = 'mInteractive=true'
2633    dumpsys_out = self._RunPipedShellCommand(
2634        'dumpsys input_method | grep %s' % input_check)
2635    if not dumpsys_out:
2636      raise device_errors.CommandFailedError(
2637          'Unable to detect screen state', str(self))
2638    return check_value in dumpsys_out[0]
2639
2640  @decorators.WithTimeoutAndRetriesFromInstance()
2641  def SetScreen(self, on, timeout=None, retries=None):
2642    """Turns screen on and off.
2643
2644    Args:
2645      on: bool to decide state to switch to. True = on False = off.
2646    """
2647    def screen_test():
2648      return self.IsScreenOn() == on
2649
2650    if screen_test():
2651      logger.info('Screen already in expected state.')
2652      return
2653    self.SendKeyEvent(keyevent.KEYCODE_POWER)
2654    timeout_retry.WaitFor(screen_test, wait_period=1)
2655