adb_interface.py revision cdfaae1a3409d380fab8228545d1b0d28ca070f7
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 command_string = "adb %s shell %s" % (self._target_arg, inst_command_string) 267 return command_string 268 269 def _BuildInstrumentationCommand( 270 self, package, runner_name, no_window_animation=False, profile=False, 271 raw_mode=True, instrumentation_args={}): 272 instrumentation_path = "%s/%s" % (package, runner_name) 273 274 return self._BuildInstrumentationCommandPath( 275 instrumentation_path, no_window_animation=no_window_animation, 276 profile=profile, raw_mode=raw_mode, 277 instrumentation_args=instrumentation_args) 278 279 def _BuildInstrumentationCommandPath( 280 self, instrumentation_path, no_window_animation=False, profile=False, 281 raw_mode=True, instrumentation_args={}): 282 command_string = "am instrument" 283 if no_window_animation: 284 command_string += " --no_window_animation" 285 if profile: 286 self._CreateTraceDir() 287 command_string += ( 288 " -p %s/%s.dmtrace" % 289 (self.DEVICE_TRACE_DIR, instrumentation_path.split(".")[-1])) 290 291 for key, value in instrumentation_args.items(): 292 command_string += " -e %s '%s'" % (key, value) 293 if raw_mode: 294 command_string += " -r" 295 command_string += " -w %s" % instrumentation_path 296 return command_string 297 298 def _CreateTraceDir(self): 299 ls_response = self.SendShellCommand("ls /data/trace") 300 if ls_response.strip("#").strip(string.whitespace) != "": 301 self.SendShellCommand("create /data/trace", "mkdir /data/trace") 302 self.SendShellCommand("make /data/trace world writeable", 303 "chmod 777 /data/trace") 304 305 def WaitForDevicePm(self, wait_time=120): 306 """Waits for targeted device's package manager to be up. 307 308 Args: 309 wait_time: time in seconds to wait 310 311 Raises: 312 WaitForResponseTimedOutError if wait_time elapses and pm still does not 313 respond. 314 """ 315 logger.Log("Waiting for device package manager...") 316 self.SendCommand("wait-for-device") 317 # Now the device is there, but may not be running. 318 # Query the package manager with a basic command 319 try: 320 self._WaitForShellCommandContents("pm path android", "package:", 321 wait_time) 322 except errors.WaitForResponseTimedOutError: 323 raise errors.WaitForResponseTimedOutError( 324 "Package manager did not respond after %s seconds" % wait_time) 325 326 def WaitForInstrumentation(self, package_name, runner_name, wait_time=120): 327 """Waits for given instrumentation to be present on device 328 329 Args: 330 wait_time: time in seconds to wait 331 332 Raises: 333 WaitForResponseTimedOutError if wait_time elapses and instrumentation 334 still not present. 335 """ 336 instrumentation_path = "%s/%s" % (package_name, runner_name) 337 logger.Log("Waiting for instrumentation to be present") 338 # Query the package manager 339 try: 340 command = "pm list instrumentation | grep %s" % instrumentation_path 341 self._WaitForShellCommandContents(command, "instrumentation:", wait_time, 342 raise_abort=False) 343 except errors.WaitForResponseTimedOutError : 344 logger.Log( 345 "Could not find instrumentation %s on device. Does the " 346 "instrumentation in test's AndroidManifest.xml match definition" 347 "in test_defs.xml?" % instrumentation_path) 348 raise 349 350 def WaitForProcess(self, name, wait_time=120): 351 """Wait until a process is running on the device. 352 353 Args: 354 name: the process name as it appears in `ps` 355 wait_time: time in seconds to wait 356 357 Raises: 358 WaitForResponseTimedOutError if wait_time elapses and the process is 359 still not running 360 """ 361 logger.Log("Waiting for process %s" % name) 362 self.SendCommand("wait-for-device") 363 self._WaitForShellCommandContents("ps", name, wait_time) 364 365 def WaitForProcessEnd(self, name, wait_time=120): 366 """Wait until a process is no longer running on the device. 367 368 Args: 369 name: the process name as it appears in `ps` 370 wait_time: time in seconds to wait 371 372 Raises: 373 WaitForResponseTimedOutError if wait_time elapses and the process is 374 still running 375 """ 376 logger.Log("Waiting for process %s to end" % name) 377 self._WaitForShellCommandContents("ps", name, wait_time, invert=True) 378 379 def _WaitForShellCommandContents(self, command, expected, wait_time, 380 raise_abort=True, invert=False): 381 """Wait until the response to a command contains a given output. 382 383 Assumes that a only successful execution of "adb shell <command>" contains 384 the substring expected. Assumes that a device is present. 385 386 Args: 387 command: adb shell command to execute 388 expected: the string that should appear to consider the 389 command successful. 390 wait_time: time in seconds to wait 391 raise_abort: if False, retry when executing the command raises an 392 AbortError, rather than failing. 393 invert: if True, wait until the command output no longer contains the 394 expected contents. 395 396 Raises: 397 WaitForResponseTimedOutError: If wait_time elapses and the command has not 398 returned an output containing expected yet. 399 """ 400 # Query the device with the command 401 success = False 402 attempts = 0 403 wait_period = 5 404 while not success and (attempts*wait_period) < wait_time: 405 # assume the command will always contain expected in the success case 406 try: 407 output = self.SendShellCommand(command, retry_count=1) 408 if ((not invert and expected in output) 409 or (invert and expected not in output)): 410 success = True 411 except errors.AbortError, e: 412 if raise_abort: 413 raise 414 # ignore otherwise 415 416 if not success: 417 time.sleep(wait_period) 418 attempts += 1 419 420 if not success: 421 raise errors.WaitForResponseTimedOutError() 422 423 def WaitForBootComplete(self, wait_time=120): 424 """Waits for targeted device's bootcomplete flag to be set. 425 426 Args: 427 wait_time: time in seconds to wait 428 429 Raises: 430 WaitForResponseTimedOutError if wait_time elapses and pm still does not 431 respond. 432 """ 433 logger.Log("Waiting for boot complete...") 434 self.SendCommand("wait-for-device") 435 # Now the device is there, but may not be running. 436 # Query the package manager with a basic command 437 boot_complete = False 438 attempts = 0 439 wait_period = 5 440 while not boot_complete and (attempts*wait_period) < wait_time: 441 output = self.SendShellCommand("getprop dev.bootcomplete", retry_count=1) 442 output = output.strip() 443 if output == "1": 444 boot_complete = True 445 else: 446 time.sleep(wait_period) 447 attempts += 1 448 if not boot_complete: 449 raise errors.WaitForResponseTimedOutError( 450 "dev.bootcomplete flag was not set after %s seconds" % wait_time) 451 452 def Sync(self, retry_count=3, runtime_restart=False): 453 """Perform a adb sync. 454 455 Blocks until device package manager is responding. 456 457 Args: 458 retry_count: number of times to retry sync before failing 459 runtime_restart: stop runtime during sync and restart afterwards, useful 460 for syncing system libraries (core, framework etc) 461 462 Raises: 463 WaitForResponseTimedOutError if package manager does not respond 464 AbortError if unrecoverable error occurred 465 """ 466 output = "" 467 error = None 468 if runtime_restart: 469 self.SendShellCommand("setprop ro.test_harness 1", retry_count=retry_count) 470 # manual rest bootcomplete flag 471 self.SendShellCommand("setprop dev.bootcomplete 0", 472 retry_count=retry_count) 473 self.SendShellCommand("stop", retry_count=retry_count) 474 475 try: 476 output = self.SendCommand("sync", retry_count=retry_count) 477 except errors.AbortError, e: 478 error = e 479 output = e.msg 480 if "Read-only file system" in output: 481 logger.SilentLog(output) 482 logger.Log("Remounting read-only filesystem") 483 self.SendCommand("remount") 484 output = self.SendCommand("sync", retry_count=retry_count) 485 elif "No space left on device" in output: 486 logger.SilentLog(output) 487 logger.Log("Restarting device runtime") 488 self.SendShellCommand("stop", retry_count=retry_count) 489 output = self.SendCommand("sync", retry_count=retry_count) 490 self.SendShellCommand("start", retry_count=retry_count) 491 elif error is not None: 492 # exception occurred that cannot be recovered from 493 raise error 494 logger.SilentLog(output) 495 if runtime_restart: 496 # start runtime and wait till boot complete flag is set 497 self.SendShellCommand("start", retry_count=retry_count) 498 self.WaitForBootComplete() 499 # press the MENU key, this will disable key guard if runtime is started 500 # with ro.monkey set to 1 501 self.SendShellCommand("input keyevent 82", retry_count=retry_count) 502 else: 503 self.WaitForDevicePm() 504 return output 505 506 def GetSerialNumber(self): 507 """Returns the serial number of the targeted device.""" 508 return self.SendCommand("get-serialno").strip() 509