• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2013 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"""This module wraps Android's adb tool.
6
7This is a thin wrapper around the adb interface. Any additional complexity
8should be delegated to a higher level (ex. DeviceUtils).
9"""
10
11import collections
12import distutils.version
13import errno
14import logging
15import os
16import posixpath
17import re
18import subprocess
19
20from devil import devil_env
21from devil.android import decorators
22from devil.android import device_errors
23from devil.utils import cmd_helper
24from devil.utils import lazy
25from devil.utils import timeout_retry
26
27with devil_env.SysPath(devil_env.DEPENDENCY_MANAGER_PATH):
28  import dependency_manager  # pylint: disable=import-error
29
30
31ADB_KEYS_FILE = '/data/misc/adb/adb_keys'
32
33DEFAULT_TIMEOUT = 30
34DEFAULT_RETRIES = 2
35
36_ADB_VERSION_RE = re.compile(r'Android Debug Bridge version (\d+\.\d+\.\d+)')
37_EMULATOR_RE = re.compile(r'^emulator-[0-9]+$')
38_READY_STATE = 'device'
39_VERITY_DISABLE_RE = re.compile('Verity (already)? disabled')
40_VERITY_ENABLE_RE = re.compile('Verity (already)? enabled')
41
42
43def VerifyLocalFileExists(path):
44  """Verifies a local file exists.
45
46  Args:
47    path: Path to the local file.
48
49  Raises:
50    IOError: If the file doesn't exist.
51  """
52  if not os.path.exists(path):
53    raise IOError(errno.ENOENT, os.strerror(errno.ENOENT), path)
54
55
56def _FindAdb():
57  try:
58    return devil_env.config.LocalPath('adb')
59  except dependency_manager.NoPathFoundError:
60    pass
61
62  try:
63    return os.path.join(devil_env.config.LocalPath('android_sdk'),
64                        'platform-tools', 'adb')
65  except dependency_manager.NoPathFoundError:
66    pass
67
68  try:
69    return devil_env.config.FetchPath('adb')
70  except dependency_manager.NoPathFoundError:
71    raise device_errors.NoAdbError()
72
73
74def _GetVersion():
75  # pylint: disable=protected-access
76  raw_version = AdbWrapper._RunAdbCmd(['version'], timeout=2, retries=0)
77  for l in raw_version.splitlines():
78    m = _ADB_VERSION_RE.search(l)
79    if m:
80      return m.group(1)
81  return None
82
83
84def _ShouldRetryAdbCmd(exc):
85  return not isinstance(exc, device_errors.NoAdbError)
86
87
88DeviceStat = collections.namedtuple('DeviceStat',
89                                    ['st_mode', 'st_size', 'st_time'])
90
91
92def _IsExtraneousLine(line, send_cmd):
93  """Determine if a line read from stdout in persistent shell is extraneous.
94
95  The results output to stdout by the persistent shell process
96  (in PersistentShell below) often include "extraneous" lines that are
97  not part of the output of the shell command. These "extraneous" lines
98  do not always appear and are of two forms: shell prompt lines and lines
99  that just duplicate what the input command was. This function
100  detects these extraneous lines. Since all these lines have the
101  original command in them, that is what it detects ror.
102
103  Args:
104      line: Output line to check.
105      send_cmd: Command that was sent to adb persistent shell.
106  """
107  return send_cmd.rstrip() in line
108
109
110class AdbWrapper(object):
111  """A wrapper around a local Android Debug Bridge executable."""
112
113  _adb_path = lazy.WeakConstant(_FindAdb)
114  _adb_version = lazy.WeakConstant(_GetVersion)
115
116  def __init__(self, device_serial):
117    """Initializes the AdbWrapper.
118
119    Args:
120      device_serial: The device serial number as a string.
121    """
122    if not device_serial:
123      raise ValueError('A device serial must be specified')
124    self._device_serial = str(device_serial)
125
126  class PersistentShell(object):
127    '''Class to use persistent shell for ADB.
128
129    This class allows a persistent ADB shell to be created, where multiple
130    commands can be passed into it. This avoids the overhead of starting
131    up a new ADB shell for each command.
132
133    Example of use:
134    with PersistentShell('123456789') as pshell:
135        pshell.RunCommand('which ls')
136        pshell.RunCommandAndClose('echo TEST')
137    '''
138    def __init__(self, serial):
139      """Initialization function:
140
141      Args:
142        serial: Serial number of device.
143      """
144      self._cmd = [AdbWrapper.GetAdbPath(), '-s', serial, 'shell']
145      self._process = None
146
147    def __enter__(self):
148      self.Start()
149      self.WaitForReady()
150      return self
151
152    def __exit__(self, exc_type, exc_value, tb):
153      self.Stop()
154
155    def Start(self):
156      """Start the shell."""
157      if self._process is not None:
158        raise RuntimeError('Persistent shell already running.')
159      self._process = subprocess.Popen(self._cmd,
160                                       stdin=subprocess.PIPE,
161                                       stdout=subprocess.PIPE,
162                                       shell=False)
163
164    def WaitForReady(self):
165      """Wait for the shell to be ready after starting.
166
167      Sends an echo command, then waits until it gets a response.
168      """
169      self._process.stdin.write('echo\n')
170      output_line = self._process.stdout.readline()
171      while output_line.rstrip() != '':
172        output_line = self._process.stdout.readline()
173
174    def RunCommand(self, command, close=False):
175      """Runs an ADB command and returns the output.
176
177      Note that there can be approximately 40 ms of additional latency
178      between sending the command and receiving the results if close=False
179      due to the use of Nagle's algorithm in the TCP socket between the
180      adb server and client. To avoid this extra latency, set close=True.
181
182      Args:
183        command: Command to send.
184      Returns:
185        The command output, given as a list of lines, and the exit code
186      """
187
188      if close:
189        def run_cmd(cmd):
190          send_cmd = '( %s ); echo $?; exit;\n' % cmd.rstrip()
191          (output, _) = self._process.communicate(send_cmd)
192          self._process = None
193          for x in output.splitlines():
194            yield x
195
196      else:
197        def run_cmd(cmd):
198          send_cmd = '( %s ); echo DONE:$?;\n' % cmd.rstrip()
199          self._process.stdin.write(send_cmd)
200          while True:
201            output_line = self._process.stdout.readline().rstrip()
202            if output_line[:5] == 'DONE:':
203              yield output_line[5:]
204              break
205            yield output_line
206
207      result = [line for line in run_cmd(command)
208                if not _IsExtraneousLine(line, command)]
209
210      return (result[:-1], int(result[-1]))
211
212    def Stop(self):
213      """Stops the ADB process if it is still running."""
214      if self._process is not None:
215        self._process.stdin.write('exit\n')
216        self._process = None
217
218  @classmethod
219  def GetAdbPath(cls):
220    return cls._adb_path.read()
221
222  @classmethod
223  def Version(cls):
224    return cls._adb_version.read()
225
226  @classmethod
227  def _BuildAdbCmd(cls, args, device_serial, cpu_affinity=None):
228    if cpu_affinity is not None:
229      cmd = ['taskset', '-c', str(cpu_affinity)]
230    else:
231      cmd = []
232    cmd.append(cls.GetAdbPath())
233    if device_serial is not None:
234      cmd.extend(['-s', device_serial])
235    cmd.extend(args)
236    return cmd
237
238  # pylint: disable=unused-argument
239  @classmethod
240  @decorators.WithTimeoutAndConditionalRetries(_ShouldRetryAdbCmd)
241  def _RunAdbCmd(cls, args, timeout=None, retries=None, device_serial=None,
242                 check_error=True, cpu_affinity=None):
243    # pylint: disable=no-member
244    try:
245      status, output = cmd_helper.GetCmdStatusAndOutputWithTimeout(
246          cls._BuildAdbCmd(args, device_serial, cpu_affinity=cpu_affinity),
247          timeout_retry.CurrentTimeoutThreadGroup().GetRemainingTime())
248    except OSError as e:
249      if e.errno in (errno.ENOENT, errno.ENOEXEC):
250        raise device_errors.NoAdbError(msg=str(e))
251      else:
252        raise
253
254    if status != 0:
255      raise device_errors.AdbCommandFailedError(
256          args, output, status, device_serial)
257    # This catches some errors, including when the device drops offline;
258    # unfortunately adb is very inconsistent with error reporting so many
259    # command failures present differently.
260    if check_error and output.startswith('error:'):
261      raise device_errors.AdbCommandFailedError(args, output)
262    return output
263  # pylint: enable=unused-argument
264
265  def _RunDeviceAdbCmd(self, args, timeout, retries, check_error=True):
266    """Runs an adb command on the device associated with this object.
267
268    Args:
269      args: A list of arguments to adb.
270      timeout: Timeout in seconds.
271      retries: Number of retries.
272      check_error: Check that the command doesn't return an error message. This
273        does NOT check the exit status of shell commands.
274
275    Returns:
276      The output of the command.
277    """
278    return self._RunAdbCmd(args, timeout=timeout, retries=retries,
279                           device_serial=self._device_serial,
280                           check_error=check_error)
281
282  def _IterRunDeviceAdbCmd(self, args, timeout):
283    """Runs an adb command and returns an iterator over its output lines.
284
285    Args:
286      args: A list of arguments to adb.
287      timeout: Timeout in seconds.
288
289    Yields:
290      The output of the command line by line.
291    """
292    return cmd_helper.IterCmdOutputLines(
293      self._BuildAdbCmd(args, self._device_serial), timeout=timeout)
294
295  def __eq__(self, other):
296    """Consider instances equal if they refer to the same device.
297
298    Args:
299      other: The instance to compare equality with.
300
301    Returns:
302      True if the instances are considered equal, false otherwise.
303    """
304    return self._device_serial == str(other)
305
306  def __str__(self):
307    """The string representation of an instance.
308
309    Returns:
310      The device serial number as a string.
311    """
312    return self._device_serial
313
314  def __repr__(self):
315    return '%s(\'%s\')' % (self.__class__.__name__, self)
316
317  # pylint: disable=unused-argument
318  @classmethod
319  def IsServerOnline(cls):
320    status, output = cmd_helper.GetCmdStatusAndOutput(['pgrep', 'adb'])
321    output = [int(x) for x in output.split()]
322    logging.info('PIDs for adb found: %r', output)
323    return status == 0
324  # pylint: enable=unused-argument
325
326  @classmethod
327  def KillServer(cls, timeout=DEFAULT_TIMEOUT, retries=DEFAULT_RETRIES):
328    cls._RunAdbCmd(['kill-server'], timeout=timeout, retries=retries)
329
330  @classmethod
331  def StartServer(cls, timeout=DEFAULT_TIMEOUT, retries=DEFAULT_RETRIES):
332    # CPU affinity is used to reduce adb instability http://crbug.com/268450
333    cls._RunAdbCmd(['start-server'], timeout=timeout, retries=retries,
334                   cpu_affinity=0)
335
336  @classmethod
337  def GetDevices(cls, timeout=DEFAULT_TIMEOUT, retries=DEFAULT_RETRIES):
338    """DEPRECATED. Refer to Devices(...) below."""
339    # TODO(jbudorick): Remove this function once no more clients are using it.
340    return cls.Devices(timeout=timeout, retries=retries)
341
342  @classmethod
343  def Devices(cls, desired_state=_READY_STATE, long_list=False,
344              timeout=DEFAULT_TIMEOUT, retries=DEFAULT_RETRIES):
345    """Get the list of active attached devices.
346
347    Args:
348      desired_state: If not None, limit the devices returned to only those
349        in the given state.
350      long_list: Whether to use the long listing format.
351      timeout: (optional) Timeout per try in seconds.
352      retries: (optional) Number of retries to attempt.
353
354    Yields:
355      AdbWrapper instances.
356    """
357    lines = cls._RawDevices(long_list=long_list, timeout=timeout,
358                            retries=retries)
359    if long_list:
360      return [
361        [AdbWrapper(line[0])] + line[1:]
362        for line in lines
363        if (len(line) >= 2 and (not desired_state or line[1] == desired_state))
364      ]
365    else:
366      return [
367        AdbWrapper(line[0])
368        for line in lines
369        if (len(line) == 2 and (not desired_state or line[1] == desired_state))
370      ]
371
372  @classmethod
373  def _RawDevices(cls, long_list=False, timeout=DEFAULT_TIMEOUT,
374                  retries=DEFAULT_RETRIES):
375    cmd = ['devices']
376    if long_list:
377      cmd.append('-l')
378    output = cls._RunAdbCmd(cmd, timeout=timeout, retries=retries)
379    return [line.split() for line in output.splitlines()[1:]]
380
381  def GetDeviceSerial(self):
382    """Gets the device serial number associated with this object.
383
384    Returns:
385      Device serial number as a string.
386    """
387    return self._device_serial
388
389  def Push(self, local, remote, timeout=60 * 5, retries=DEFAULT_RETRIES):
390    """Pushes a file from the host to the device.
391
392    Args:
393      local: Path on the host filesystem.
394      remote: Path on the device filesystem.
395      timeout: (optional) Timeout per try in seconds.
396      retries: (optional) Number of retries to attempt.
397    """
398    VerifyLocalFileExists(local)
399
400    if (distutils.version.LooseVersion(self.Version()) <
401        distutils.version.LooseVersion('1.0.36')):
402
403      # Different versions of adb handle pushing a directory to an existing
404      # directory differently.
405
406      # In the version packaged with the M SDK, 1.0.32, the following push:
407      #   foo/bar -> /sdcard/foo/bar
408      # where bar is an existing directory both on the host and the device
409      # results in the contents of bar/ on the host being pushed to bar/ on
410      # the device, i.e.
411      #   foo/bar/A -> /sdcard/foo/bar/A
412      #   foo/bar/B -> /sdcard/foo/bar/B
413      #   ... etc.
414
415      # In the version packaged with the N SDK, 1.0.36, the same push under
416      # the same conditions results in a second bar/ directory being created
417      # underneath the first bar/ directory on the device, i.e.
418      #   foo/bar/A -> /sdcard/foo/bar/bar/A
419      #   foo/bar/B -> /sdcard/foo/bar/bar/B
420      #   ... etc.
421
422      # In order to provide a consistent interface to clients, we check whether
423      # the target is an existing directory on the device and, if so, modifies
424      # the target passed to adb to emulate the behavior on 1.0.36 and above.
425
426      # Note that this behavior may have started before 1.0.36; that's simply
427      # the earliest version we've confirmed thus far.
428
429      try:
430        self.Shell('test -d %s' % remote, timeout=timeout, retries=retries)
431        remote = posixpath.join(remote, posixpath.basename(local))
432      except device_errors.AdbShellCommandFailedError:
433        # The target directory doesn't exist on the device, so we can use it
434        # without modification.
435        pass
436
437    self._RunDeviceAdbCmd(['push', local, remote], timeout, retries)
438
439  def Pull(self, remote, local, timeout=60 * 5, retries=DEFAULT_RETRIES):
440    """Pulls a file from the device to the host.
441
442    Args:
443      remote: Path on the device filesystem.
444      local: Path on the host filesystem.
445      timeout: (optional) Timeout per try in seconds.
446      retries: (optional) Number of retries to attempt.
447    """
448    cmd = ['pull', remote, local]
449    self._RunDeviceAdbCmd(cmd, timeout, retries)
450    try:
451      VerifyLocalFileExists(local)
452    except IOError:
453      raise device_errors.AdbCommandFailedError(
454          cmd, 'File not found on host: %s' % local, device_serial=str(self))
455
456  def Shell(self, command, expect_status=0, timeout=DEFAULT_TIMEOUT,
457            retries=DEFAULT_RETRIES):
458    """Runs a shell command on the device.
459
460    Args:
461      command: A string with the shell command to run.
462      expect_status: (optional) Check that the command's exit status matches
463        this value. Default is 0. If set to None the test is skipped.
464      timeout: (optional) Timeout per try in seconds.
465      retries: (optional) Number of retries to attempt.
466
467    Returns:
468      The output of the shell command as a string.
469
470    Raises:
471      device_errors.AdbCommandFailedError: If the exit status doesn't match
472        |expect_status|.
473    """
474    if expect_status is None:
475      args = ['shell', command]
476    else:
477      args = ['shell', '( %s );echo %%$?' % command.rstrip()]
478    output = self._RunDeviceAdbCmd(args, timeout, retries, check_error=False)
479    if expect_status is not None:
480      output_end = output.rfind('%')
481      if output_end < 0:
482        # causes the status string to become empty and raise a ValueError
483        output_end = len(output)
484
485      try:
486        status = int(output[output_end + 1:])
487      except ValueError:
488        logging.warning('exit status of shell command %r missing.', command)
489        raise device_errors.AdbShellCommandFailedError(
490            command, output, status=None, device_serial=self._device_serial)
491      output = output[:output_end]
492      if status != expect_status:
493        raise device_errors.AdbShellCommandFailedError(
494            command, output, status=status, device_serial=self._device_serial)
495    return output
496
497  def IterShell(self, command, timeout):
498    """Runs a shell command and returns an iterator over its output lines.
499
500    Args:
501      command: A string with the shell command to run.
502      timeout: Timeout in seconds.
503
504    Yields:
505      The output of the command line by line.
506    """
507    args = ['shell', command]
508    return cmd_helper.IterCmdOutputLines(
509      self._BuildAdbCmd(args, self._device_serial), timeout=timeout)
510
511  def Ls(self, path, timeout=DEFAULT_TIMEOUT, retries=DEFAULT_RETRIES):
512    """List the contents of a directory on the device.
513
514    Args:
515      path: Path on the device filesystem.
516      timeout: (optional) Timeout per try in seconds.
517      retries: (optional) Number of retries to attempt.
518
519    Returns:
520      A list of pairs (filename, stat) for each file found in the directory,
521      where the stat object has the properties: st_mode, st_size, and st_time.
522
523    Raises:
524      AdbCommandFailedError if |path| does not specify a valid and accessible
525          directory in the device, or the output of "adb ls" command is less
526          than four columns
527    """
528    def ParseLine(line, cmd):
529      cols = line.split(None, 3)
530      if len(cols) < 4:
531        raise device_errors.AdbCommandFailedError(
532            cmd, line, "the output should be 4 columns, but is only %d columns"
533            % len(cols), device_serial=self._device_serial)
534      filename = cols.pop()
535      stat = DeviceStat(*[int(num, base=16) for num in cols])
536      return (filename, stat)
537
538    cmd = ['ls', path]
539    lines = self._RunDeviceAdbCmd(
540        cmd, timeout=timeout, retries=retries).splitlines()
541    if lines:
542      return [ParseLine(line, cmd) for line in lines]
543    else:
544      raise device_errors.AdbCommandFailedError(
545          cmd, 'path does not specify an accessible directory in the device',
546          device_serial=self._device_serial)
547
548  def Logcat(self, clear=False, dump=False, filter_specs=None,
549             logcat_format=None, ring_buffer=None, timeout=None,
550             retries=DEFAULT_RETRIES):
551    """Get an iterable over the logcat output.
552
553    Args:
554      clear: If true, clear the logcat.
555      dump: If true, dump the current logcat contents.
556      filter_specs: If set, a list of specs to filter the logcat.
557      logcat_format: If set, the format in which the logcat should be output.
558        Options include "brief", "process", "tag", "thread", "raw", "time",
559        "threadtime", and "long"
560      ring_buffer: If set, a list of alternate ring buffers to request.
561        Options include "main", "system", "radio", "events", "crash" or "all".
562        The default is equivalent to ["main", "system", "crash"].
563      timeout: (optional) If set, timeout per try in seconds. If clear or dump
564        is set, defaults to DEFAULT_TIMEOUT.
565      retries: (optional) If clear or dump is set, the number of retries to
566        attempt. Otherwise, does nothing.
567
568    Yields:
569      logcat output line by line.
570    """
571    cmd = ['logcat']
572    use_iter = True
573    if clear:
574      cmd.append('-c')
575      use_iter = False
576    if dump:
577      cmd.append('-d')
578      use_iter = False
579    if logcat_format:
580      cmd.extend(['-v', logcat_format])
581    if ring_buffer:
582      for buffer_name in ring_buffer:
583        cmd.extend(['-b', buffer_name])
584    if filter_specs:
585      cmd.extend(filter_specs)
586
587    if use_iter:
588      return self._IterRunDeviceAdbCmd(cmd, timeout)
589    else:
590      timeout = timeout if timeout is not None else DEFAULT_TIMEOUT
591      return self._RunDeviceAdbCmd(cmd, timeout, retries).splitlines()
592
593  def Forward(self, local, remote, allow_rebind=False,
594              timeout=DEFAULT_TIMEOUT, retries=DEFAULT_RETRIES):
595    """Forward socket connections from the local socket to the remote socket.
596
597    Sockets are specified by one of:
598      tcp:<port>
599      localabstract:<unix domain socket name>
600      localreserved:<unix domain socket name>
601      localfilesystem:<unix domain socket name>
602      dev:<character device name>
603      jdwp:<process pid> (remote only)
604
605    Args:
606      local: The host socket.
607      remote: The device socket.
608      allow_rebind: A boolean indicating whether adb may rebind a local socket;
609        otherwise, the default, an exception is raised if the local socket is
610        already being forwarded.
611      timeout: (optional) Timeout per try in seconds.
612      retries: (optional) Number of retries to attempt.
613    """
614    cmd = ['forward']
615    if not allow_rebind:
616      cmd.append('--no-rebind')
617    cmd.extend([str(local), str(remote)])
618    self._RunDeviceAdbCmd(cmd, timeout, retries)
619
620  def ForwardRemove(self, local, timeout=DEFAULT_TIMEOUT,
621                    retries=DEFAULT_RETRIES):
622    """Remove a forward socket connection.
623
624    Args:
625      local: The host socket.
626      timeout: (optional) Timeout per try in seconds.
627      retries: (optional) Number of retries to attempt.
628    """
629    self._RunDeviceAdbCmd(['forward', '--remove', str(local)], timeout,
630                          retries)
631
632  def ForwardList(self, timeout=DEFAULT_TIMEOUT, retries=DEFAULT_RETRIES):
633    """List all currently forwarded socket connections.
634
635    Args:
636      timeout: (optional) Timeout per try in seconds.
637      retries: (optional) Number of retries to attempt.
638    """
639    return self._RunDeviceAdbCmd(['forward', '--list'], timeout, retries)
640
641  def JDWP(self, timeout=DEFAULT_TIMEOUT, retries=DEFAULT_RETRIES):
642    """List of PIDs of processes hosting a JDWP transport.
643
644    Args:
645      timeout: (optional) Timeout per try in seconds.
646      retries: (optional) Number of retries to attempt.
647
648    Returns:
649      A list of PIDs as strings.
650    """
651    return [a.strip() for a in
652            self._RunDeviceAdbCmd(['jdwp'], timeout, retries).split('\n')]
653
654  def Install(self, apk_path, forward_lock=False, allow_downgrade=False,
655              reinstall=False, sd_card=False, timeout=60 * 2,
656              retries=DEFAULT_RETRIES):
657    """Install an apk on the device.
658
659    Args:
660      apk_path: Host path to the APK file.
661      forward_lock: (optional) If set forward-locks the app.
662      allow_downgrade: (optional) If set, allows for downgrades.
663      reinstall: (optional) If set reinstalls the app, keeping its data.
664      sd_card: (optional) If set installs on the SD card.
665      timeout: (optional) Timeout per try in seconds.
666      retries: (optional) Number of retries to attempt.
667    """
668    VerifyLocalFileExists(apk_path)
669    cmd = ['install']
670    if forward_lock:
671      cmd.append('-l')
672    if reinstall:
673      cmd.append('-r')
674    if sd_card:
675      cmd.append('-s')
676    if allow_downgrade:
677      cmd.append('-d')
678    cmd.append(apk_path)
679    output = self._RunDeviceAdbCmd(cmd, timeout, retries)
680    if 'Success' not in output:
681      raise device_errors.AdbCommandFailedError(
682          cmd, output, device_serial=self._device_serial)
683
684  def InstallMultiple(self, apk_paths, forward_lock=False, reinstall=False,
685                      sd_card=False, allow_downgrade=False, partial=False,
686                      timeout=60 * 2, retries=DEFAULT_RETRIES):
687    """Install an apk with splits on the device.
688
689    Args:
690      apk_paths: Host path to the APK file.
691      forward_lock: (optional) If set forward-locks the app.
692      reinstall: (optional) If set reinstalls the app, keeping its data.
693      sd_card: (optional) If set installs on the SD card.
694      allow_downgrade: (optional) Allow versionCode downgrade.
695      partial: (optional) Package ID if apk_paths doesn't include all .apks.
696      timeout: (optional) Timeout per try in seconds.
697      retries: (optional) Number of retries to attempt.
698    """
699    for path in apk_paths:
700      VerifyLocalFileExists(path)
701    cmd = ['install-multiple']
702    if forward_lock:
703      cmd.append('-l')
704    if reinstall:
705      cmd.append('-r')
706    if sd_card:
707      cmd.append('-s')
708    if allow_downgrade:
709      cmd.append('-d')
710    if partial:
711      cmd.extend(('-p', partial))
712    cmd.extend(apk_paths)
713    output = self._RunDeviceAdbCmd(cmd, timeout, retries)
714    if 'Success' not in output:
715      raise device_errors.AdbCommandFailedError(
716          cmd, output, device_serial=self._device_serial)
717
718  def Uninstall(self, package, keep_data=False, timeout=DEFAULT_TIMEOUT,
719                retries=DEFAULT_RETRIES):
720    """Remove the app |package| from the device.
721
722    Args:
723      package: The package to uninstall.
724      keep_data: (optional) If set keep the data and cache directories.
725      timeout: (optional) Timeout per try in seconds.
726      retries: (optional) Number of retries to attempt.
727    """
728    cmd = ['uninstall']
729    if keep_data:
730      cmd.append('-k')
731    cmd.append(package)
732    output = self._RunDeviceAdbCmd(cmd, timeout, retries)
733    if 'Failure' in output:
734      raise device_errors.AdbCommandFailedError(
735          cmd, output, device_serial=self._device_serial)
736
737  def Backup(self, path, packages=None, apk=False, shared=False,
738             nosystem=True, include_all=False, timeout=DEFAULT_TIMEOUT,
739             retries=DEFAULT_RETRIES):
740    """Write an archive of the device's data to |path|.
741
742    Args:
743      path: Local path to store the backup file.
744      packages: List of to packages to be backed up.
745      apk: (optional) If set include the .apk files in the archive.
746      shared: (optional) If set buckup the device's SD card.
747      nosystem: (optional) If set exclude system applications.
748      include_all: (optional) If set back up all installed applications and
749        |packages| is optional.
750      timeout: (optional) Timeout per try in seconds.
751      retries: (optional) Number of retries to attempt.
752    """
753    cmd = ['backup', '-f', path]
754    if apk:
755      cmd.append('-apk')
756    if shared:
757      cmd.append('-shared')
758    if nosystem:
759      cmd.append('-nosystem')
760    if include_all:
761      cmd.append('-all')
762    if packages:
763      cmd.extend(packages)
764    assert bool(packages) ^ bool(include_all), (
765        'Provide \'packages\' or set \'include_all\' but not both.')
766    ret = self._RunDeviceAdbCmd(cmd, timeout, retries)
767    VerifyLocalFileExists(path)
768    return ret
769
770  def Restore(self, path, timeout=DEFAULT_TIMEOUT, retries=DEFAULT_RETRIES):
771    """Restore device contents from the backup archive.
772
773    Args:
774      path: Host path to the backup archive.
775      timeout: (optional) Timeout per try in seconds.
776      retries: (optional) Number of retries to attempt.
777    """
778    VerifyLocalFileExists(path)
779    self._RunDeviceAdbCmd(['restore'] + [path], timeout, retries)
780
781  def WaitForDevice(self, timeout=60 * 5, retries=DEFAULT_RETRIES):
782    """Block until the device is online.
783
784    Args:
785      timeout: (optional) Timeout per try in seconds.
786      retries: (optional) Number of retries to attempt.
787    """
788    self._RunDeviceAdbCmd(['wait-for-device'], timeout, retries)
789
790  def GetState(self, timeout=DEFAULT_TIMEOUT, retries=DEFAULT_RETRIES):
791    """Get device state.
792
793    Args:
794      timeout: (optional) Timeout per try in seconds.
795      retries: (optional) Number of retries to attempt.
796
797    Returns:
798      One of 'offline', 'bootloader', or 'device'.
799    """
800    # TODO(jbudorick): Revert to using get-state once it doesn't cause a
801    # a protocol fault.
802    # return self._RunDeviceAdbCmd(['get-state'], timeout, retries).strip()
803
804    lines = self._RawDevices(timeout=timeout, retries=retries)
805    for line in lines:
806      if len(line) >= 2 and line[0] == self._device_serial:
807        return line[1]
808    return 'offline'
809
810  def GetDevPath(self, timeout=DEFAULT_TIMEOUT, retries=DEFAULT_RETRIES):
811    """Gets the device path.
812
813    Args:
814      timeout: (optional) Timeout per try in seconds.
815      retries: (optional) Number of retries to attempt.
816
817    Returns:
818      The device path (e.g. usb:3-4)
819    """
820    return self._RunDeviceAdbCmd(['get-devpath'], timeout, retries)
821
822  def Remount(self, timeout=DEFAULT_TIMEOUT, retries=DEFAULT_RETRIES):
823    """Remounts the /system partition on the device read-write."""
824    self._RunDeviceAdbCmd(['remount'], timeout, retries)
825
826  def Reboot(self, to_bootloader=False, timeout=60 * 5,
827             retries=DEFAULT_RETRIES):
828    """Reboots the device.
829
830    Args:
831      to_bootloader: (optional) If set reboots to the bootloader.
832      timeout: (optional) Timeout per try in seconds.
833      retries: (optional) Number of retries to attempt.
834    """
835    if to_bootloader:
836      cmd = ['reboot-bootloader']
837    else:
838      cmd = ['reboot']
839    self._RunDeviceAdbCmd(cmd, timeout, retries)
840
841  def Root(self, timeout=DEFAULT_TIMEOUT, retries=DEFAULT_RETRIES):
842    """Restarts the adbd daemon with root permissions, if possible.
843
844    Args:
845      timeout: (optional) Timeout per try in seconds.
846      retries: (optional) Number of retries to attempt.
847    """
848    output = self._RunDeviceAdbCmd(['root'], timeout, retries)
849    if 'cannot' in output:
850      raise device_errors.AdbCommandFailedError(
851          ['root'], output, device_serial=self._device_serial)
852
853  def Emu(self, cmd, timeout=DEFAULT_TIMEOUT,
854               retries=DEFAULT_RETRIES):
855    """Runs an emulator console command.
856
857    See http://developer.android.com/tools/devices/emulator.html#console
858
859    Args:
860      cmd: The command to run on the emulator console.
861      timeout: (optional) Timeout per try in seconds.
862      retries: (optional) Number of retries to attempt.
863
864    Returns:
865      The output of the emulator console command.
866    """
867    if isinstance(cmd, basestring):
868      cmd = [cmd]
869    return self._RunDeviceAdbCmd(['emu'] + cmd, timeout, retries)
870
871  def DisableVerity(self, timeout=DEFAULT_TIMEOUT, retries=DEFAULT_RETRIES):
872    """Disable Marshmallow's Verity security feature"""
873    output = self._RunDeviceAdbCmd(['disable-verity'], timeout, retries)
874    if output and _VERITY_DISABLE_RE.search(output):
875      raise device_errors.AdbCommandFailedError(
876          ['disable-verity'], output, device_serial=self._device_serial)
877
878  def EnableVerity(self, timeout=DEFAULT_TIMEOUT, retries=DEFAULT_RETRIES):
879    """Enable Marshmallow's Verity security feature"""
880    output = self._RunDeviceAdbCmd(['enable-verity'], timeout, retries)
881    if output and _VERITY_ENABLE_RE.search(output):
882      raise device_errors.AdbCommandFailedError(
883          ['enable-verity'], output, device_serial=self._device_serial)
884
885  @property
886  def is_emulator(self):
887    return _EMULATOR_RE.match(self._device_serial)
888
889  @property
890  def is_ready(self):
891    try:
892      return self.GetState() == _READY_STATE
893    except device_errors.CommandFailedError:
894      return False
895