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