adb_interface.py revision 9290dd9c7d6286906ef0f1fc9706e67ca0bb4bf4
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 StartInstrumentationForPackage( 152 self, package_name, runner_name, timeout_time=60*10, 153 no_window_animation=False, instrumentation_args={}): 154 """Run instrumentation test for given package and runner. 155 156 Equivalent to StartInstrumentation, except instrumentation path is 157 separated into its package and runner components. 158 """ 159 instrumentation_path = "%s/%s" % (package_name, runner_name) 160 return self.StartInstrumentation(instrumentation_path, timeout_time=timeout_time, 161 no_window_animation=no_window_animation, 162 instrumentation_args=instrumentation_args) 163 164 def StartInstrumentation( 165 self, instrumentation_path, timeout_time=60*10, no_window_animation=False, 166 profile=False, instrumentation_args={}): 167 168 """Runs an instrumentation class on the target. 169 170 Returns a dictionary containing the key value pairs from the 171 instrumentations result bundle and a list of TestResults. Also handles the 172 interpreting of error output from the device and raises the necessary 173 exceptions. 174 175 Args: 176 instrumentation_path: string. It should be the fully classified package 177 name, and instrumentation test runner, separated by "/" 178 e.g. com.android.globaltimelaunch/.GlobalTimeLaunch 179 timeout_time: Timeout value for the am command. 180 no_window_animation: boolean, Whether you want window animations enabled 181 or disabled 182 profile: If True, profiling will be turned on for the instrumentation. 183 instrumentation_args: Dictionary of key value bundle arguments to pass to 184 instrumentation. 185 186 Returns: 187 (test_results, inst_finished_bundle) 188 189 test_results: a list of TestResults 190 inst_finished_bundle (dict): Key/value pairs contained in the bundle that 191 is passed into ActivityManager.finishInstrumentation(). Included in this 192 bundle is the return code of the Instrumentation process, any error 193 codes reported by the activity manager, and any results explicitly added 194 by the instrumentation code. 195 196 Raises: 197 WaitForResponseTimedOutError: if timeout occurred while waiting for 198 response to adb instrument command 199 DeviceUnresponsiveError: if device system process is not responding 200 InstrumentationError: if instrumentation failed to run 201 """ 202 203 command_string = self._BuildInstrumentationCommandPath( 204 instrumentation_path, no_window_animation=no_window_animation, 205 profile=profile, raw_mode=True, 206 instrumentation_args=instrumentation_args) 207 logger.Log(command_string) 208 (test_results, inst_finished_bundle) = ( 209 am_instrument_parser.ParseAmInstrumentOutput( 210 self.SendShellCommand(command_string, timeout_time=timeout_time, 211 retry_count=2))) 212 213 if "code" not in inst_finished_bundle: 214 raise errors.InstrumentationError("no test results... device setup " 215 "correctly?") 216 217 if inst_finished_bundle["code"] == "0": 218 short_msg_result = "no error message" 219 if "shortMsg" in inst_finished_bundle: 220 short_msg_result = inst_finished_bundle["shortMsg"] 221 logger.Log("Error! Test run failed: %s" % short_msg_result) 222 raise errors.InstrumentationError(short_msg_result) 223 224 if "INSTRUMENTATION_ABORTED" in inst_finished_bundle: 225 logger.Log("INSTRUMENTATION ABORTED!") 226 raise errors.DeviceUnresponsiveError 227 228 return (test_results, inst_finished_bundle) 229 230 def StartInstrumentationNoResults( 231 self, package_name, runner_name, no_window_animation=False, 232 raw_mode=False, instrumentation_args={}): 233 """Runs instrumentation and dumps output to stdout. 234 235 Equivalent to StartInstrumentation, but will dump instrumentation 236 'normal' output to stdout, instead of parsing return results. Command will 237 never timeout. 238 """ 239 adb_command_string = self.PreviewInstrumentationCommand( 240 package_name, runner_name, no_window_animation=no_window_animation, 241 raw_mode=raw_mode, instrumentation_args=instrumentation_args) 242 logger.Log(adb_command_string) 243 run_command.RunCommand(adb_command_string, return_output=False) 244 245 def PreviewInstrumentationCommand( 246 self, package_name, runner_name, no_window_animation=False, 247 raw_mode=False, instrumentation_args={}): 248 """Returns a string of adb command that will be executed.""" 249 inst_command_string = self._BuildInstrumentationCommand( 250 package_name, runner_name, no_window_animation=no_window_animation, 251 raw_mode=raw_mode, instrumentation_args=instrumentation_args) 252 command_string = "adb %s shell %s" % (self._target_arg, inst_command_string) 253 return command_string 254 255 def _BuildInstrumentationCommand( 256 self, package, runner_name, no_window_animation=False, profile=False, 257 raw_mode=True, instrumentation_args={}): 258 instrumentation_path = "%s/%s" % (package, runner_name) 259 260 return self._BuildInstrumentationCommandPath( 261 instrumentation_path, no_window_animation=no_window_animation, 262 profile=profile, raw_mode=raw_mode, 263 instrumentation_args=instrumentation_args) 264 265 def _BuildInstrumentationCommandPath( 266 self, instrumentation_path, no_window_animation=False, profile=False, 267 raw_mode=True, instrumentation_args={}): 268 command_string = "am instrument" 269 if no_window_animation: 270 command_string += " --no_window_animation" 271 if profile: 272 self._CreateTraceDir() 273 command_string += ( 274 " -p %s/%s.dmtrace" % 275 (self.DEVICE_TRACE_DIR, instrumentation_path.split(".")[-1])) 276 277 for key, value in instrumentation_args.items(): 278 command_string += " -e %s '%s'" % (key, value) 279 if raw_mode: 280 command_string += " -r" 281 command_string += " -w %s" % instrumentation_path 282 return command_string 283 284 def _CreateTraceDir(self): 285 ls_response = self.SendShellCommand("ls /data/trace") 286 if ls_response.strip("#").strip(string.whitespace) != "": 287 self.SendShellCommand("create /data/trace", "mkdir /data/trace") 288 self.SendShellCommand("make /data/trace world writeable", 289 "chmod 777 /data/trace") 290 291 def WaitForDevicePm(self, wait_time=120): 292 """Waits for targeted device's package manager to be up. 293 294 Args: 295 wait_time: time in seconds to wait 296 297 Raises: 298 WaitForResponseTimedOutError if wait_time elapses and pm still does not 299 respond. 300 """ 301 logger.Log("Waiting for device package manager...") 302 self.SendCommand("wait-for-device") 303 # Now the device is there, but may not be running. 304 # Query the package manager with a basic command 305 try: 306 self._WaitForShellCommandContents("pm path android", "package:", 307 wait_time) 308 except errors.WaitForResponseTimedOutError: 309 raise errors.WaitForResponseTimedOutError( 310 "Package manager did not respond after %s seconds" % wait_time) 311 312 def WaitForInstrumentation(self, package_name, runner_name, wait_time=120): 313 """Waits for given instrumentation to be present on device 314 315 Args: 316 wait_time: time in seconds to wait 317 318 Raises: 319 WaitForResponseTimedOutError if wait_time elapses and instrumentation 320 still not present. 321 """ 322 instrumentation_path = "%s/%s" % (package_name, runner_name) 323 logger.Log("Waiting for instrumentation to be present") 324 # Query the package manager 325 try: 326 command = "pm list instrumentation | grep %s" % instrumentation_path 327 self._WaitForShellCommandContents(command, "instrumentation:", wait_time, 328 raise_abort=False) 329 except errors.WaitForResponseTimedOutError : 330 logger.Log( 331 "Could not find instrumentation %s on device. Does the " 332 "instrumentation in test's AndroidManifest.xml match definition" 333 "in test_defs.xml?" % instrumentation_path) 334 raise 335 336 def WaitForProcess(self, name, wait_time=120): 337 """Wait until a process is running on the device. 338 339 Args: 340 name: the process name as it appears in `ps` 341 wait_time: time in seconds to wait 342 343 Raises: 344 WaitForResponseTimedOutError if wait_time elapses and the process is 345 still not running 346 """ 347 logger.Log("Waiting for process %s" % name) 348 self.SendCommand("wait-for-device") 349 self._WaitForShellCommandContents("ps", name, wait_time) 350 351 def WaitForProcessEnd(self, name, wait_time=120): 352 """Wait until a process is no longer 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 running 361 """ 362 logger.Log("Waiting for process %s to end" % name) 363 self._WaitForShellCommandContents("ps", name, wait_time, invert=True) 364 365 def _WaitForShellCommandContents(self, command, expected, wait_time, 366 raise_abort=True, invert=False): 367 """Wait until the response to a command contains a given output. 368 369 Assumes that a only successful execution of "adb shell <command>" contains 370 the substring expected. Assumes that a device is present. 371 372 Args: 373 command: adb shell command to execute 374 expected: the string that should appear to consider the 375 command successful. 376 wait_time: time in seconds to wait 377 raise_abort: if False, retry when executing the command raises an 378 AbortError, rather than failing. 379 invert: if True, wait until the command output no longer contains the 380 expected contents. 381 382 Raises: 383 WaitForResponseTimedOutError: If wait_time elapses and the command has not 384 returned an output containing expected yet. 385 """ 386 # Query the device with the command 387 success = False 388 attempts = 0 389 wait_period = 5 390 while not success and (attempts*wait_period) < wait_time: 391 # assume the command will always contain expected in the success case 392 try: 393 output = self.SendShellCommand(command, retry_count=1) 394 if ((not invert and expected in output) 395 or (invert and expected not in output)): 396 success = True 397 except errors.AbortError, e: 398 if raise_abort: 399 raise 400 # ignore otherwise 401 402 if not success: 403 time.sleep(wait_period) 404 attempts += 1 405 406 if not success: 407 raise errors.WaitForResponseTimedOutError() 408 409 def Sync(self, retry_count=3): 410 """Perform a adb sync. 411 412 Blocks until device package manager is responding. 413 414 Args: 415 retry_count: number of times to retry sync before failing 416 417 Raises: 418 WaitForResponseTimedOutError if package manager does not respond 419 AbortError if unrecoverable error occurred 420 """ 421 output = "" 422 error = None 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 self.WaitForDevicePm() 444 return output 445 446 def GetSerialNumber(self): 447 """Returns the serial number of the targeted device.""" 448 return self.SendCommand("get-serialno").strip() 449 450