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=unused-argument | 9 # pylint: disable=unused-argument |
| 10 | 10 |
| (...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 195 other: The instance of DeviceUtils to compare to. | 195 other: The instance of DeviceUtils to compare to. |
| 196 Returns: | 196 Returns: |
| 197 Whether |self| is less than |other|. | 197 Whether |self| is less than |other|. |
| 198 """ | 198 """ |
| 199 return self.adb.GetDeviceSerial() < other.adb.GetDeviceSerial() | 199 return self.adb.GetDeviceSerial() < other.adb.GetDeviceSerial() |
| 200 | 200 |
| 201 def __str__(self): | 201 def __str__(self): |
| 202 """Returns the device serial.""" | 202 """Returns the device serial.""" |
| 203 return self.adb.GetDeviceSerial() | 203 return self.adb.GetDeviceSerial() |
| 204 | 204 |
| 205 # pylint: disable=no-self-argument | |
|
mikecase (-- gone --)
2015/06/08 19:14:52
Guessing you won't like this function (kinda hacky
jbudorick
2015/06/08 19:38:26
should be in decorators.py
mikecase (-- gone --)
2015/06/17 22:00:42
Removed decorator. Added _CheckSdkLevel function.
| |
| 206 def RequiresSdkLevel(requiredSdkLevel): | |
| 207 """Returns a decorator that checks that the device has the required | |
| 208 SDK level. | |
| 209 """ | |
| 210 def decorator(f): | |
| 211 def wrapper(self, *args, **kwargs): | |
| 212 if self.build_version_sdk < requiredSdkLevel: | |
| 213 raise device_errors.DeviceVersionError( | |
| 214 ('%s requires SDK level %s, device is SDK level %s' % | |
| 215 (f.__name__, requiredSdkLevel, self.build_version_sdk)), | |
| 216 device_serial=self.adb.GetDeviceSerial()) | |
| 217 f(self, *args, **kwargs) | |
| 218 return wrapper | |
| 219 return decorator | |
| 220 | |
| 205 @decorators.WithTimeoutAndRetriesFromInstance() | 221 @decorators.WithTimeoutAndRetriesFromInstance() |
| 206 def IsOnline(self, timeout=None, retries=None): | 222 def IsOnline(self, timeout=None, retries=None): |
| 207 """Checks whether the device is online. | 223 """Checks whether the device is online. |
| 208 | 224 |
| 209 Args: | 225 Args: |
| 210 timeout: timeout in seconds | 226 timeout: timeout in seconds |
| 211 retries: number of retries | 227 retries: number of retries |
| 212 | 228 |
| 213 Returns: | 229 Returns: |
| 214 True if the device is online, False otherwise. | 230 True if the device is online, False otherwise. |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 332 value = self.RunShellCommand('echo $EXTERNAL_STORAGE', | 348 value = self.RunShellCommand('echo $EXTERNAL_STORAGE', |
| 333 single_line=True, | 349 single_line=True, |
| 334 check_return=True) | 350 check_return=True) |
| 335 if not value: | 351 if not value: |
| 336 raise device_errors.CommandFailedError('$EXTERNAL_STORAGE is not set', | 352 raise device_errors.CommandFailedError('$EXTERNAL_STORAGE is not set', |
| 337 str(self)) | 353 str(self)) |
| 338 self._cache['external_storage'] = value | 354 self._cache['external_storage'] = value |
| 339 return value | 355 return value |
| 340 | 356 |
| 341 @decorators.WithTimeoutAndRetriesFromInstance() | 357 @decorators.WithTimeoutAndRetriesFromInstance() |
| 342 def GetApplicationPath(self, package, timeout=None, retries=None): | 358 def GetApplicationPaths(self, package, timeout=None, retries=None): |
| 343 """Get the path of the installed apk on the device for the given package. | 359 """Get the paths of the installed apks on the device for the given package. |
| 344 | 360 |
| 345 Args: | 361 Args: |
| 346 package: Name of the package. | 362 package: Name of the package. |
| 347 | 363 |
| 348 Returns: | 364 Returns: |
| 349 Path to the apk on the device if it exists, None otherwise. | 365 List of paths to the apks on the device if they exists, None otherwise. |
| 350 """ | 366 """ |
| 351 # 'pm path' is liable to incorrectly exit with a nonzero number starting | 367 # 'pm path' is liable to incorrectly exit with a nonzero number starting |
| 352 # in Lollipop. | 368 # in Lollipop. |
| 353 # TODO(jbudorick): Check if this is fixed as new Android versions are | 369 # TODO(jbudorick): Check if this is fixed as new Android versions are |
| 354 # released to put an upper bound on this. | 370 # released to put an upper bound on this. |
| 355 should_check_return = (self.build_version_sdk < | 371 should_check_return = (self.build_version_sdk < |
| 356 constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP) | 372 constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP) |
| 357 output = self.RunShellCommand(['pm', 'path', package], single_line=True, | 373 output = self.RunShellCommand( |
| 358 check_return=should_check_return) | 374 ['pm', 'path', package], check_return=should_check_return) |
| 359 if not output: | 375 if not output: |
| 360 return None | 376 return None |
| 361 if not output.startswith('package:'): | 377 if not all([s.startswith('package:') for s in output]): |
| 362 raise device_errors.CommandFailedError('pm path returned: %r' % output, | 378 raise device_errors.CommandFailedError( |
| 363 str(self)) | 379 'pm path returned: %r' % '\n'.join(output), str(self)) |
| 364 return output[len('package:'):] | 380 return [s[len('package:'):] for s in output] |
| 365 | 381 |
| 366 @decorators.WithTimeoutAndRetriesFromInstance() | 382 @decorators.WithTimeoutAndRetriesFromInstance() |
| 367 def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None): | 383 def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None): |
| 368 """Wait for the device to fully boot. | 384 """Wait for the device to fully boot. |
| 369 | 385 |
| 370 This means waiting for the device to boot, the package manager to be | 386 This means waiting for the device to boot, the package manager to be |
| 371 available, and the SD card to be ready. It can optionally mean waiting | 387 available, and the SD card to be ready. It can optionally mean waiting |
| 372 for wifi to come up, too. | 388 for wifi to come up, too. |
| 373 | 389 |
| 374 Args: | 390 Args: |
| 375 wifi: A boolean indicating if we should wait for wifi to come up or not. | 391 wifi: A boolean indicating if we should wait for wifi to come up or not. |
| 376 timeout: timeout in seconds | 392 timeout: timeout in seconds |
| 377 retries: number of retries | 393 retries: number of retries |
| 378 | 394 |
| 379 Raises: | 395 Raises: |
| 380 CommandFailedError on failure. | 396 CommandFailedError on failure. |
| 381 CommandTimeoutError if one of the component waits times out. | 397 CommandTimeoutError if one of the component waits times out. |
| 382 DeviceUnreachableError if the device becomes unresponsive. | 398 DeviceUnreachableError if the device becomes unresponsive. |
| 383 """ | 399 """ |
| 384 def sd_card_ready(): | 400 def sd_card_ready(): |
| 385 try: | 401 try: |
| 386 self.RunShellCommand(['test', '-d', self.GetExternalStoragePath()], | 402 self.RunShellCommand(['test', '-d', self.GetExternalStoragePath()], |
| 387 check_return=True) | 403 check_return=True) |
| 388 return True | 404 return True |
| 389 except device_errors.AdbCommandFailedError: | 405 except device_errors.AdbCommandFailedError: |
| 390 return False | 406 return False |
| 391 | 407 |
| 392 def pm_ready(): | 408 def pm_ready(): |
| 393 try: | 409 try: |
| 394 return self.GetApplicationPath('android') | 410 return self.GetApplicationPaths('android') |
| 395 except device_errors.CommandFailedError: | 411 except device_errors.CommandFailedError: |
| 396 return False | 412 return False |
| 397 | 413 |
| 398 def boot_completed(): | 414 def boot_completed(): |
| 399 return self.GetProp('sys.boot_completed') == '1' | 415 return self.GetProp('sys.boot_completed') == '1' |
| 400 | 416 |
| 401 def wifi_enabled(): | 417 def wifi_enabled(): |
| 402 return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi'], | 418 return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi'], |
| 403 check_return=False) | 419 check_return=False) |
| 404 | 420 |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 454 reinstall: A boolean indicating if we should keep any existing app data. | 470 reinstall: A boolean indicating if we should keep any existing app data. |
| 455 timeout: timeout in seconds | 471 timeout: timeout in seconds |
| 456 retries: number of retries | 472 retries: number of retries |
| 457 | 473 |
| 458 Raises: | 474 Raises: |
| 459 CommandFailedError if the installation fails. | 475 CommandFailedError if the installation fails. |
| 460 CommandTimeoutError if the installation times out. | 476 CommandTimeoutError if the installation times out. |
| 461 DeviceUnreachableError on missing device. | 477 DeviceUnreachableError on missing device. |
| 462 """ | 478 """ |
| 463 package_name = apk_helper.GetPackageName(apk_path) | 479 package_name = apk_helper.GetPackageName(apk_path) |
| 464 device_path = self.GetApplicationPath(package_name) | 480 device_paths = self.GetApplicationPaths(package_name) |
| 465 if device_path is not None: | 481 if device_paths: |
| 466 should_install = bool(self._GetChangedFilesImpl(apk_path, device_path)) | 482 should_install = bool( |
| 483 self._GetChangedFilesImpl(apk_path, device_paths[0])) | |
| 467 if should_install and not reinstall: | 484 if should_install and not reinstall: |
| 468 self.adb.Uninstall(package_name) | 485 self.adb.Uninstall(package_name) |
| 469 else: | 486 else: |
| 470 should_install = True | 487 should_install = True |
| 471 if should_install: | 488 if should_install: |
| 472 self.adb.Install(apk_path, reinstall=reinstall) | 489 self.adb.Install(apk_path, reinstall=reinstall) |
| 473 | 490 |
| 491 @RequiresSdkLevel(constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP) | |
| 492 @decorators.WithTimeoutAndRetriesDefaults( | |
| 493 INSTALL_DEFAULT_TIMEOUT, | |
| 494 INSTALL_DEFAULT_RETRIES) | |
| 495 def InstallSplitApk(self, base_apk, split_apks, reinstall=False, | |
| 496 timeout=None, retries=None): | |
| 497 """Install a split APK. | |
| 498 | |
| 499 Noop if all of the APK splits are already installed. | |
| 500 | |
| 501 Args: | |
| 502 base_apk: A string of the path to the base APK. | |
| 503 split_apks: A list of strings of paths of all of the APK splits. | |
| 504 reinstall: A boolean indicating if we should keep any existing app data. | |
| 505 timeout: timeout in seconds | |
| 506 retries: number of retries | |
| 507 | |
| 508 Raises: | |
| 509 CommandFailedError if the installation fails. | |
| 510 CommandTimeoutError if the installation times out. | |
| 511 DeviceUnreachableError on missing device. | |
| 512 DeviceVersionError if device SDK is less than Android L. | |
| 513 """ | |
| 514 def select_splits(): | |
|
jbudorick
2015/06/08 19:38:26
extract to its own module
mikecase (-- gone --)
2015/06/17 22:00:42
done
| |
| 515 split_config = ('%s-r%s-%s:%s' % | |
| 516 (self.langauge_setting, | |
| 517 self.country_setting, | |
| 518 self.screen_density, | |
| 519 self.product_cpu_abi)) | |
| 520 cmd = [os.path.join(constants.ANDROID_SDK_TOOLS, 'split-select'), | |
| 521 '--target', split_config, '--base', base_apk] | |
| 522 for split in split_apks: | |
| 523 cmd.extend(['--split', split]) | |
| 524 return cmd_helper.GetCmdOutput(cmd).splitlines() | |
| 525 | |
| 526 required_apk_splits = select_splits() | |
| 527 package_name = apk_helper.GetPackageName(base_apk) | |
| 528 device_apk_paths = self.GetApplicationPaths(package_name) | |
| 529 if device_apk_paths: | |
|
jbudorick
2015/06/08 19:38:26
I'm not sure about this part. It looks like it's i
mikecase (-- gone --)
2015/06/17 22:00:42
I think you have to install them all or nothing. I
| |
| 530 device_checksums = md5sum.CalculateDeviceMd5Sums( | |
| 531 device_apk_paths, self).values() | |
| 532 host_checksums = md5sum.CalculateHostMd5Sums( | |
| 533 [base_apk] + required_apk_splits).values() | |
| 534 should_install = sorted(host_checksums) != sorted(device_checksums) | |
| 535 if should_install and not reinstall: | |
| 536 self.adb.Uninstall(package_name) | |
| 537 else: | |
| 538 should_install = True | |
| 539 if should_install: | |
| 540 self.adb.InstallMultiple([base_apk] + required_apk_splits, | |
| 541 reinstall=reinstall) | |
| 542 | |
| 474 @decorators.WithTimeoutAndRetriesFromInstance() | 543 @decorators.WithTimeoutAndRetriesFromInstance() |
| 475 def RunShellCommand(self, cmd, check_return=False, cwd=None, env=None, | 544 def RunShellCommand(self, cmd, check_return=False, cwd=None, env=None, |
| 476 as_root=False, single_line=False, large_output=False, | 545 as_root=False, single_line=False, large_output=False, |
| 477 timeout=None, retries=None): | 546 timeout=None, retries=None): |
| 478 """Run an ADB shell command. | 547 """Run an ADB shell command. |
| 479 | 548 |
| 480 The command to run |cmd| should be a sequence of program arguments or else | 549 The command to run |cmd| should be a sequence of program arguments or else |
| 481 a single string. | 550 a single string. |
| 482 | 551 |
| 483 When |cmd| is a sequence, it is assumed to contain the name of the command | 552 When |cmd| is a sequence, it is assumed to contain the name of the command |
| (...skipping 291 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 775 | 844 |
| 776 Raises: | 845 Raises: |
| 777 CommandTimeoutError on timeout. | 846 CommandTimeoutError on timeout. |
| 778 DeviceUnreachableError on missing device. | 847 DeviceUnreachableError on missing device. |
| 779 """ | 848 """ |
| 780 # Check that the package exists before clearing it for android builds below | 849 # Check that the package exists before clearing it for android builds below |
| 781 # JB MR2. Necessary because calling pm clear on a package that doesn't exist | 850 # JB MR2. Necessary because calling pm clear on a package that doesn't exist |
| 782 # may never return. | 851 # may never return. |
| 783 if ((self.build_version_sdk >= | 852 if ((self.build_version_sdk >= |
| 784 constants.ANDROID_SDK_VERSION_CODES.JELLY_BEAN_MR2) | 853 constants.ANDROID_SDK_VERSION_CODES.JELLY_BEAN_MR2) |
| 785 or self.GetApplicationPath(package)): | 854 or self.GetApplicationPaths(package)): |
| 786 self.RunShellCommand(['pm', 'clear', package], check_return=True) | 855 self.RunShellCommand(['pm', 'clear', package], check_return=True) |
| 787 | 856 |
| 788 @decorators.WithTimeoutAndRetriesFromInstance() | 857 @decorators.WithTimeoutAndRetriesFromInstance() |
| 789 def SendKeyEvent(self, keycode, timeout=None, retries=None): | 858 def SendKeyEvent(self, keycode, timeout=None, retries=None): |
| 790 """Sends a keycode to the device. | 859 """Sends a keycode to the device. |
| 791 | 860 |
| 792 See the pylib.constants.keyevent module for suitable keycode values. | 861 See the pylib.constants.keyevent module for suitable keycode values. |
| 793 | 862 |
| 794 Args: | 863 Args: |
| 795 keycode: A integer keycode to send to the device. | 864 keycode: A integer keycode to send to the device. |
| (...skipping 441 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1237 | 1306 |
| 1238 # Next, check the current runtime value is what we need, and | 1307 # Next, check the current runtime value is what we need, and |
| 1239 # if not, set it and report that a reboot is required. | 1308 # if not, set it and report that a reboot is required. |
| 1240 value = self.GetProp(self.JAVA_ASSERT_PROPERTY) | 1309 value = self.GetProp(self.JAVA_ASSERT_PROPERTY) |
| 1241 if new_value != value: | 1310 if new_value != value: |
| 1242 self.SetProp(self.JAVA_ASSERT_PROPERTY, new_value) | 1311 self.SetProp(self.JAVA_ASSERT_PROPERTY, new_value) |
| 1243 return True | 1312 return True |
| 1244 else: | 1313 else: |
| 1245 return False | 1314 return False |
| 1246 | 1315 |
| 1316 @property | |
| 1317 def langauge_setting(self): | |
| 1318 """Returns the language setting on the device.""" | |
| 1319 return self.GetProp('persist.sys.language', cache=False) | |
| 1320 | |
| 1321 @property | |
| 1322 def country_setting(self): | |
| 1323 """Returns the country setting on the device.""" | |
| 1324 return self.GetProp('persist.sys.country', cache=False) | |
| 1325 | |
| 1326 @property | |
| 1327 def screen_density(self): | |
| 1328 """Returns the screen density of the device.""" | |
| 1329 DPI_TO_DENSITY = { | |
| 1330 120: 'ldpi', | |
| 1331 160: 'mdpi', | |
| 1332 240: 'hdpi', | |
| 1333 320: 'xhdpi', | |
| 1334 480: 'xxhdpi', | |
| 1335 } | |
| 1336 dpi = int(self.GetProp('ro.sf.lcd_density', cache=True)) | |
| 1337 return DPI_TO_DENSITY.get(dpi, 'tvdpi') | |
| 1247 | 1338 |
| 1248 @property | 1339 @property |
| 1249 def build_description(self): | 1340 def build_description(self): |
| 1250 """Returns the build description of the system. | 1341 """Returns the build description of the system. |
| 1251 | 1342 |
| 1252 For example: | 1343 For example: |
| 1253 nakasi-user 4.4.4 KTU84P 1227136 release-keys | 1344 nakasi-user 4.4.4 KTU84P 1227136 release-keys |
| 1254 """ | 1345 """ |
| 1255 return self.GetProp('ro.build.description', cache=True) | 1346 return self.GetProp('ro.build.description', cache=True) |
| 1256 | 1347 |
| (...skipping 320 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1577 @classmethod | 1668 @classmethod |
| 1578 def HealthyDevices(cls): | 1669 def HealthyDevices(cls): |
| 1579 blacklist = device_blacklist.ReadBlacklist() | 1670 blacklist = device_blacklist.ReadBlacklist() |
| 1580 def blacklisted(adb): | 1671 def blacklisted(adb): |
| 1581 if adb.GetDeviceSerial() in blacklist: | 1672 if adb.GetDeviceSerial() in blacklist: |
| 1582 logging.warning('Device %s is blacklisted.', adb.GetDeviceSerial()) | 1673 logging.warning('Device %s is blacklisted.', adb.GetDeviceSerial()) |
| 1583 return True | 1674 return True |
| 1584 return False | 1675 return False |
| 1585 | 1676 |
| 1586 return [cls(adb) for adb in adb_wrapper.AdbWrapper.Devices() | 1677 return [cls(adb) for adb in adb_wrapper.AdbWrapper.Devices() |
| 1587 if not blacklisted(adb)] | 1678 if not blacklisted(adb)] |
| 1588 | |
| OLD | NEW |