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