• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python2.4
2#
3#
4# Copyright 2008, The Android Open Source Project
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10#     http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17
18"""Provides an interface to communicate with the device via the adb command.
19
20Assumes adb binary is currently on system path.
21"""
22# Python imports
23import os
24import string
25import time
26
27# local imports
28import am_instrument_parser
29import errors
30import logger
31import run_command
32
33
34class AdbInterface:
35  """Helper class for communicating with Android device via adb."""
36
37  # argument to pass to adb, to direct command to specific device
38  _target_arg = ""
39
40  DEVICE_TRACE_DIR = "/data/test_results/"
41
42  def SetEmulatorTarget(self):
43    """Direct all future commands to the only running emulator."""
44    self._target_arg = "-e"
45
46  def SetDeviceTarget(self):
47    """Direct all future commands to the only connected USB device."""
48    self._target_arg = "-d"
49
50  def SetTargetSerial(self, serial):
51    """Direct all future commands to Android target with the given serial."""
52    self._target_arg = "-s %s" % serial
53
54  def SendCommand(self, command_string, timeout_time=20, retry_count=3):
55    """Send a command via adb.
56
57    Args:
58      command_string: adb command to run
59      timeout_time: number of seconds to wait for command to respond before
60        retrying
61      retry_count: number of times to retry command before raising
62        WaitForResponseTimedOutError
63    Returns:
64      string output of command
65
66    Raises:
67      WaitForResponseTimedOutError if device does not respond to command within time
68    """
69    adb_cmd = "adb %s %s" % (self._target_arg, command_string)
70    logger.SilentLog("about to run %s" % adb_cmd)
71    return run_command.RunCommand(adb_cmd, timeout_time=timeout_time,
72                                  retry_count=retry_count)
73
74  def SendShellCommand(self, cmd, timeout_time=20, retry_count=3):
75    """Send a adb shell command.
76
77    Args:
78      cmd: adb shell command to run
79      timeout_time: number of seconds to wait for command to respond before
80        retrying
81      retry_count: number of times to retry command before raising
82        WaitForResponseTimedOutError
83
84    Returns:
85      string output of command
86
87    Raises:
88      WaitForResponseTimedOutError: if device does not respond to command
89    """
90    return self.SendCommand("shell %s" % cmd, timeout_time=timeout_time,
91                            retry_count=retry_count)
92
93  def BugReport(self, path):
94    """Dumps adb bugreport to the file specified by the path.
95
96    Args:
97      path: Path of the file where adb bugreport is dumped to.
98    """
99    bug_output = self.SendShellCommand("bugreport", timeout_time=60)
100    bugreport_file = open(path, "w")
101    bugreport_file.write(bug_output)
102    bugreport_file.close()
103
104  def Push(self, src, dest):
105    """Pushes the file src onto the device at dest.
106
107    Args:
108      src: file path of host file to push
109      dest: destination absolute file path on device
110    """
111    self.SendCommand("push %s %s" % (src, dest), timeout_time=60)
112
113  def Pull(self, src, dest):
114    """Pulls the file src on the device onto dest on the host.
115
116    Args:
117      src: absolute file path of file on device to pull
118      dest: destination file path on host
119
120    Returns:
121      True if success and False otherwise.
122    """
123    # Create the base dir if it doesn't exist already
124    if not os.path.exists(os.path.dirname(dest)):
125      os.makedirs(os.path.dirname(dest))
126
127    if self.DoesFileExist(src):
128      self.SendCommand("pull %s %s" % (src, dest), timeout_time=60)
129      return True
130    else:
131      logger.Log("ADB Pull Failed: Source file %s does not exist." % src)
132      return False
133
134  def DoesFileExist(self, src):
135    """Checks if the given path exists on device target.
136
137    Args:
138      src: file path to be checked.
139
140    Returns:
141      True if file exists
142    """
143
144    output = self.SendShellCommand("ls %s" % src)
145    error = "No such file or directory"
146
147    if error in output:
148      return False
149    return True
150
151  def EnableAdbRoot(self):
152    """Enable adb root on device."""
153    output = self.SendCommand("root")
154    if "adbd is already running as root" in output:
155      return True
156    elif "restarting adbd as root" in output:
157      # device will disappear from adb, wait for it to come back
158      time.sleep(2)
159      self.SendCommand("wait-for-device")
160      return True
161    else:
162      logger.Log("Unrecognized output from adb root: %s" % output)
163      return False
164
165  def StartInstrumentationForPackage(
166      self, package_name, runner_name, timeout_time=60*10,
167      no_window_animation=False, instrumentation_args={}):
168    """Run instrumentation test for given package and runner.
169
170    Equivalent to StartInstrumentation, except instrumentation path is
171    separated into its package and runner components.
172    """
173    instrumentation_path = "%s/%s" % (package_name, runner_name)
174    return self.StartInstrumentation(instrumentation_path, timeout_time=timeout_time,
175                                     no_window_animation=no_window_animation,
176                                     instrumentation_args=instrumentation_args)
177
178  def StartInstrumentation(
179      self, instrumentation_path, timeout_time=60*10, no_window_animation=False,
180      profile=False, instrumentation_args={}):
181
182    """Runs an instrumentation class on the target.
183
184    Returns a dictionary containing the key value pairs from the
185    instrumentations result bundle and a list of TestResults. Also handles the
186    interpreting of error output from the device and raises the necessary
187    exceptions.
188
189    Args:
190      instrumentation_path: string. It should be the fully classified package
191      name, and instrumentation test runner, separated by "/"
192        e.g. com.android.globaltimelaunch/.GlobalTimeLaunch
193      timeout_time: Timeout value for the am command.
194      no_window_animation: boolean, Whether you want window animations enabled
195        or disabled
196      profile: If True, profiling will be turned on for the instrumentation.
197      instrumentation_args: Dictionary of key value bundle arguments to pass to
198      instrumentation.
199
200    Returns:
201      (test_results, inst_finished_bundle)
202
203      test_results: a list of TestResults
204      inst_finished_bundle (dict): Key/value pairs contained in the bundle that
205        is passed into ActivityManager.finishInstrumentation(). Included in this
206        bundle is the return code of the Instrumentation process, any error
207        codes reported by the activity manager, and any results explicitly added
208        by the instrumentation code.
209
210     Raises:
211       WaitForResponseTimedOutError: if timeout occurred while waiting for
212         response to adb instrument command
213       DeviceUnresponsiveError: if device system process is not responding
214       InstrumentationError: if instrumentation failed to run
215    """
216
217    command_string = self._BuildInstrumentationCommandPath(
218        instrumentation_path, no_window_animation=no_window_animation,
219        profile=profile, raw_mode=True,
220        instrumentation_args=instrumentation_args)
221    logger.Log(command_string)
222    (test_results, inst_finished_bundle) = (
223        am_instrument_parser.ParseAmInstrumentOutput(
224            self.SendShellCommand(command_string, timeout_time=timeout_time,
225                                  retry_count=2)))
226
227    if "code" not in inst_finished_bundle:
228      raise errors.InstrumentationError("no test results... device setup "
229                                        "correctly?")
230
231    if inst_finished_bundle["code"] == "0":
232      short_msg_result = "no error message"
233      if "shortMsg" in inst_finished_bundle:
234        short_msg_result = inst_finished_bundle["shortMsg"]
235        logger.Log("Error! Test run failed: %s" % short_msg_result)
236      raise errors.InstrumentationError(short_msg_result)
237
238    if "INSTRUMENTATION_ABORTED" in inst_finished_bundle:
239      logger.Log("INSTRUMENTATION ABORTED!")
240      raise errors.DeviceUnresponsiveError
241
242    return (test_results, inst_finished_bundle)
243
244  def StartInstrumentationNoResults(
245      self, package_name, runner_name, no_window_animation=False,
246      raw_mode=False, instrumentation_args={}):
247    """Runs instrumentation and dumps output to stdout.
248
249    Equivalent to StartInstrumentation, but will dump instrumentation
250    'normal' output to stdout, instead of parsing return results. Command will
251    never timeout.
252    """
253    adb_command_string = self.PreviewInstrumentationCommand(
254        package_name, runner_name, no_window_animation=no_window_animation,
255        raw_mode=raw_mode, instrumentation_args=instrumentation_args)
256    logger.Log(adb_command_string)
257    run_command.RunCommand(adb_command_string, return_output=False)
258
259  def PreviewInstrumentationCommand(
260      self, package_name, runner_name, no_window_animation=False,
261      raw_mode=False, instrumentation_args={}):
262    """Returns a string of adb command that will be executed."""
263    inst_command_string = self._BuildInstrumentationCommand(
264        package_name, runner_name, no_window_animation=no_window_animation,
265        raw_mode=raw_mode, instrumentation_args=instrumentation_args)
266    return self.PreviewShellCommand(inst_command_string)
267
268  def PreviewShellCommand(self, cmd):
269    return "adb %s shell %s" % (self._target_arg, cmd)
270
271  def _BuildInstrumentationCommand(
272      self, package, runner_name, no_window_animation=False, profile=False,
273      raw_mode=True, instrumentation_args={}):
274    instrumentation_path = "%s/%s" % (package, runner_name)
275
276    return self._BuildInstrumentationCommandPath(
277        instrumentation_path, no_window_animation=no_window_animation,
278        profile=profile, raw_mode=raw_mode,
279        instrumentation_args=instrumentation_args)
280
281  def _BuildInstrumentationCommandPath(
282      self, instrumentation_path, no_window_animation=False, profile=False,
283      raw_mode=True, instrumentation_args={}):
284    command_string = "am instrument"
285    if no_window_animation:
286      command_string += " --no_window_animation"
287    if profile:
288      self._CreateTraceDir()
289      command_string += (
290          " -p %s/%s.dmtrace" %
291          (self.DEVICE_TRACE_DIR, instrumentation_path.split(".")[-1]))
292
293    for key, value in instrumentation_args.items():
294      command_string += " -e %s '%s'" % (key, value)
295    if raw_mode:
296      command_string += " -r"
297    command_string += " -w %s" % instrumentation_path
298    return command_string
299
300  def _CreateTraceDir(self):
301    ls_response = self.SendShellCommand("ls /data/trace")
302    if ls_response.strip("#").strip(string.whitespace) != "":
303      self.SendShellCommand("create /data/trace", "mkdir /data/trace")
304      self.SendShellCommand("make /data/trace world writeable",
305                            "chmod 777 /data/trace")
306
307  def WaitForDevicePm(self, wait_time=120):
308    """Waits for targeted device's package manager to be up.
309
310    Args:
311      wait_time: time in seconds to wait
312
313    Raises:
314      WaitForResponseTimedOutError if wait_time elapses and pm still does not
315      respond.
316    """
317    logger.Log("Waiting for device package manager...")
318    self.SendCommand("wait-for-device")
319    # Now the device is there, but may not be running.
320    # Query the package manager with a basic command
321    try:
322      self._WaitForShellCommandContents("pm path android", "package:",
323                                        wait_time)
324    except errors.WaitForResponseTimedOutError:
325      raise errors.WaitForResponseTimedOutError(
326          "Package manager did not respond after %s seconds" % wait_time)
327
328  def WaitForInstrumentation(self, package_name, runner_name, wait_time=120):
329    """Waits for given instrumentation to be present on device
330
331    Args:
332      wait_time: time in seconds to wait
333
334    Raises:
335      WaitForResponseTimedOutError if wait_time elapses and instrumentation
336      still not present.
337    """
338    instrumentation_path = "%s/%s" % (package_name, runner_name)
339    logger.Log("Waiting for instrumentation to be present")
340    # Query the package manager
341    try:
342      command = "pm list instrumentation | grep %s" % instrumentation_path
343      self._WaitForShellCommandContents(command, "instrumentation:", wait_time,
344                                        raise_abort=False)
345    except errors.WaitForResponseTimedOutError :
346      logger.Log(
347          "Could not find instrumentation %s on device. Does the "
348          "instrumentation in test's AndroidManifest.xml match definition"
349          "in test_defs.xml?" % instrumentation_path)
350      raise
351
352  def WaitForProcess(self, name, wait_time=120):
353    """Wait until a process is running on the device.
354
355    Args:
356      name: the process name as it appears in `ps`
357      wait_time: time in seconds to wait
358
359    Raises:
360      WaitForResponseTimedOutError if wait_time elapses and the process is
361          still not running
362    """
363    logger.Log("Waiting for process %s" % name)
364    self.SendCommand("wait-for-device")
365    self._WaitForShellCommandContents("ps", name, wait_time)
366
367  def WaitForProcessEnd(self, name, wait_time=120):
368    """Wait until a process is no longer running on the device.
369
370    Args:
371      name: the process name as it appears in `ps`
372      wait_time: time in seconds to wait
373
374    Raises:
375      WaitForResponseTimedOutError if wait_time elapses and the process is
376          still running
377    """
378    logger.Log("Waiting for process %s to end" % name)
379    self._WaitForShellCommandContents("ps", name, wait_time, invert=True)
380
381  def _WaitForShellCommandContents(self, command, expected, wait_time,
382                                   raise_abort=True, invert=False):
383    """Wait until the response to a command contains a given output.
384
385    Assumes that a only successful execution of "adb shell <command>" contains
386    the substring expected. Assumes that a device is present.
387
388    Args:
389      command: adb shell command to execute
390      expected: the string that should appear to consider the
391          command successful.
392      wait_time: time in seconds to wait
393      raise_abort: if False, retry when executing the command raises an
394          AbortError, rather than failing.
395      invert: if True, wait until the command output no longer contains the
396          expected contents.
397
398    Raises:
399      WaitForResponseTimedOutError: If wait_time elapses and the command has not
400          returned an output containing expected yet.
401    """
402    # Query the device with the command
403    success = False
404    attempts = 0
405    wait_period = 5
406    while not success and (attempts*wait_period) < wait_time:
407      # assume the command will always contain expected in the success case
408      try:
409        output = self.SendShellCommand(command, retry_count=1)
410        if ((not invert and expected in output)
411            or (invert and expected not in output)):
412          success = True
413      except errors.AbortError, e:
414        if raise_abort:
415          raise
416        # ignore otherwise
417
418      if not success:
419        time.sleep(wait_period)
420        attempts += 1
421
422    if not success:
423      raise errors.WaitForResponseTimedOutError()
424
425  def WaitForBootComplete(self, wait_time=120):
426    """Waits for targeted device's bootcomplete flag to be set.
427
428    Args:
429      wait_time: time in seconds to wait
430
431    Raises:
432      WaitForResponseTimedOutError if wait_time elapses and pm still does not
433      respond.
434    """
435    logger.Log("Waiting for boot complete...")
436    self.SendCommand("wait-for-device")
437    # Now the device is there, but may not be running.
438    # Query the package manager with a basic command
439    boot_complete = False
440    attempts = 0
441    wait_period = 5
442    while not boot_complete and (attempts*wait_period) < wait_time:
443      output = self.SendShellCommand("getprop dev.bootcomplete", retry_count=1)
444      output = output.strip()
445      if output == "1":
446        boot_complete = True
447      else:
448        time.sleep(wait_period)
449        attempts += 1
450    if not boot_complete:
451      raise errors.WaitForResponseTimedOutError(
452          "dev.bootcomplete flag was not set after %s seconds" % wait_time)
453
454  def Sync(self, retry_count=3, runtime_restart=False):
455    """Perform a adb sync.
456
457    Blocks until device package manager is responding.
458
459    Args:
460      retry_count: number of times to retry sync before failing
461      runtime_restart: stop runtime during sync and restart afterwards, useful
462        for syncing system libraries (core, framework etc)
463
464    Raises:
465      WaitForResponseTimedOutError if package manager does not respond
466      AbortError if unrecoverable error occurred
467    """
468    output = ""
469    error = None
470    if runtime_restart:
471      self.SendShellCommand("setprop ro.test_harness 1", retry_count=retry_count)
472      # manual rest bootcomplete flag
473      self.SendShellCommand("setprop dev.bootcomplete 0",
474                            retry_count=retry_count)
475      self.SendShellCommand("stop", retry_count=retry_count)
476
477    try:
478      output = self.SendCommand("sync", retry_count=retry_count)
479    except errors.AbortError, e:
480      error = e
481      output = e.msg
482    if "Read-only file system" in output:
483      logger.SilentLog(output)
484      logger.Log("Remounting read-only filesystem")
485      self.SendCommand("remount")
486      output = self.SendCommand("sync", retry_count=retry_count)
487    elif "No space left on device" in output:
488      logger.SilentLog(output)
489      logger.Log("Restarting device runtime")
490      self.SendShellCommand("stop", retry_count=retry_count)
491      output = self.SendCommand("sync", retry_count=retry_count)
492      self.SendShellCommand("start", retry_count=retry_count)
493    elif error is not None:
494      # exception occurred that cannot be recovered from
495      raise error
496    logger.SilentLog(output)
497    if runtime_restart:
498      # start runtime and wait till boot complete flag is set
499      self.SendShellCommand("start", retry_count=retry_count)
500      self.WaitForBootComplete()
501      # press the MENU key, this will disable key guard if runtime is started
502      # with ro.monkey set to 1
503      self.SendShellCommand("input keyevent 82", retry_count=retry_count)
504    else:
505      self.WaitForDevicePm()
506    return output
507
508  def GetSerialNumber(self):
509    """Returns the serial number of the targeted device."""
510    return self.SendCommand("get-serialno").strip()
511