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 15 matching lines...) Expand all Loading... | |
| 26 from pylib import cmd_helper | 26 from pylib import cmd_helper |
| 27 from pylib import constants | 27 from pylib import constants |
| 28 from pylib import device_signal | 28 from pylib import device_signal |
| 29 from pylib.device import adb_wrapper | 29 from pylib.device import adb_wrapper |
| 30 from pylib.device import decorators | 30 from pylib.device import decorators |
| 31 from pylib.device import device_blacklist | 31 from pylib.device import device_blacklist |
| 32 from pylib.device import device_errors | 32 from pylib.device import device_errors |
| 33 from pylib.device import intent | 33 from pylib.device import intent |
| 34 from pylib.device import logcat_monitor | 34 from pylib.device import logcat_monitor |
| 35 from pylib.device.commands import install_commands | 35 from pylib.device.commands import install_commands |
| 36 from pylib.sdk import split_select | |
| 36 from pylib.utils import apk_helper | 37 from pylib.utils import apk_helper |
| 37 from pylib.utils import base_error | 38 from pylib.utils import base_error |
| 38 from pylib.utils import device_temp_file | 39 from pylib.utils import device_temp_file |
| 39 from pylib.utils import host_utils | 40 from pylib.utils import host_utils |
| 40 from pylib.utils import md5sum | 41 from pylib.utils import md5sum |
| 41 from pylib.utils import parallelizer | 42 from pylib.utils import parallelizer |
| 42 from pylib.utils import timeout_retry | 43 from pylib.utils import timeout_retry |
| 43 from pylib.utils import zip_utils | 44 from pylib.utils import zip_utils |
| 44 | 45 |
| 45 _DEFAULT_TIMEOUT = 30 | 46 _DEFAULT_TIMEOUT = 30 |
| (...skipping 286 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 332 value = self.RunShellCommand('echo $EXTERNAL_STORAGE', | 333 value = self.RunShellCommand('echo $EXTERNAL_STORAGE', |
| 333 single_line=True, | 334 single_line=True, |
| 334 check_return=True) | 335 check_return=True) |
| 335 if not value: | 336 if not value: |
| 336 raise device_errors.CommandFailedError('$EXTERNAL_STORAGE is not set', | 337 raise device_errors.CommandFailedError('$EXTERNAL_STORAGE is not set', |
| 337 str(self)) | 338 str(self)) |
| 338 self._cache['external_storage'] = value | 339 self._cache['external_storage'] = value |
| 339 return value | 340 return value |
| 340 | 341 |
| 341 @decorators.WithTimeoutAndRetriesFromInstance() | 342 @decorators.WithTimeoutAndRetriesFromInstance() |
| 342 def GetApplicationPath(self, package, timeout=None, retries=None): | 343 def GetApplicationPaths(self, package, timeout=None, retries=None): |
| 343 """Get the path of the installed apk on the device for the given package. | 344 """Get the paths of the installed apks on the device for the given package. |
| 344 | 345 |
| 345 Args: | 346 Args: |
| 346 package: Name of the package. | 347 package: Name of the package. |
| 347 | 348 |
| 348 Returns: | 349 Returns: |
| 349 Path to the apk on the device if it exists, None otherwise. | 350 List of paths to the apks on the device if they exists, None otherwise. |
|
jbudorick
2015/06/19 17:32:13
nit: "None otherwise" is no longer accurate.
| |
| 350 """ | 351 """ |
| 351 # 'pm path' is liable to incorrectly exit with a nonzero number starting | 352 # 'pm path' is liable to incorrectly exit with a nonzero number starting |
| 352 # in Lollipop. | 353 # in Lollipop. |
| 353 # TODO(jbudorick): Check if this is fixed as new Android versions are | 354 # TODO(jbudorick): Check if this is fixed as new Android versions are |
| 354 # released to put an upper bound on this. | 355 # released to put an upper bound on this. |
| 355 should_check_return = (self.build_version_sdk < | 356 should_check_return = (self.build_version_sdk < |
| 356 constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP) | 357 constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP) |
| 357 output = self.RunShellCommand(['pm', 'path', package], single_line=True, | 358 output = self.RunShellCommand( |
| 358 check_return=should_check_return) | 359 ['pm', 'path', package], check_return=should_check_return) |
| 359 if not output: | 360 apks = [] |
| 360 return None | 361 for line in output: |
| 361 if not output.startswith('package:'): | 362 if not line.startswith('package:'): |
| 362 raise device_errors.CommandFailedError('pm path returned: %r' % output, | 363 raise device_errors.CommandFailedError( |
| 363 str(self)) | 364 'pm path returned: %r' % '\n'.join(output), str(self)) |
| 364 return output[len('package:'):] | 365 apks.append(line[len('package:'):]) |
| 366 return apks | |
| 365 | 367 |
| 366 @decorators.WithTimeoutAndRetriesFromInstance() | 368 @decorators.WithTimeoutAndRetriesFromInstance() |
| 367 def GetApplicationDataDirectory(self, package, timeout=None, retries=None): | 369 def GetApplicationDataDirectory(self, package, timeout=None, retries=None): |
| 368 """Get the data directory on the device for the given package. | 370 """Get the data directory on the device for the given package. |
| 369 | 371 |
| 370 Args: | 372 Args: |
| 371 package: Name of the package. | 373 package: Name of the package. |
| 372 | 374 |
| 373 Returns: | 375 Returns: |
| 374 The package's data directory, or None if the package doesn't exist on the | 376 The package's data directory, or None if the package doesn't exist on the |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 406 def sd_card_ready(): | 408 def sd_card_ready(): |
| 407 try: | 409 try: |
| 408 self.RunShellCommand(['test', '-d', self.GetExternalStoragePath()], | 410 self.RunShellCommand(['test', '-d', self.GetExternalStoragePath()], |
| 409 check_return=True) | 411 check_return=True) |
| 410 return True | 412 return True |
| 411 except device_errors.AdbCommandFailedError: | 413 except device_errors.AdbCommandFailedError: |
| 412 return False | 414 return False |
| 413 | 415 |
| 414 def pm_ready(): | 416 def pm_ready(): |
| 415 try: | 417 try: |
| 416 return self.GetApplicationPath('android') | 418 return self.GetApplicationPaths('android') |
| 417 except device_errors.CommandFailedError: | 419 except device_errors.CommandFailedError: |
| 418 return False | 420 return False |
| 419 | 421 |
| 420 def boot_completed(): | 422 def boot_completed(): |
| 421 return self.GetProp('sys.boot_completed') == '1' | 423 return self.GetProp('sys.boot_completed') == '1' |
| 422 | 424 |
| 423 def wifi_enabled(): | 425 def wifi_enabled(): |
| 424 return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi'], | 426 return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi'], |
| 425 check_return=False) | 427 check_return=False) |
| 426 | 428 |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 476 reinstall: A boolean indicating if we should keep any existing app data. | 478 reinstall: A boolean indicating if we should keep any existing app data. |
| 477 timeout: timeout in seconds | 479 timeout: timeout in seconds |
| 478 retries: number of retries | 480 retries: number of retries |
| 479 | 481 |
| 480 Raises: | 482 Raises: |
| 481 CommandFailedError if the installation fails. | 483 CommandFailedError if the installation fails. |
| 482 CommandTimeoutError if the installation times out. | 484 CommandTimeoutError if the installation times out. |
| 483 DeviceUnreachableError on missing device. | 485 DeviceUnreachableError on missing device. |
| 484 """ | 486 """ |
| 485 package_name = apk_helper.GetPackageName(apk_path) | 487 package_name = apk_helper.GetPackageName(apk_path) |
| 486 device_path = self.GetApplicationPath(package_name) | 488 device_paths = self.GetApplicationPaths(package_name) |
| 487 if device_path is not None: | 489 if device_paths: |
| 490 if len(device_paths) > 1: | |
| 491 logging.warning( | |
| 492 'Installing single APK (%s) when split APKs (%s) are currently ' | |
| 493 'installed.', apk_path, ' '.join(device_paths)) | |
| 488 (files_to_push, _) = self._GetChangedAndStaleFiles( | 494 (files_to_push, _) = self._GetChangedAndStaleFiles( |
| 489 apk_path, device_path) | 495 apk_path, device_paths[0]) |
| 490 should_install = bool(files_to_push) | 496 should_install = bool(files_to_push) |
| 491 if should_install and not reinstall: | 497 if should_install and not reinstall: |
| 492 self.adb.Uninstall(package_name) | 498 self.adb.Uninstall(package_name) |
| 493 else: | 499 else: |
| 494 should_install = True | 500 should_install = True |
| 495 if should_install: | 501 if should_install: |
| 496 self.adb.Install(apk_path, reinstall=reinstall) | 502 self.adb.Install(apk_path, reinstall=reinstall) |
| 497 | 503 |
| 504 @decorators.WithTimeoutAndRetriesDefaults( | |
| 505 INSTALL_DEFAULT_TIMEOUT, | |
| 506 INSTALL_DEFAULT_RETRIES) | |
| 507 def InstallSplitApk(self, base_apk, split_apks, reinstall=False, | |
| 508 timeout=None, retries=None): | |
| 509 """Install a split APK. | |
| 510 | |
| 511 Noop if all of the APK splits are already installed. | |
| 512 | |
| 513 Args: | |
| 514 base_apk: A string of the path to the base APK. | |
| 515 split_apks: A list of strings of paths of all of the APK splits. | |
| 516 reinstall: A boolean indicating if we should keep any existing app data. | |
| 517 timeout: timeout in seconds | |
| 518 retries: number of retries | |
| 519 | |
| 520 Raises: | |
| 521 CommandFailedError if the installation fails. | |
| 522 CommandTimeoutError if the installation times out. | |
| 523 DeviceUnreachableError on missing device. | |
| 524 DeviceVersionError if device SDK is less than Android L. | |
| 525 """ | |
| 526 self._CheckSdkLevel(constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP) | |
| 527 | |
| 528 all_apks = [base_apk] + split_select.SelectSplits( | |
| 529 self, base_apk, split_apks) | |
| 530 package_name = apk_helper.GetPackageName(base_apk) | |
| 531 device_apk_paths = self.GetApplicationPaths(package_name) | |
| 532 | |
| 533 if device_apk_paths: | |
| 534 partial_install_package = package_name | |
| 535 device_checksums = md5sum.CalculateDeviceMd5Sums(device_apk_paths, self) | |
| 536 host_checksums = md5sum.CalculateHostMd5Sums(all_apks) | |
| 537 apks_to_install = [k for (k, v) in host_checksums.iteritems() | |
| 538 if v not in device_checksums.values()] | |
| 539 if apks_to_install and not reinstall: | |
| 540 self.adb.Uninstall(package_name) | |
| 541 partial_install_package = None | |
| 542 apks_to_install = all_apks | |
| 543 else: | |
| 544 partial_install_package = None | |
| 545 apks_to_install = all_apks | |
| 546 if apks_to_install: | |
| 547 self.adb.InstallMultiple( | |
| 548 apks_to_install, partial=partial_install_package, reinstall=reinstall) | |
| 549 | |
| 550 def _CheckSdkLevel(self, required_sdk_level): | |
| 551 """Raises an exception if the device does not have the required SDK level. | |
| 552 """ | |
| 553 if self.build_version_sdk < required_sdk_level: | |
| 554 raise device_errors.DeviceVersionError( | |
| 555 ('Requires SDK level %s, device is SDK level %s' % | |
| 556 (required_sdk_level, self.build_version_sdk)), | |
| 557 device_serial=self.adb.GetDeviceSerial()) | |
| 558 | |
| 559 | |
| 498 @decorators.WithTimeoutAndRetriesFromInstance() | 560 @decorators.WithTimeoutAndRetriesFromInstance() |
| 499 def RunShellCommand(self, cmd, check_return=False, cwd=None, env=None, | 561 def RunShellCommand(self, cmd, check_return=False, cwd=None, env=None, |
| 500 as_root=False, single_line=False, large_output=False, | 562 as_root=False, single_line=False, large_output=False, |
| 501 timeout=None, retries=None): | 563 timeout=None, retries=None): |
| 502 """Run an ADB shell command. | 564 """Run an ADB shell command. |
| 503 | 565 |
| 504 The command to run |cmd| should be a sequence of program arguments or else | 566 The command to run |cmd| should be a sequence of program arguments or else |
| 505 a single string. | 567 a single string. |
| 506 | 568 |
| 507 When |cmd| is a sequence, it is assumed to contain the name of the command | 569 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... | |
| 799 | 861 |
| 800 Raises: | 862 Raises: |
| 801 CommandTimeoutError on timeout. | 863 CommandTimeoutError on timeout. |
| 802 DeviceUnreachableError on missing device. | 864 DeviceUnreachableError on missing device. |
| 803 """ | 865 """ |
| 804 # Check that the package exists before clearing it for android builds below | 866 # Check that the package exists before clearing it for android builds below |
| 805 # JB MR2. Necessary because calling pm clear on a package that doesn't exist | 867 # JB MR2. Necessary because calling pm clear on a package that doesn't exist |
| 806 # may never return. | 868 # may never return. |
| 807 if ((self.build_version_sdk >= | 869 if ((self.build_version_sdk >= |
| 808 constants.ANDROID_SDK_VERSION_CODES.JELLY_BEAN_MR2) | 870 constants.ANDROID_SDK_VERSION_CODES.JELLY_BEAN_MR2) |
| 809 or self.GetApplicationPath(package)): | 871 or self.GetApplicationPaths(package)): |
| 810 self.RunShellCommand(['pm', 'clear', package], check_return=True) | 872 self.RunShellCommand(['pm', 'clear', package], check_return=True) |
| 811 | 873 |
| 812 @decorators.WithTimeoutAndRetriesFromInstance() | 874 @decorators.WithTimeoutAndRetriesFromInstance() |
| 813 def SendKeyEvent(self, keycode, timeout=None, retries=None): | 875 def SendKeyEvent(self, keycode, timeout=None, retries=None): |
| 814 """Sends a keycode to the device. | 876 """Sends a keycode to the device. |
| 815 | 877 |
| 816 See the pylib.constants.keyevent module for suitable keycode values. | 878 See the pylib.constants.keyevent module for suitable keycode values. |
| 817 | 879 |
| 818 Args: | 880 Args: |
| 819 keycode: A integer keycode to send to the device. | 881 keycode: A integer keycode to send to the device. |
| (...skipping 466 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1286 | 1348 |
| 1287 # Next, check the current runtime value is what we need, and | 1349 # Next, check the current runtime value is what we need, and |
| 1288 # if not, set it and report that a reboot is required. | 1350 # if not, set it and report that a reboot is required. |
| 1289 value = self.GetProp(self.JAVA_ASSERT_PROPERTY) | 1351 value = self.GetProp(self.JAVA_ASSERT_PROPERTY) |
| 1290 if new_value != value: | 1352 if new_value != value: |
| 1291 self.SetProp(self.JAVA_ASSERT_PROPERTY, new_value) | 1353 self.SetProp(self.JAVA_ASSERT_PROPERTY, new_value) |
| 1292 return True | 1354 return True |
| 1293 else: | 1355 else: |
| 1294 return False | 1356 return False |
| 1295 | 1357 |
| 1358 @property | |
| 1359 def langauge(self): | |
|
jbudorick
2015/06/19 17:32:13
nit: langauge -> language
| |
| 1360 """Returns the language setting on the device.""" | |
| 1361 return self.GetProp('persist.sys.language', cache=False) | |
| 1362 | |
| 1363 @property | |
| 1364 def country(self): | |
| 1365 """Returns the country setting on the device.""" | |
| 1366 return self.GetProp('persist.sys.country', cache=False) | |
| 1367 | |
| 1368 @property | |
| 1369 def screen_density(self): | |
| 1370 """Returns the screen density of the device.""" | |
| 1371 DPI_TO_DENSITY = { | |
| 1372 120: 'ldpi', | |
| 1373 160: 'mdpi', | |
| 1374 240: 'hdpi', | |
| 1375 320: 'xhdpi', | |
| 1376 480: 'xxhdpi', | |
| 1377 } | |
| 1378 dpi = int(self.GetProp('ro.sf.lcd_density', cache=True)) | |
| 1379 return DPI_TO_DENSITY.get(dpi, 'tvdpi') | |
| 1296 | 1380 |
| 1297 @property | 1381 @property |
| 1298 def build_description(self): | 1382 def build_description(self): |
| 1299 """Returns the build description of the system. | 1383 """Returns the build description of the system. |
| 1300 | 1384 |
| 1301 For example: | 1385 For example: |
| 1302 nakasi-user 4.4.4 KTU84P 1227136 release-keys | 1386 nakasi-user 4.4.4 KTU84P 1227136 release-keys |
| 1303 """ | 1387 """ |
| 1304 return self.GetProp('ro.build.description', cache=True) | 1388 return self.GetProp('ro.build.description', cache=True) |
| 1305 | 1389 |
| (...skipping 320 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1626 @classmethod | 1710 @classmethod |
| 1627 def HealthyDevices(cls): | 1711 def HealthyDevices(cls): |
| 1628 blacklist = device_blacklist.ReadBlacklist() | 1712 blacklist = device_blacklist.ReadBlacklist() |
| 1629 def blacklisted(adb): | 1713 def blacklisted(adb): |
| 1630 if adb.GetDeviceSerial() in blacklist: | 1714 if adb.GetDeviceSerial() in blacklist: |
| 1631 logging.warning('Device %s is blacklisted.', adb.GetDeviceSerial()) | 1715 logging.warning('Device %s is blacklisted.', adb.GetDeviceSerial()) |
| 1632 return True | 1716 return True |
| 1633 return False | 1717 return False |
| 1634 | 1718 |
| 1635 return [cls(adb) for adb in adb_wrapper.AdbWrapper.Devices() | 1719 return [cls(adb) for adb in adb_wrapper.AdbWrapper.Devices() |
| 1636 if not blacklisted(adb)] | 1720 if not blacklisted(adb)] |
| 1637 | |
| OLD | NEW |