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