Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright 2014 The Chromium Authors. All rights reserved. | 1 # Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 """Provides a variety of device interactions based on adb. | 5 """Provides a variety of device interactions based on adb. |
| 6 | 6 |
| 7 Eventually, this will be based on adb_wrapper. | 7 Eventually, this will be based on adb_wrapper. |
| 8 """ | 8 """ |
| 9 # pylint: disable=W0613 | 9 # pylint: disable=W0613 |
| 10 | 10 |
| 11 import signal | |
| 12 import sys | |
| 11 import time | 13 import time |
| 12 | 14 |
| 13 import pylib.android_commands | 15 import pylib.android_commands |
| 14 from pylib.device import adb_wrapper | 16 from pylib.device import adb_wrapper |
| 15 from pylib.device import decorators | 17 from pylib.device import decorators |
| 16 from pylib.device import device_errors | 18 from pylib.device import device_errors |
| 17 from pylib.utils import apk_helper | 19 from pylib.utils import apk_helper |
| 18 from pylib.utils import parallelizer | 20 from pylib.utils import parallelizer |
| 19 | 21 |
| 20 _DEFAULT_TIMEOUT = 30 | 22 _DEFAULT_TIMEOUT = 30 |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 117 """Restarts adbd with root privileges. | 119 """Restarts adbd with root privileges. |
| 118 | 120 |
| 119 Args: | 121 Args: |
| 120 timeout: Same as for |IsOnline|. | 122 timeout: Same as for |IsOnline|. |
| 121 retries: Same as for |IsOnline|. | 123 retries: Same as for |IsOnline|. |
| 122 Raises: | 124 Raises: |
| 123 CommandFailedError if root could not be enabled. | 125 CommandFailedError if root could not be enabled. |
| 124 """ | 126 """ |
| 125 if not self.old_interface.EnableAdbRoot(): | 127 if not self.old_interface.EnableAdbRoot(): |
| 126 raise device_errors.CommandFailedError( | 128 raise device_errors.CommandFailedError( |
| 127 ['adb', 'root'], 'Could not enable root.') | 129 'Could not enable root.', device=str(self)) |
| 128 | 130 |
| 129 @decorators.WithTimeoutAndRetriesFromInstance() | 131 @decorators.WithTimeoutAndRetriesFromInstance() |
| 130 def GetExternalStoragePath(self, timeout=None, retries=None): | 132 def GetExternalStoragePath(self, timeout=None, retries=None): |
| 131 """Get the device's path to its SD card. | 133 """Get the device's path to its SD card. |
| 132 | 134 |
| 133 Args: | 135 Args: |
| 134 timeout: Same as for |IsOnline|. | 136 timeout: Same as for |IsOnline|. |
| 135 retries: Same as for |IsOnline|. | 137 retries: Same as for |IsOnline|. |
| 136 Returns: | 138 Returns: |
| 137 The device's path to its SD card. | 139 The device's path to its SD card. |
| 138 """ | 140 """ |
| 139 try: | 141 try: |
| 140 return self.old_interface.GetExternalStorage() | 142 return self.old_interface.GetExternalStorage() |
| 141 except AssertionError as e: | 143 except AssertionError as e: |
| 142 raise device_errors.CommandFailedError( | 144 raise device_errors.CommandFailedError( |
| 143 ['adb', 'shell', 'echo', '$EXTERNAL_STORAGE'], str(e)) | 145 str(e), device=str(self)), None, sys.exc_info()[2] |
| 144 | 146 |
| 145 @decorators.WithTimeoutAndRetriesFromInstance() | 147 @decorators.WithTimeoutAndRetriesFromInstance() |
| 146 def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None): | 148 def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None): |
| 147 """Wait for the device to fully boot. | 149 """Wait for the device to fully boot. |
| 148 | 150 |
| 149 This means waiting for the device to boot, the package manager to be | 151 This means waiting for the device to boot, the package manager to be |
| 150 available, and the SD card to be ready. It can optionally mean waiting | 152 available, and the SD card to be ready. It can optionally mean waiting |
| 151 for wifi to come up, too. | 153 for wifi to come up, too. |
| 152 | 154 |
| 153 Args: | 155 Args: |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 220 if device_path is not None: | 222 if device_path is not None: |
| 221 files_changed = self.old_interface.GetFilesChanged( | 223 files_changed = self.old_interface.GetFilesChanged( |
| 222 apk_path, device_path, ignore_filenames=True) | 224 apk_path, device_path, ignore_filenames=True) |
| 223 if len(files_changed) > 0: | 225 if len(files_changed) > 0: |
| 224 should_install = True | 226 should_install = True |
| 225 if not reinstall: | 227 if not reinstall: |
| 226 out = self.old_interface.Uninstall(package_name) | 228 out = self.old_interface.Uninstall(package_name) |
| 227 for line in out.splitlines(): | 229 for line in out.splitlines(): |
| 228 if 'Failure' in line: | 230 if 'Failure' in line: |
| 229 raise device_errors.CommandFailedError( | 231 raise device_errors.CommandFailedError( |
| 230 ['adb', 'uninstall', package_name], line.strip()) | 232 line.strip(), device=str(self)) |
| 231 else: | 233 else: |
| 232 should_install = False | 234 should_install = False |
| 233 else: | 235 else: |
| 234 should_install = True | 236 should_install = True |
| 235 if should_install: | 237 if should_install: |
| 236 try: | 238 try: |
| 237 out = self.old_interface.Install(apk_path, reinstall=reinstall) | 239 out = self.old_interface.Install(apk_path, reinstall=reinstall) |
| 238 for line in out.splitlines(): | 240 for line in out.splitlines(): |
| 239 if 'Failure' in line: | 241 if 'Failure' in line: |
| 240 raise device_errors.CommandFailedError( | 242 raise device_errors.CommandFailedError( |
| 241 ['adb', 'install', apk_path], line.strip()) | 243 line.strip(), device=str(self)) |
| 242 except AssertionError as e: | 244 except AssertionError as e: |
| 243 raise device_errors.CommandFailedError( | 245 raise device_errors.CommandFailedError( |
| 244 ['adb', 'install', apk_path], str(e)) | 246 str(e), device=str(self)), None, sys.exc_info()[2] |
| 245 | 247 |
| 246 @decorators.WithTimeoutAndRetriesFromInstance() | 248 @decorators.WithTimeoutAndRetriesFromInstance() |
| 247 def RunShellCommand(self, cmd, check_return=False, root=False, timeout=None, | 249 def RunShellCommand(self, cmd, check_return=False, as_root=False, |
| 248 retries=None): | 250 timeout=None, retries=None): |
| 249 """Run an ADB shell command. | 251 """Run an ADB shell command. |
| 250 | 252 |
| 251 TODO(jbudorick) Switch the default value of check_return to True after | 253 TODO(jbudorick) Switch the default value of check_return to True after |
| 252 AndroidCommands is gone. | 254 AndroidCommands is gone. |
| 253 | 255 |
| 254 Args: | 256 Args: |
| 255 cmd: A list containing the command to run on the device and any arguments. | 257 cmd: A list containing the command to run on the device and any arguments. |
| 256 check_return: A boolean indicating whether or not the return code should | 258 check_return: A boolean indicating whether or not the return code should |
| 257 be checked. | 259 be checked. |
| 260 as_root: A boolean indicating whether the shell command should be run | |
| 261 with root privileges. | |
| 258 timeout: Same as for |IsOnline|. | 262 timeout: Same as for |IsOnline|. |
| 259 retries: Same as for |IsOnline|. | 263 retries: Same as for |IsOnline|. |
| 260 Raises: | 264 Raises: |
| 261 CommandFailedError if check_return is True and the return code is nozero. | 265 CommandFailedError if check_return is True and the return code is nozero. |
| 262 Returns: | 266 Returns: |
| 263 The output of the command. | 267 The output of the command. |
| 264 """ | 268 """ |
| 265 return self._RunShellCommandImpl(cmd, check_return=check_return, root=root, | 269 return self._RunShellCommandImpl(cmd, check_return=check_return, |
| 266 timeout=timeout) | 270 as_root=as_root, timeout=timeout) |
| 267 | 271 |
| 268 def _RunShellCommandImpl(self, cmd, check_return=False, root=False, | 272 def _RunShellCommandImpl(self, cmd, check_return=False, as_root=False, |
| 269 timeout=None): | 273 timeout=None): |
| 270 """Implementation of RunShellCommand. | 274 """Implementation of RunShellCommand. |
| 271 | 275 |
| 272 This is split from RunShellCommand to allow other DeviceUtils methods to | 276 This is split from RunShellCommand to allow other DeviceUtils methods to |
| 273 call RunShellCommand without spawning a new timeout thread. | 277 call RunShellCommand without spawning a new timeout thread. |
| 274 | 278 |
| 275 TODO(jbudorick) Remove the timeout parameter once this is no longer | 279 TODO(jbudorick) Remove the timeout parameter once this is no longer |
| 276 implemented via AndroidCommands. | 280 implemented via AndroidCommands. |
| 277 | 281 |
| 278 Args: | 282 Args: |
| 279 cmd: Same as for |RunShellCommand|. | 283 cmd: Same as for |RunShellCommand|. |
| 280 check_return: Same as for |RunShellCommand|. | 284 check_return: Same as for |RunShellCommand|. |
| 285 as_root: Same as for |RunShellCommand|. | |
| 281 timeout: Same as for |IsOnline|. | 286 timeout: Same as for |IsOnline|. |
| 282 Raises: | 287 Raises: |
| 283 Same as for |RunShellCommand|. | 288 Same as for |RunShellCommand|. |
| 284 Returns: | 289 Returns: |
| 285 Same as for |RunShellCommand|. | 290 Same as for |RunShellCommand|. |
| 286 """ | 291 """ |
| 287 if isinstance(cmd, list): | 292 if isinstance(cmd, list): |
| 288 cmd = ' '.join(cmd) | 293 cmd = ' '.join(cmd) |
| 289 if root and not self.HasRoot(): | 294 if as_root and not self.HasRoot(): |
| 290 cmd = 'su -c %s' % cmd | 295 cmd = 'su -c %s' % cmd |
| 291 if check_return: | 296 if check_return: |
| 292 code, output = self.old_interface.GetShellCommandStatusAndOutput( | 297 code, output = self.old_interface.GetShellCommandStatusAndOutput( |
| 293 cmd, timeout_time=timeout) | 298 cmd, timeout_time=timeout) |
| 294 if int(code) != 0: | 299 if int(code) != 0: |
| 295 raise device_errors.CommandFailedError( | 300 raise device_errors.AdbCommandFailedError( |
| 296 cmd, 'Nonzero exit code (%d)' % code) | 301 cmd.split(), 'Nonzero exit code (%d)' % code, device=str(self)) |
| 297 else: | 302 else: |
| 298 output = self.old_interface.RunShellCommand(cmd, timeout_time=timeout) | 303 output = self.old_interface.RunShellCommand(cmd, timeout_time=timeout) |
| 299 return output | 304 return output |
| 300 | 305 |
| 306 @decorators.WithTimeoutAndRetriesFromInstance() | |
| 307 def KillAll(self, process_name, signum=signal.SIGKILL, as_root=False, | |
| 308 blocking=False, timeout=None, retries=None): | |
| 309 """Kill all processes with the given name on the device. | |
| 310 | |
| 311 Args: | |
| 312 process_name: A string containing the name of the process to kill. | |
| 313 signum: An integer containing the signal number to send to kill. Defaults | |
| 314 to 9 (SIGKILL). | |
| 315 as_root: A boolean indicating whether the kill should be executed with | |
| 316 root priveleges. | |
| 317 blocking: A boolean indicating whether we should wait until all processes | |
| 318 with the given |process_name| are dead. | |
| 319 timeout: Same as for |IsOnline|. | |
| 320 retries: Same as for |IsOnline|. | |
| 321 Raises: | |
| 322 CommandFailedError if no process was killed. | |
| 323 """ | |
| 324 pids = self.old_interface.ExtractPid(process_name) | |
| 325 if len(pids) == 0: | |
| 326 raise device_errors.CommandFailedError( | |
| 327 'No process "%s"' % process_name, device=str(self)) | |
| 328 | |
| 329 if blocking: | |
| 330 total_killed = self.old_interface.KillAllBlocking( | |
| 331 process_name, signum=signum, with_su=as_root, timeout_sec=timeout) | |
| 332 else: | |
| 333 total_killed = self.old_interface.KillAll( | |
| 334 process_name, signum=signum, with_su=as_root) | |
| 335 if total_killed == 0: | |
| 336 raise device_errors.CommandFailedError( | |
| 337 'Failed to kill "%s"' % process_name, device=str(self)) | |
| 338 | |
| 339 @decorators.WithTimeoutAndRetriesFromInstance() | |
| 340 def StartActivity(self, intent, blocking=False, trace_file_name=None, | |
| 341 force_stop=False, timeout=None, retries=None): | |
| 342 """Start package's activity on the device. | |
| 343 | |
| 344 Args: | |
| 345 intent: An Intent to send. | |
| 346 blocking: A boolean indicating whether we should wait for the activity to | |
| 347 finish launching. | |
| 348 trace_file_name: If present, a string that both indicates that we want to | |
| 349 profile the activity and contains the path to which the | |
| 350 trace should be saved. | |
| 351 force_stop: A boolean indicating whether we should stop the activity | |
| 352 before starting it. | |
| 353 timeout: Same as for |IsOnline|. | |
| 354 retries: Same as for |IsOnline|. | |
|
frankf
2014/06/23 18:13:36
add Raises
jbudorick
2014/06/24 19:38:26
Done.
| |
| 355 """ | |
| 356 output = self.old_interface.StartActivity( | |
| 357 intent.package, intent.activity, wait_for_completion=blocking, | |
| 358 action=intent.action, category=intent.category, data=intent.data, | |
| 359 extras=intent.extras, trace_file_name=trace_file_name, | |
| 360 force_stop=force_stop, flags=intent.flags) | |
| 361 for l in output: | |
| 362 if l.startswith('Error:'): | |
| 363 raise device_errors.CommandFailedError(l, device=str(self)) | |
| 364 | |
| 365 @decorators.WithTimeoutAndRetriesFromInstance() | |
| 366 def BroadcastIntent(self, intent, timeout=None, retries=None): | |
| 367 """Send a broadcast intent. | |
| 368 | |
| 369 Args: | |
| 370 intent: An Intent to broadcast. | |
| 371 timeout: Same as for |IsOnline|. | |
| 372 retries: Same as for |IsOnline|. | |
| 373 """ | |
| 374 package, old_intent = intent.action.rsplit('.', 1) | |
| 375 if intent.extras is None: | |
| 376 args = [] | |
| 377 else: | |
| 378 args = ['-e %s%s' % (k, ' "%s"' % v if v else '') | |
| 379 for k, v in intent.extras.items() if len(k) > 0] | |
| 380 self.old_interface.BroadcastIntent(package, old_intent, *args) | |
| 381 | |
| 301 def __str__(self): | 382 def __str__(self): |
| 302 """Returns the device serial.""" | 383 """Returns the device serial.""" |
| 303 return self.old_interface.GetDevice() | 384 return self.old_interface.GetDevice() |
| 304 | 385 |
| 305 @staticmethod | 386 @staticmethod |
| 306 def parallel(devices=None, async=False): | 387 def parallel(devices=None, async=False): |
| 307 """Creates a Parallelizer to operate over the provided list of devices. | 388 """Creates a Parallelizer to operate over the provided list of devices. |
| 308 | 389 |
| 309 If |devices| is either |None| or an empty list, the Parallelizer will | 390 If |devices| is either |None| or an empty list, the Parallelizer will |
| 310 operate over all attached devices. | 391 operate over all attached devices. |
| 311 | 392 |
| 312 Args: | 393 Args: |
| 313 devices: A list of either DeviceUtils instances or objects from | 394 devices: A list of either DeviceUtils instances or objects from |
| 314 from which DeviceUtils instances can be constructed. If None, | 395 from which DeviceUtils instances can be constructed. If None, |
| 315 all attached devices will be used. | 396 all attached devices will be used. |
| 316 async: If true, returns a Parallelizer that runs operations | 397 async: If true, returns a Parallelizer that runs operations |
| 317 asynchronously. | 398 asynchronously. |
| 318 Returns: | 399 Returns: |
| 319 A Parallelizer operating over |devices|. | 400 A Parallelizer operating over |devices|. |
| 320 """ | 401 """ |
| 321 if not devices or len(devices) == 0: | 402 if not devices or len(devices) == 0: |
| 322 devices = pylib.android_commands.GetAttachedDevices() | 403 devices = pylib.android_commands.GetAttachedDevices() |
| 323 parallelizer_type = (parallelizer.Parallelizer if async | 404 parallelizer_type = (parallelizer.Parallelizer if async |
| 324 else parallelizer.SyncParallelizer) | 405 else parallelizer.SyncParallelizer) |
| 325 return parallelizer_type([ | 406 return parallelizer_type([ |
| 326 d if isinstance(d, DeviceUtils) else DeviceUtils(d) | 407 d if isinstance(d, DeviceUtils) else DeviceUtils(d) |
| 327 for d in devices]) | 408 for d in devices]) |
| 328 | 409 |
| OLD | NEW |