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 errno
13import logging
14import os
15import re
16
17from devil import devil_env
18from devil.android import decorators
19from devil.android import device_errors
20from devil.utils import cmd_helper
21from devil.utils import lazy
22from devil.utils import timeout_retry
23
24with devil_env.SysPath(devil_env.DEPENDENCY_MANAGER_PATH):
25  import dependency_manager  # pylint: disable=import-error
26
27
28_DEFAULT_TIMEOUT = 30
29_DEFAULT_RETRIES = 2
30
31_EMULATOR_RE = re.compile(r'^emulator-[0-9]+$')
32
33_READY_STATE = 'device'
34
35
36def VerifyLocalFileExists(path):
37  """Verifies a local file exists.
38
39  Args:
40    path: Path to the local file.
41
42  Raises:
43    IOError: If the file doesn't exist.
44  """
45  if not os.path.exists(path):
46    raise IOError(errno.ENOENT, os.strerror(errno.ENOENT), path)
47
48
49def _FindAdb():
50  try:
51    return devil_env.config.LocalPath('adb')
52  except dependency_manager.NoPathFoundError:
53    pass
54
55  try:
56    return os.path.join(devil_env.config.LocalPath('android_sdk'),
57                        'platform-tools', 'adb')
58  except dependency_manager.NoPathFoundError:
59    pass
60
61  try:
62    return devil_env.config.FetchPath('adb')
63  except dependency_manager.NoPathFoundError:
64    raise device_errors.NoAdbError()
65
66
67def _ShouldRetryAdbCmd(exc):
68  return not isinstance(exc, device_errors.NoAdbError)
69
70
71DeviceStat = collections.namedtuple('DeviceStat',
72                                    ['st_mode', 'st_size', 'st_time'])
73
74
75class AdbWrapper(object):
76  """A wrapper around a local Android Debug Bridge executable."""
77
78  _adb_path = lazy.WeakConstant(_FindAdb)
79
80  def __init__(self, device_serial):
81    """Initializes the AdbWrapper.
82
83    Args:
84      device_serial: The device serial number as a string.
85    """
86    if not device_serial:
87      raise ValueError('A device serial must be specified')
88    self._device_serial = str(device_serial)
89
90  @classmethod
91  def GetAdbPath(cls):
92    return cls._adb_path.read()
93
94  @classmethod
95  def _BuildAdbCmd(cls, args, device_serial, cpu_affinity=None):
96    if cpu_affinity is not None:
97      cmd = ['taskset', '-c', str(cpu_affinity)]
98    else:
99      cmd = []
100    cmd.append(cls.GetAdbPath())
101    if device_serial is not None:
102      cmd.extend(['-s', device_serial])
103    cmd.extend(args)
104    return cmd
105
106  # pylint: disable=unused-argument
107  @classmethod
108  @decorators.WithTimeoutAndConditionalRetries(_ShouldRetryAdbCmd)
109  def _RunAdbCmd(cls, args, timeout=None, retries=None, device_serial=None,
110                 check_error=True, cpu_affinity=None):
111    # pylint: disable=no-member
112    try:
113      status, output = cmd_helper.GetCmdStatusAndOutputWithTimeout(
114          cls._BuildAdbCmd(args, device_serial, cpu_affinity=cpu_affinity),
115          timeout_retry.CurrentTimeoutThreadGroup().GetRemainingTime())
116    except OSError as e:
117      if e.errno in (errno.ENOENT, errno.ENOEXEC):
118        raise device_errors.NoAdbError(msg=str(e))
119      else:
120        raise
121
122    if status != 0:
123      raise device_errors.AdbCommandFailedError(
124          args, output, status, device_serial)
125    # This catches some errors, including when the device drops offline;
126    # unfortunately adb is very inconsistent with error reporting so many
127    # command failures present differently.
128    if check_error and output.startswith('error:'):
129      raise device_errors.AdbCommandFailedError(args, output)
130    return output
131  # pylint: enable=unused-argument
132
133  def _RunDeviceAdbCmd(self, args, timeout, retries, check_error=True):
134    """Runs an adb command on the device associated with this object.
135
136    Args:
137      args: A list of arguments to adb.
138      timeout: Timeout in seconds.
139      retries: Number of retries.
140      check_error: Check that the command doesn't return an error message. This
141        does NOT check the exit status of shell commands.
142
143    Returns:
144      The output of the command.
145    """
146    return self._RunAdbCmd(args, timeout=timeout, retries=retries,
147                           device_serial=self._device_serial,
148                           check_error=check_error)
149
150  def _IterRunDeviceAdbCmd(self, args, timeout):
151    """Runs an adb command and returns an iterator over its output lines.
152
153    Args:
154      args: A list of arguments to adb.
155      timeout: Timeout in seconds.
156
157    Yields:
158      The output of the command line by line.
159    """
160    return cmd_helper.IterCmdOutputLines(
161      self._BuildAdbCmd(args, self._device_serial), timeout=timeout)
162
163  def __eq__(self, other):
164    """Consider instances equal if they refer to the same device.
165
166    Args:
167      other: The instance to compare equality with.
168
169    Returns:
170      True if the instances are considered equal, false otherwise.
171    """
172    return self._device_serial == str(other)
173
174  def __str__(self):
175    """The string representation of an instance.
176
177    Returns:
178      The device serial number as a string.
179    """
180    return self._device_serial
181
182  def __repr__(self):
183    return '%s(\'%s\')' % (self.__class__.__name__, self)
184
185  # pylint: disable=unused-argument
186  @classmethod
187  def IsServerOnline(cls):
188    status, output = cmd_helper.GetCmdStatusAndOutput(['pgrep', 'adb'])
189    output = [int(x) for x in output.split()]
190    logging.info('PIDs for adb found: %r', output)
191    return status == 0
192  # pylint: enable=unused-argument
193
194  @classmethod
195  def KillServer(cls, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
196    cls._RunAdbCmd(['kill-server'], timeout=timeout, retries=retries)
197
198  @classmethod
199  def StartServer(cls, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
200    # CPU affinity is used to reduce adb instability http://crbug.com/268450
201    cls._RunAdbCmd(['start-server'], timeout=timeout, retries=retries,
202                   cpu_affinity=0)
203
204  @classmethod
205  def GetDevices(cls, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
206    """DEPRECATED. Refer to Devices(...) below."""
207    # TODO(jbudorick): Remove this function once no more clients are using it.
208    return cls.Devices(timeout=timeout, retries=retries)
209
210  @classmethod
211  def Devices(cls, desired_state=_READY_STATE, long_list=False,
212              timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
213    """Get the list of active attached devices.
214
215    Args:
216      desired_state: If not None, limit the devices returned to only those
217        in the given state.
218      long_list: Whether to use the long listing format.
219      timeout: (optional) Timeout per try in seconds.
220      retries: (optional) Number of retries to attempt.
221
222    Yields:
223      AdbWrapper instances.
224    """
225    lines = cls._RawDevices(long_list=long_list, timeout=timeout,
226                            retries=retries)
227    if long_list:
228      return [
229        [AdbWrapper(line[0])] + line[1:]
230        for line in lines
231        if (len(line) >= 2 and (not desired_state or line[1] == desired_state))
232      ]
233    else:
234      return [
235        AdbWrapper(line[0])
236        for line in lines
237        if (len(line) == 2 and (not desired_state or line[1] == desired_state))
238      ]
239
240  @classmethod
241  def _RawDevices(cls, long_list=False, timeout=_DEFAULT_TIMEOUT,
242                  retries=_DEFAULT_RETRIES):
243    cmd = ['devices']
244    if long_list:
245      cmd.append('-l')
246    output = cls._RunAdbCmd(cmd, timeout=timeout, retries=retries)
247    return [line.split() for line in output.splitlines()[1:]]
248
249  def GetDeviceSerial(self):
250    """Gets the device serial number associated with this object.
251
252    Returns:
253      Device serial number as a string.
254    """
255    return self._device_serial
256
257  def Push(self, local, remote, timeout=60 * 5, retries=_DEFAULT_RETRIES):
258    """Pushes a file from the host to the device.
259
260    Args:
261      local: Path on the host filesystem.
262      remote: Path on the device filesystem.
263      timeout: (optional) Timeout per try in seconds.
264      retries: (optional) Number of retries to attempt.
265    """
266    VerifyLocalFileExists(local)
267    self._RunDeviceAdbCmd(['push', local, remote], timeout, retries)
268
269  def Pull(self, remote, local, timeout=60 * 5, retries=_DEFAULT_RETRIES):
270    """Pulls a file from the device to the host.
271
272    Args:
273      remote: Path on the device filesystem.
274      local: Path on the host filesystem.
275      timeout: (optional) Timeout per try in seconds.
276      retries: (optional) Number of retries to attempt.
277    """
278    cmd = ['pull', remote, local]
279    self._RunDeviceAdbCmd(cmd, timeout, retries)
280    try:
281      VerifyLocalFileExists(local)
282    except IOError:
283      raise device_errors.AdbCommandFailedError(
284          cmd, 'File not found on host: %s' % local, device_serial=str(self))
285
286  def Shell(self, command, expect_status=0, timeout=_DEFAULT_TIMEOUT,
287            retries=_DEFAULT_RETRIES):
288    """Runs a shell command on the device.
289
290    Args:
291      command: A string with the shell command to run.
292      expect_status: (optional) Check that the command's exit status matches
293        this value. Default is 0. If set to None the test is skipped.
294      timeout: (optional) Timeout per try in seconds.
295      retries: (optional) Number of retries to attempt.
296
297    Returns:
298      The output of the shell command as a string.
299
300    Raises:
301      device_errors.AdbCommandFailedError: If the exit status doesn't match
302        |expect_status|.
303    """
304    if expect_status is None:
305      args = ['shell', command]
306    else:
307      args = ['shell', '( %s );echo %%$?' % command.rstrip()]
308    output = self._RunDeviceAdbCmd(args, timeout, retries, check_error=False)
309    if expect_status is not None:
310      output_end = output.rfind('%')
311      if output_end < 0:
312        # causes the status string to become empty and raise a ValueError
313        output_end = len(output)
314
315      try:
316        status = int(output[output_end + 1:])
317      except ValueError:
318        logging.warning('exit status of shell command %r missing.', command)
319        raise device_errors.AdbShellCommandFailedError(
320            command, output, status=None, device_serial=self._device_serial)
321      output = output[:output_end]
322      if status != expect_status:
323        raise device_errors.AdbShellCommandFailedError(
324            command, output, status=status, device_serial=self._device_serial)
325    return output
326
327  def IterShell(self, command, timeout):
328    """Runs a shell command and returns an iterator over its output lines.
329
330    Args:
331      command: A string with the shell command to run.
332      timeout: Timeout in seconds.
333
334    Yields:
335      The output of the command line by line.
336    """
337    args = ['shell', command]
338    return cmd_helper.IterCmdOutputLines(
339      self._BuildAdbCmd(args, self._device_serial), timeout=timeout)
340
341  def Ls(self, path, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
342    """List the contents of a directory on the device.
343
344    Args:
345      path: Path on the device filesystem.
346      timeout: (optional) Timeout per try in seconds.
347      retries: (optional) Number of retries to attempt.
348
349    Returns:
350      A list of pairs (filename, stat) for each file found in the directory,
351      where the stat object has the properties: st_mode, st_size, and st_time.
352
353    Raises:
354      AdbCommandFailedError if |path| does not specify a valid and accessible
355          directory in the device, or the output of "adb ls" command is less
356          than four columns
357    """
358    def ParseLine(line, cmd):
359      cols = line.split(None, 3)
360      if len(cols) < 4:
361        raise device_errors.AdbCommandFailedError(
362            cmd, line, "the output should be 4 columns, but is only %d columns"
363            % len(cols), device_serial=self._device_serial)
364      filename = cols.pop()
365      stat = DeviceStat(*[int(num, base=16) for num in cols])
366      return (filename, stat)
367
368    cmd = ['ls', path]
369    lines = self._RunDeviceAdbCmd(
370        cmd, timeout=timeout, retries=retries).splitlines()
371    if lines:
372      return [ParseLine(line, cmd) for line in lines]
373    else:
374      raise device_errors.AdbCommandFailedError(
375          cmd, 'path does not specify an accessible directory in the device',
376          device_serial=self._device_serial)
377
378  def Logcat(self, clear=False, dump=False, filter_specs=None,
379             logcat_format=None, ring_buffer=None, timeout=None,
380             retries=_DEFAULT_RETRIES):
381    """Get an iterable over the logcat output.
382
383    Args:
384      clear: If true, clear the logcat.
385      dump: If true, dump the current logcat contents.
386      filter_specs: If set, a list of specs to filter the logcat.
387      logcat_format: If set, the format in which the logcat should be output.
388        Options include "brief", "process", "tag", "thread", "raw", "time",
389        "threadtime", and "long"
390      ring_buffer: If set, a list of alternate ring buffers to request.
391        Options include "main", "system", "radio", "events", "crash" or "all".
392        The default is equivalent to ["main", "system", "crash"].
393      timeout: (optional) If set, timeout per try in seconds. If clear or dump
394        is set, defaults to _DEFAULT_TIMEOUT.
395      retries: (optional) If clear or dump is set, the number of retries to
396        attempt. Otherwise, does nothing.
397
398    Yields:
399      logcat output line by line.
400    """
401    cmd = ['logcat']
402    use_iter = True
403    if clear:
404      cmd.append('-c')
405      use_iter = False
406    if dump:
407      cmd.append('-d')
408      use_iter = False
409    if logcat_format:
410      cmd.extend(['-v', logcat_format])
411    if ring_buffer:
412      for buffer_name in ring_buffer:
413        cmd.extend(['-b', buffer_name])
414    if filter_specs:
415      cmd.extend(filter_specs)
416
417    if use_iter:
418      return self._IterRunDeviceAdbCmd(cmd, timeout)
419    else:
420      timeout = timeout if timeout is not None else _DEFAULT_TIMEOUT
421      return self._RunDeviceAdbCmd(cmd, timeout, retries).splitlines()
422
423  def Forward(self, local, remote, timeout=_DEFAULT_TIMEOUT,
424              retries=_DEFAULT_RETRIES):
425    """Forward socket connections from the local socket to the remote socket.
426
427    Sockets are specified by one of:
428      tcp:<port>
429      localabstract:<unix domain socket name>
430      localreserved:<unix domain socket name>
431      localfilesystem:<unix domain socket name>
432      dev:<character device name>
433      jdwp:<process pid> (remote only)
434
435    Args:
436      local: The host socket.
437      remote: The device socket.
438      timeout: (optional) Timeout per try in seconds.
439      retries: (optional) Number of retries to attempt.
440    """
441    self._RunDeviceAdbCmd(['forward', str(local), str(remote)], timeout,
442                          retries)
443
444  def ForwardRemove(self, local, timeout=_DEFAULT_TIMEOUT,
445                    retries=_DEFAULT_RETRIES):
446    """Remove a forward socket connection.
447
448    Args:
449      local: The host socket.
450      timeout: (optional) Timeout per try in seconds.
451      retries: (optional) Number of retries to attempt.
452    """
453    self._RunDeviceAdbCmd(['forward', '--remove', str(local)], timeout,
454                          retries)
455
456  def ForwardList(self, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
457    """List all currently forwarded socket connections.
458
459    Args:
460      timeout: (optional) Timeout per try in seconds.
461      retries: (optional) Number of retries to attempt.
462    """
463    return self._RunDeviceAdbCmd(['forward', '--list'], timeout, retries)
464
465  def JDWP(self, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
466    """List of PIDs of processes hosting a JDWP transport.
467
468    Args:
469      timeout: (optional) Timeout per try in seconds.
470      retries: (optional) Number of retries to attempt.
471
472    Returns:
473      A list of PIDs as strings.
474    """
475    return [a.strip() for a in
476            self._RunDeviceAdbCmd(['jdwp'], timeout, retries).split('\n')]
477
478  def Install(self, apk_path, forward_lock=False, allow_downgrade=False,
479              reinstall=False, sd_card=False, timeout=60 * 2,
480              retries=_DEFAULT_RETRIES):
481    """Install an apk on the device.
482
483    Args:
484      apk_path: Host path to the APK file.
485      forward_lock: (optional) If set forward-locks the app.
486      allow_downgrade: (optional) If set, allows for downgrades.
487      reinstall: (optional) If set reinstalls the app, keeping its data.
488      sd_card: (optional) If set installs on the SD card.
489      timeout: (optional) Timeout per try in seconds.
490      retries: (optional) Number of retries to attempt.
491    """
492    VerifyLocalFileExists(apk_path)
493    cmd = ['install']
494    if forward_lock:
495      cmd.append('-l')
496    if reinstall:
497      cmd.append('-r')
498    if sd_card:
499      cmd.append('-s')
500    if allow_downgrade:
501      cmd.append('-d')
502    cmd.append(apk_path)
503    output = self._RunDeviceAdbCmd(cmd, timeout, retries)
504    if 'Success' not in output:
505      raise device_errors.AdbCommandFailedError(
506          cmd, output, device_serial=self._device_serial)
507
508  def InstallMultiple(self, apk_paths, forward_lock=False, reinstall=False,
509                      sd_card=False, allow_downgrade=False, partial=False,
510                      timeout=60 * 2, retries=_DEFAULT_RETRIES):
511    """Install an apk with splits on the device.
512
513    Args:
514      apk_paths: Host path to the APK file.
515      forward_lock: (optional) If set forward-locks the app.
516      reinstall: (optional) If set reinstalls the app, keeping its data.
517      sd_card: (optional) If set installs on the SD card.
518      allow_downgrade: (optional) Allow versionCode downgrade.
519      partial: (optional) Package ID if apk_paths doesn't include all .apks.
520      timeout: (optional) Timeout per try in seconds.
521      retries: (optional) Number of retries to attempt.
522    """
523    for path in apk_paths:
524      VerifyLocalFileExists(path)
525    cmd = ['install-multiple']
526    if forward_lock:
527      cmd.append('-l')
528    if reinstall:
529      cmd.append('-r')
530    if sd_card:
531      cmd.append('-s')
532    if allow_downgrade:
533      cmd.append('-d')
534    if partial:
535      cmd.extend(('-p', partial))
536    cmd.extend(apk_paths)
537    output = self._RunDeviceAdbCmd(cmd, timeout, retries)
538    if 'Success' not in output:
539      raise device_errors.AdbCommandFailedError(
540          cmd, output, device_serial=self._device_serial)
541
542  def Uninstall(self, package, keep_data=False, timeout=_DEFAULT_TIMEOUT,
543                retries=_DEFAULT_RETRIES):
544    """Remove the app |package| from the device.
545
546    Args:
547      package: The package to uninstall.
548      keep_data: (optional) If set keep the data and cache directories.
549      timeout: (optional) Timeout per try in seconds.
550      retries: (optional) Number of retries to attempt.
551    """
552    cmd = ['uninstall']
553    if keep_data:
554      cmd.append('-k')
555    cmd.append(package)
556    output = self._RunDeviceAdbCmd(cmd, timeout, retries)
557    if 'Failure' in output:
558      raise device_errors.AdbCommandFailedError(
559          cmd, output, device_serial=self._device_serial)
560
561  def Backup(self, path, packages=None, apk=False, shared=False,
562             nosystem=True, include_all=False, timeout=_DEFAULT_TIMEOUT,
563             retries=_DEFAULT_RETRIES):
564    """Write an archive of the device's data to |path|.
565
566    Args:
567      path: Local path to store the backup file.
568      packages: List of to packages to be backed up.
569      apk: (optional) If set include the .apk files in the archive.
570      shared: (optional) If set buckup the device's SD card.
571      nosystem: (optional) If set exclude system applications.
572      include_all: (optional) If set back up all installed applications and
573        |packages| is optional.
574      timeout: (optional) Timeout per try in seconds.
575      retries: (optional) Number of retries to attempt.
576    """
577    cmd = ['backup', '-f', path]
578    if apk:
579      cmd.append('-apk')
580    if shared:
581      cmd.append('-shared')
582    if nosystem:
583      cmd.append('-nosystem')
584    if include_all:
585      cmd.append('-all')
586    if packages:
587      cmd.extend(packages)
588    assert bool(packages) ^ bool(include_all), (
589        'Provide \'packages\' or set \'include_all\' but not both.')
590    ret = self._RunDeviceAdbCmd(cmd, timeout, retries)
591    VerifyLocalFileExists(path)
592    return ret
593
594  def Restore(self, path, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
595    """Restore device contents from the backup archive.
596
597    Args:
598      path: Host path to the backup archive.
599      timeout: (optional) Timeout per try in seconds.
600      retries: (optional) Number of retries to attempt.
601    """
602    VerifyLocalFileExists(path)
603    self._RunDeviceAdbCmd(['restore'] + [path], timeout, retries)
604
605  def WaitForDevice(self, timeout=60 * 5, retries=_DEFAULT_RETRIES):
606    """Block until the device is online.
607
608    Args:
609      timeout: (optional) Timeout per try in seconds.
610      retries: (optional) Number of retries to attempt.
611    """
612    self._RunDeviceAdbCmd(['wait-for-device'], timeout, retries)
613
614  def GetState(self, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
615    """Get device state.
616
617    Args:
618      timeout: (optional) Timeout per try in seconds.
619      retries: (optional) Number of retries to attempt.
620
621    Returns:
622      One of 'offline', 'bootloader', or 'device'.
623    """
624    # TODO(jbudorick): Revert to using get-state once it doesn't cause a
625    # a protocol fault.
626    # return self._RunDeviceAdbCmd(['get-state'], timeout, retries).strip()
627
628    lines = self._RawDevices(timeout=timeout, retries=retries)
629    for line in lines:
630      if len(line) >= 2 and line[0] == self._device_serial:
631        return line[1]
632    return 'offline'
633
634  def GetDevPath(self, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
635    """Gets the device path.
636
637    Args:
638      timeout: (optional) Timeout per try in seconds.
639      retries: (optional) Number of retries to attempt.
640
641    Returns:
642      The device path (e.g. usb:3-4)
643    """
644    return self._RunDeviceAdbCmd(['get-devpath'], timeout, retries)
645
646  def Remount(self, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
647    """Remounts the /system partition on the device read-write."""
648    self._RunDeviceAdbCmd(['remount'], timeout, retries)
649
650  def Reboot(self, to_bootloader=False, timeout=60 * 5,
651             retries=_DEFAULT_RETRIES):
652    """Reboots the device.
653
654    Args:
655      to_bootloader: (optional) If set reboots to the bootloader.
656      timeout: (optional) Timeout per try in seconds.
657      retries: (optional) Number of retries to attempt.
658    """
659    if to_bootloader:
660      cmd = ['reboot-bootloader']
661    else:
662      cmd = ['reboot']
663    self._RunDeviceAdbCmd(cmd, timeout, retries)
664
665  def Root(self, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
666    """Restarts the adbd daemon with root permissions, if possible.
667
668    Args:
669      timeout: (optional) Timeout per try in seconds.
670      retries: (optional) Number of retries to attempt.
671    """
672    output = self._RunDeviceAdbCmd(['root'], timeout, retries)
673    if 'cannot' in output:
674      raise device_errors.AdbCommandFailedError(
675          ['root'], output, device_serial=self._device_serial)
676
677  def Emu(self, cmd, timeout=_DEFAULT_TIMEOUT,
678               retries=_DEFAULT_RETRIES):
679    """Runs an emulator console command.
680
681    See http://developer.android.com/tools/devices/emulator.html#console
682
683    Args:
684      cmd: The command to run on the emulator console.
685      timeout: (optional) Timeout per try in seconds.
686      retries: (optional) Number of retries to attempt.
687
688    Returns:
689      The output of the emulator console command.
690    """
691    if isinstance(cmd, basestring):
692      cmd = [cmd]
693    return self._RunDeviceAdbCmd(['emu'] + cmd, timeout, retries)
694
695  @property
696  def is_emulator(self):
697    return _EMULATOR_RE.match(self._device_serial)
698
699  @property
700  def is_ready(self):
701    try:
702      return self.GetState() == _READY_STATE
703    except device_errors.CommandFailedError:
704      return False
705