• 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={}):
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
199    Returns:
200      (test_results, inst_finished_bundle)
201
202      test_results: a list of TestResults
203      inst_finished_bundle (dict): Key/value pairs contained in the bundle that
204        is passed into ActivityManager.finishInstrumentation(). Included in this
205        bundle is the return code of the Instrumentation process, any error
206        codes reported by the activity manager, and any results explicitly added
207        by the instrumentation code.
208
209     Raises:
210       WaitForResponseTimedOutError: if timeout occurred while waiting for
211         response to adb instrument command
212       DeviceUnresponsiveError: if device system process is not responding
213       InstrumentationError: if instrumentation failed to run
214    """
215
216    command_string = self._BuildInstrumentationCommandPath(
217        instrumentation_path, no_window_animation=no_window_animation,
218        profile=profile, raw_mode=True,
219        instrumentation_args=instrumentation_args)
220    logger.Log(command_string)
221    (test_results, inst_finished_bundle) = (
222        am_instrument_parser.ParseAmInstrumentOutput(
223            self.SendShellCommand(command_string, timeout_time=timeout_time,
224                                  retry_count=2)))
225
226    if "code" not in inst_finished_bundle:
227      raise errors.InstrumentationError("no test results... device setup "
228                                        "correctly?")
229
230    if inst_finished_bundle["code"] == "0":
231      short_msg_result = "no error message"
232      if "shortMsg" in inst_finished_bundle:
233        short_msg_result = inst_finished_bundle["shortMsg"]
234        logger.Log("Error! Test run failed: %s" % short_msg_result)
235      raise errors.InstrumentationError(short_msg_result)
236
237    if "INSTRUMENTATION_ABORTED" in inst_finished_bundle:
238      logger.Log("INSTRUMENTATION ABORTED!")
239      raise errors.DeviceUnresponsiveError
240
241    return (test_results, inst_finished_bundle)
242
243  def StartInstrumentationNoResults(
244      self, package_name, runner_name, no_window_animation=False,
245      raw_mode=False, instrumentation_args={}):
246    """Runs instrumentation and dumps output to stdout.
247
248    Equivalent to StartInstrumentation, but will dump instrumentation
249    'normal' output to stdout, instead of parsing return results. Command will
250    never timeout.
251    """
252    adb_command_string = self.PreviewInstrumentationCommand(
253        package_name, runner_name, no_window_animation=no_window_animation,
254        raw_mode=raw_mode, instrumentation_args=instrumentation_args)
255    logger.Log(adb_command_string)
256    run_command.RunCommand(adb_command_string, return_output=False)
257
258  def PreviewInstrumentationCommand(
259      self, package_name, runner_name, no_window_animation=False,
260      raw_mode=False, instrumentation_args={}):
261    """Returns a string of adb command that will be executed."""
262    inst_command_string = self._BuildInstrumentationCommand(
263        package_name, runner_name, no_window_animation=no_window_animation,
264        raw_mode=raw_mode, instrumentation_args=instrumentation_args)
265    command_string = "adb %s shell %s" % (self._target_arg, inst_command_string)
266    return command_string
267
268  def _BuildInstrumentationCommand(
269      self, package, runner_name, no_window_animation=False, profile=False,
270      raw_mode=True, instrumentation_args={}):
271    instrumentation_path = "%s/%s" % (package, runner_name)
272
273    return self._BuildInstrumentationCommandPath(
274        instrumentation_path, no_window_animation=no_window_animation,
275        profile=profile, raw_mode=raw_mode,
276        instrumentation_args=instrumentation_args)
277
278  def _BuildInstrumentationCommandPath(
279      self, instrumentation_path, no_window_animation=False, profile=False,
280      raw_mode=True, instrumentation_args={}):
281    command_string = "am instrument"
282    if no_window_animation:
283      command_string += " --no_window_animation"
284    if profile:
285      self._CreateTraceDir()
286      command_string += (
287          " -p %s/%s.dmtrace" %
288          (self.DEVICE_TRACE_DIR, instrumentation_path.split(".")[-1]))
289
290    for key, value in instrumentation_args.items():
291      command_string += " -e %s '%s'" % (key, value)
292    if raw_mode:
293      command_string += " -r"
294    command_string += " -w %s" % instrumentation_path
295    return command_string
296
297  def _CreateTraceDir(self):
298    ls_response = self.SendShellCommand("ls /data/trace")
299    if ls_response.strip("#").strip(string.whitespace) != "":
300      self.SendShellCommand("create /data/trace", "mkdir /data/trace")
301      self.SendShellCommand("make /data/trace world writeable",
302                            "chmod 777 /data/trace")
303
304  def WaitForDevicePm(self, wait_time=120):
305    """Waits for targeted device's package manager to be up.
306
307    Args:
308      wait_time: time in seconds to wait
309
310    Raises:
311      WaitForResponseTimedOutError if wait_time elapses and pm still does not
312      respond.
313    """
314    logger.Log("Waiting for device package manager...")
315    self.SendCommand("wait-for-device")
316    # Now the device is there, but may not be running.
317    # Query the package manager with a basic command
318    pm_found = False
319    attempts = 0
320    wait_period = 5
321    while not pm_found and (attempts*wait_period) < wait_time:
322      # assume the 'adb shell pm path android' command will always
323      # return 'package: something' in the success case
324      output = self.SendShellCommand("pm path android", retry_count=1)
325      if "package:" in output:
326        pm_found = True
327      else:
328        time.sleep(wait_period)
329        attempts += 1
330    if not pm_found:
331      raise errors.WaitForResponseTimedOutError(
332          "Package manager did not respond after %s seconds" % wait_time)
333
334  def WaitForInstrumentation(self, package_name, runner_name, wait_time=120):
335    """Waits for given instrumentation to be present on device
336
337    Args:
338      wait_time: time in seconds to wait
339
340    Raises:
341      WaitForResponseTimedOutError if wait_time elapses and instrumentation
342      still not present.
343    """
344    instrumentation_path = "%s/%s" % (package_name, runner_name)
345    logger.Log("Waiting for instrumentation to be present")
346    # Query the package manager
347    inst_found = False
348    attempts = 0
349    wait_period = 5
350    while not inst_found and (attempts*wait_period) < wait_time:
351      # assume the 'adb shell pm list instrumentation'
352      # return 'instrumentation: something' in the success case
353      try:
354        output = self.SendShellCommand("pm list instrumentation | grep %s"
355                                       % instrumentation_path, retry_count=1)
356        if "instrumentation:" in output:
357          inst_found = True
358      except errors.AbortError, e:
359        # ignore
360        pass
361      if not inst_found:
362        time.sleep(wait_period)
363        attempts += 1
364    if not inst_found:
365      logger.Log(
366          "Could not find instrumentation %s on device. Does the "
367          "instrumentation in test's AndroidManifest.xml match definition"
368          "in test_defs.xml?" % instrumentation_path)
369      raise errors.WaitForResponseTimedOutError()
370
371  def WaitForBootComplete(self, wait_time=120):
372    """Waits for targeted device's bootcomplete flag to be set.
373
374    Args:
375      wait_time: time in seconds to wait
376
377    Raises:
378      WaitForResponseTimedOutError if wait_time elapses and pm still does not
379      respond.
380    """
381    logger.Log("Waiting for boot complete...")
382    self.SendCommand("wait-for-device")
383    # Now the device is there, but may not be running.
384    # Query the package manager with a basic command
385    boot_complete = False
386    attempts = 0
387    wait_period = 5
388    while not boot_complete and (attempts*wait_period) < wait_time:
389      output = self.SendShellCommand("getprop dev.bootcomplete", retry_count=1)
390      output = output.strip()
391      if output == "1":
392        boot_complete = True
393      else:
394        time.sleep(wait_period)
395        attempts += 1
396    if not boot_complete:
397      raise errors.WaitForResponseTimedOutError(
398          "dev.bootcomplete flag was not set after %s seconds" % wait_time)
399
400  def Sync(self, retry_count=3, runtime_restart=False):
401    """Perform a adb sync.
402
403    Blocks until device package manager is responding.
404
405    Args:
406      retry_count: number of times to retry sync before failing
407      runtime_restart: stop runtime during sync and restart afterwards, useful
408        for syncing system libraries (core, framework etc)
409
410    Raises:
411      WaitForResponseTimedOutError if package manager does not respond
412      AbortError if unrecoverable error occurred
413    """
414    output = ""
415    error = None
416    if runtime_restart:
417      self.SendShellCommand("setprop ro.monkey 1", retry_count=retry_count)
418      # manual rest bootcomplete flag
419      self.SendShellCommand("setprop dev.bootcomplete 0",
420                            retry_count=retry_count)
421      self.SendShellCommand("stop", retry_count=retry_count)
422
423    try:
424      output = self.SendCommand("sync", retry_count=retry_count)
425    except errors.AbortError, e:
426      error = e
427      output = e.msg
428    if "Read-only file system" in output:
429      logger.SilentLog(output)
430      logger.Log("Remounting read-only filesystem")
431      self.SendCommand("remount")
432      output = self.SendCommand("sync", retry_count=retry_count)
433    elif "No space left on device" in output:
434      logger.SilentLog(output)
435      logger.Log("Restarting device runtime")
436      self.SendShellCommand("stop", retry_count=retry_count)
437      output = self.SendCommand("sync", retry_count=retry_count)
438      self.SendShellCommand("start", retry_count=retry_count)
439    elif error is not None:
440      # exception occurred that cannot be recovered from
441      raise error
442    logger.SilentLog(output)
443    if runtime_restart:
444      # start runtime and wait till boot complete flag is set
445      self.SendShellCommand("start", retry_count=retry_count)
446      self.WaitForBootComplete()
447      # press the MENU key, this will disable key guard if runtime is started
448      # with ro.monkey set to 1
449      self.SendShellCommand("input keyevent 82", retry_count=retry_count)
450    else:
451      self.WaitForDevicePm()
452    return output
453
454  def GetSerialNumber(self):
455    """Returns the serial number of the targeted device."""
456    return self.SendCommand("get-serialno").strip()
457