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