• 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      self.SendCommand("wait-for-device")
159      return True
160    else:
161      logger.Log("Unrecognized output from adb root: %s" % output)
162      return False
163
164  def StartInstrumentationForPackage(
165      self, package_name, runner_name, timeout_time=60*10,
166      no_window_animation=False, instrumentation_args={}):
167    """Run instrumentation test for given package and runner.
168
169    Equivalent to StartInstrumentation, except instrumentation path is
170    separated into its package and runner components.
171    """
172    instrumentation_path = "%s/%s" % (package_name, runner_name)
173    return self.StartInstrumentation(instrumentation_path, timeout_time=timeout_time,
174                                     no_window_animation=no_window_animation,
175                                     instrumentation_args=instrumentation_args)
176
177  def StartInstrumentation(
178      self, instrumentation_path, timeout_time=60*10, no_window_animation=False,
179      profile=False, instrumentation_args={}, silent_log=False):
180
181    """Runs an instrumentation class on the target.
182
183    Returns a dictionary containing the key value pairs from the
184    instrumentations result bundle and a list of TestResults. Also handles the
185    interpreting of error output from the device and raises the necessary
186    exceptions.
187
188    Args:
189      instrumentation_path: string. It should be the fully classified package
190      name, and instrumentation test runner, separated by "/"
191        e.g. com.android.globaltimelaunch/.GlobalTimeLaunch
192      timeout_time: Timeout value for the am command.
193      no_window_animation: boolean, Whether you want window animations enabled
194        or disabled
195      profile: If True, profiling will be turned on for the instrumentation.
196      instrumentation_args: Dictionary of key value bundle arguments to pass to
197      instrumentation.
198      silent_log: If True, the invocation of the instrumentation test runner
199        will not be logged.
200
201    Returns:
202      (test_results, inst_finished_bundle)
203
204      test_results: a list of TestResults
205      inst_finished_bundle (dict): Key/value pairs contained in the bundle that
206        is passed into ActivityManager.finishInstrumentation(). Included in this
207        bundle is the return code of the Instrumentation process, any error
208        codes reported by the activity manager, and any results explicitly added
209        by the instrumentation code.
210
211     Raises:
212       WaitForResponseTimedOutError: if timeout occurred while waiting for
213         response to adb instrument command
214       DeviceUnresponsiveError: if device system process is not responding
215       InstrumentationError: if instrumentation failed to run
216    """
217
218    command_string = self._BuildInstrumentationCommandPath(
219        instrumentation_path, no_window_animation=no_window_animation,
220        profile=profile, raw_mode=True,
221        instrumentation_args=instrumentation_args)
222    if silent_log:
223      logger.SilentLog(command_string)
224    else:
225      logger.Log(command_string)
226    (test_results, inst_finished_bundle) = (
227        am_instrument_parser.ParseAmInstrumentOutput(
228            self.SendShellCommand(command_string, timeout_time=timeout_time,
229                                  retry_count=2)))
230    if "code" not in inst_finished_bundle:
231      logger.Log('No code available. inst_finished_bundle contains: %s '
232                 % inst_finished_bundle)
233      raise errors.InstrumentationError("no test results... device setup "
234                                        "correctly?")
235
236    if inst_finished_bundle["code"] == "0":
237      long_msg_result = "no error message"
238      if "longMsg" in inst_finished_bundle:
239        long_msg_result = inst_finished_bundle["longMsg"]
240        logger.Log("Error! Test run failed: %s" % long_msg_result)
241      raise errors.InstrumentationError(long_msg_result)
242
243    if "INSTRUMENTATION_ABORTED" in inst_finished_bundle:
244      logger.Log("INSTRUMENTATION ABORTED!")
245      raise errors.DeviceUnresponsiveError
246
247    return (test_results, inst_finished_bundle)
248
249  def StartInstrumentationNoResults(
250      self, package_name, runner_name, no_window_animation=False,
251      raw_mode=False, instrumentation_args={}):
252    """Runs instrumentation and dumps output to stdout.
253
254    Equivalent to StartInstrumentation, but will dump instrumentation
255    'normal' output to stdout, instead of parsing return results. Command will
256    never timeout.
257    """
258    adb_command_string = self.PreviewInstrumentationCommand(
259        package_name, runner_name, no_window_animation=no_window_animation,
260        raw_mode=raw_mode, instrumentation_args=instrumentation_args)
261    logger.Log(adb_command_string)
262    run_command.RunCommand(adb_command_string, return_output=False)
263
264  def PreviewInstrumentationCommand(
265      self, package_name, runner_name, no_window_animation=False,
266      raw_mode=False, instrumentation_args={}):
267    """Returns a string of adb command that will be executed."""
268    inst_command_string = self._BuildInstrumentationCommand(
269        package_name, runner_name, no_window_animation=no_window_animation,
270        raw_mode=raw_mode, instrumentation_args=instrumentation_args)
271    command_string = "adb %s shell %s" % (self._target_arg, inst_command_string)
272    return command_string
273
274  def _BuildInstrumentationCommand(
275      self, package, runner_name, no_window_animation=False, profile=False,
276      raw_mode=True, instrumentation_args={}):
277    instrumentation_path = "%s/%s" % (package, runner_name)
278
279    return self._BuildInstrumentationCommandPath(
280        instrumentation_path, no_window_animation=no_window_animation,
281        profile=profile, raw_mode=raw_mode,
282        instrumentation_args=instrumentation_args)
283
284  def _BuildInstrumentationCommandPath(
285      self, instrumentation_path, no_window_animation=False, profile=False,
286      raw_mode=True, instrumentation_args={}):
287    command_string = "am instrument"
288    if no_window_animation:
289      command_string += " --no_window_animation"
290    if profile:
291      self._CreateTraceDir()
292      command_string += (
293          " -p %s/%s.dmtrace" %
294          (self.DEVICE_TRACE_DIR, instrumentation_path.split(".")[-1]))
295
296    for key, value in instrumentation_args.items():
297      command_string += " -e %s '%s'" % (key, value)
298    if raw_mode:
299      command_string += " -r"
300    command_string += " -w %s" % instrumentation_path
301    return command_string
302
303  def _CreateTraceDir(self):
304    ls_response = self.SendShellCommand("ls /data/trace")
305    if ls_response.strip("#").strip(string.whitespace) != "":
306      self.SendShellCommand("create /data/trace", "mkdir /data/trace")
307      self.SendShellCommand("make /data/trace world writeable",
308                            "chmod 777 /data/trace")
309
310  def WaitForDevicePm(self, wait_time=120):
311    """Waits for targeted device's package manager to be up.
312
313    Args:
314      wait_time: time in seconds to wait
315
316    Raises:
317      WaitForResponseTimedOutError if wait_time elapses and pm still does not
318      respond.
319    """
320    logger.Log("Waiting for device package manager...")
321    self.SendCommand("wait-for-device")
322    # Now the device is there, but may not be running.
323    # Query the package manager with a basic command
324    try:
325      self._WaitForShellCommandContents("pm path android", "package:",
326                                        wait_time)
327    except errors.WaitForResponseTimedOutError:
328      raise errors.WaitForResponseTimedOutError(
329          "Package manager did not respond after %s seconds" % wait_time)
330
331  def WaitForInstrumentation(self, package_name, runner_name, wait_time=120):
332    """Waits for given instrumentation to be present on device
333
334    Args:
335      wait_time: time in seconds to wait
336
337    Raises:
338      WaitForResponseTimedOutError if wait_time elapses and instrumentation
339      still not present.
340    """
341    instrumentation_path = "%s/%s" % (package_name, runner_name)
342    logger.Log("Waiting for instrumentation to be present")
343    # Query the package manager
344    try:
345      command = "pm list instrumentation | grep %s" % instrumentation_path
346      self._WaitForShellCommandContents(command, "instrumentation:", wait_time,
347                                        raise_abort=False)
348    except errors.WaitForResponseTimedOutError :
349      logger.Log(
350          "Could not find instrumentation %s on device. Does the "
351          "instrumentation in test's AndroidManifest.xml match definition"
352          "in test_defs.xml?" % instrumentation_path)
353      raise
354
355  def WaitForProcess(self, name, wait_time=120):
356    """Wait until a process is running on the device.
357
358    Args:
359      name: the process name as it appears in `ps`
360      wait_time: time in seconds to wait
361
362    Raises:
363      WaitForResponseTimedOutError if wait_time elapses and the process is
364          still not running
365    """
366    logger.Log("Waiting for process %s" % name)
367    self.SendCommand("wait-for-device")
368    self._WaitForShellCommandContents("ps", name, wait_time)
369
370  def WaitForProcessEnd(self, name, wait_time=120):
371    """Wait until a process is no longer running on the device.
372
373    Args:
374      name: the process name as it appears in `ps`
375      wait_time: time in seconds to wait
376
377    Raises:
378      WaitForResponseTimedOutError if wait_time elapses and the process is
379          still running
380    """
381    logger.Log("Waiting for process %s to end" % name)
382    self._WaitForShellCommandContents("ps", name, wait_time, invert=True)
383
384  def _WaitForShellCommandContents(self, command, expected, wait_time,
385                                   raise_abort=True, invert=False):
386    """Wait until the response to a command contains a given output.
387
388    Assumes that a only successful execution of "adb shell <command>" contains
389    the substring expected. Assumes that a device is present.
390
391    Args:
392      command: adb shell command to execute
393      expected: the string that should appear to consider the
394          command successful.
395      wait_time: time in seconds to wait
396      raise_abort: if False, retry when executing the command raises an
397          AbortError, rather than failing.
398      invert: if True, wait until the command output no longer contains the
399          expected contents.
400
401    Raises:
402      WaitForResponseTimedOutError: If wait_time elapses and the command has not
403          returned an output containing expected yet.
404    """
405    # Query the device with the command
406    success = False
407    attempts = 0
408    wait_period = 5
409    while not success and (attempts*wait_period) < wait_time:
410      # assume the command will always contain expected in the success case
411      try:
412        output = self.SendShellCommand(command, retry_count=1)
413        if ((not invert and expected in output)
414            or (invert and expected not in output)):
415          success = True
416      except errors.AbortError, e:
417        if raise_abort:
418          raise
419        # ignore otherwise
420
421      if not success:
422        time.sleep(wait_period)
423        attempts += 1
424
425    if not success:
426      raise errors.WaitForResponseTimedOutError()
427
428  def WaitForBootComplete(self, wait_time=120):
429    """Waits for targeted device's bootcomplete flag to be set.
430
431    Args:
432      wait_time: time in seconds to wait
433
434    Raises:
435      WaitForResponseTimedOutError if wait_time elapses and pm still does not
436      respond.
437    """
438    logger.Log("Waiting for boot complete...")
439    self.SendCommand("wait-for-device")
440    # Now the device is there, but may not be running.
441    # Query the package manager with a basic command
442    boot_complete = False
443    attempts = 0
444    wait_period = 5
445    while not boot_complete and (attempts*wait_period) < wait_time:
446      output = self.SendShellCommand("getprop dev.bootcomplete", retry_count=1)
447      output = output.strip()
448      if output == "1":
449        boot_complete = True
450      else:
451        time.sleep(wait_period)
452        attempts += 1
453    if not boot_complete:
454      raise errors.WaitForResponseTimedOutError(
455          "dev.bootcomplete flag was not set after %s seconds" % wait_time)
456
457  def Sync(self, retry_count=3, runtime_restart=False):
458    """Perform a adb sync.
459
460    Blocks until device package manager is responding.
461
462    Args:
463      retry_count: number of times to retry sync before failing
464      runtime_restart: stop runtime during sync and restart afterwards, useful
465        for syncing system libraries (core, framework etc)
466
467    Raises:
468      WaitForResponseTimedOutError if package manager does not respond
469      AbortError if unrecoverable error occurred
470    """
471    output = ""
472    error = None
473    if runtime_restart:
474      self.SendShellCommand("setprop ro.monkey 1", retry_count=retry_count)
475      # manual rest bootcomplete flag
476      self.SendShellCommand("setprop dev.bootcomplete 0",
477                            retry_count=retry_count)
478      self.SendShellCommand("stop", retry_count=retry_count)
479
480    try:
481      output = self.SendCommand("sync", retry_count=retry_count)
482    except errors.AbortError, e:
483      error = e
484      output = e.msg
485    if "Read-only file system" in output:
486      logger.SilentLog(output)
487      logger.Log("Remounting read-only filesystem")
488      self.SendCommand("remount")
489      output = self.SendCommand("sync", retry_count=retry_count)
490    elif "No space left on device" in output:
491      logger.SilentLog(output)
492      logger.Log("Restarting device runtime")
493      self.SendShellCommand("stop", retry_count=retry_count)
494      output = self.SendCommand("sync", retry_count=retry_count)
495      self.SendShellCommand("start", retry_count=retry_count)
496    elif error is not None:
497      # exception occurred that cannot be recovered from
498      raise error
499    logger.SilentLog(output)
500    if runtime_restart:
501      # start runtime and wait till boot complete flag is set
502      self.SendShellCommand("start", retry_count=retry_count)
503      self.WaitForBootComplete()
504      # press the MENU key, this will disable key guard if runtime is started
505      # with ro.monkey set to 1
506      self.SendShellCommand("input keyevent 82", retry_count=retry_count)
507    else:
508      self.WaitForDevicePm()
509    return output
510
511  def GetSerialNumber(self):
512    """Returns the serial number of the targeted device."""
513    return self.SendCommand("get-serialno").strip()
514