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