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