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 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
186 else: | 186 else: |
187 raise ValueError('Unsupported device value: %r' % device) | 187 raise ValueError('Unsupported device value: %r' % device) |
188 self._commands_installed = None | 188 self._commands_installed = None |
189 self._default_timeout = default_timeout | 189 self._default_timeout = default_timeout |
190 self._default_retries = default_retries | 190 self._default_retries = default_retries |
191 self._cache = {} | 191 self._cache = {} |
192 self._client_caches = {} | 192 self._client_caches = {} |
193 assert hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR) | 193 assert hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR) |
194 assert hasattr(self, decorators.DEFAULT_RETRIES_ATTR) | 194 assert hasattr(self, decorators.DEFAULT_RETRIES_ATTR) |
195 | 195 |
196 self._ClearCache() | |
197 | |
196 def __eq__(self, other): | 198 def __eq__(self, other): |
197 """Checks whether |other| refers to the same device as |self|. | 199 """Checks whether |other| refers to the same device as |self|. |
198 | 200 |
199 Args: | 201 Args: |
200 other: The object to compare to. This can be a basestring, an instance | 202 other: The object to compare to. This can be a basestring, an instance |
201 of adb_wrapper.AdbWrapper, or an instance of DeviceUtils. | 203 of adb_wrapper.AdbWrapper, or an instance of DeviceUtils. |
202 Returns: | 204 Returns: |
203 Whether |other| refers to the same device as |self|. | 205 Whether |other| refers to the same device as |self|. |
204 """ | 206 """ |
205 return self.adb.GetDeviceSerial() == str(other) | 207 return self.adb.GetDeviceSerial() == str(other) |
(...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
359 @decorators.WithTimeoutAndRetriesFromInstance() | 361 @decorators.WithTimeoutAndRetriesFromInstance() |
360 def GetApplicationPaths(self, package, timeout=None, retries=None): | 362 def GetApplicationPaths(self, package, timeout=None, retries=None): |
361 """Get the paths of the installed apks on the device for the given package. | 363 """Get the paths of the installed apks on the device for the given package. |
362 | 364 |
363 Args: | 365 Args: |
364 package: Name of the package. | 366 package: Name of the package. |
365 | 367 |
366 Returns: | 368 Returns: |
367 List of paths to the apks on the device for the given package. | 369 List of paths to the apks on the device for the given package. |
368 """ | 370 """ |
371 return self._GetApplicationPathsInternal(package) | |
372 | |
373 def _GetApplicationPathsInternal(self, package, skip_cache=False): | |
374 cached_result = self._cache['package_apk_paths'].get(package) | |
375 if cached_result is not None and not skip_cache: | |
376 return list(cached_result) | |
369 # 'pm path' is liable to incorrectly exit with a nonzero number starting | 377 # 'pm path' is liable to incorrectly exit with a nonzero number starting |
370 # in Lollipop. | 378 # in Lollipop. |
371 # TODO(jbudorick): Check if this is fixed as new Android versions are | 379 # TODO(jbudorick): Check if this is fixed as new Android versions are |
372 # released to put an upper bound on this. | 380 # released to put an upper bound on this. |
373 should_check_return = (self.build_version_sdk < | 381 should_check_return = (self.build_version_sdk < |
374 constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP) | 382 constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP) |
375 output = self.RunShellCommand( | 383 output = self.RunShellCommand( |
376 ['pm', 'path', package], check_return=should_check_return) | 384 ['pm', 'path', package], check_return=should_check_return) |
377 apks = [] | 385 apks = [] |
378 for line in output: | 386 for line in output: |
379 if not line.startswith('package:'): | 387 if not line.startswith('package:'): |
380 raise device_errors.CommandFailedError( | 388 raise device_errors.CommandFailedError( |
381 'pm path returned: %r' % '\n'.join(output), str(self)) | 389 'pm path returned: %r' % '\n'.join(output), str(self)) |
382 apks.append(line[len('package:'):]) | 390 apks.append(line[len('package:'):]) |
391 self._cache['package_apk_paths'][package] = list(apks) | |
383 return apks | 392 return apks |
384 | 393 |
385 @decorators.WithTimeoutAndRetriesFromInstance() | 394 @decorators.WithTimeoutAndRetriesFromInstance() |
386 def GetApplicationDataDirectory(self, package, timeout=None, retries=None): | 395 def GetApplicationDataDirectory(self, package, timeout=None, retries=None): |
387 """Get the data directory on the device for the given package. | 396 """Get the data directory on the device for the given package. |
388 | 397 |
389 Args: | 398 Args: |
390 package: Name of the package. | 399 package: Name of the package. |
391 | 400 |
392 Returns: | 401 Returns: |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
425 def sd_card_ready(): | 434 def sd_card_ready(): |
426 try: | 435 try: |
427 self.RunShellCommand(['test', '-d', self.GetExternalStoragePath()], | 436 self.RunShellCommand(['test', '-d', self.GetExternalStoragePath()], |
428 check_return=True) | 437 check_return=True) |
429 return True | 438 return True |
430 except device_errors.AdbCommandFailedError: | 439 except device_errors.AdbCommandFailedError: |
431 return False | 440 return False |
432 | 441 |
433 def pm_ready(): | 442 def pm_ready(): |
434 try: | 443 try: |
435 return self.GetApplicationPaths('android') | 444 return self._GetApplicationPathsInternal('android', skip_cache=True) |
436 except device_errors.CommandFailedError: | 445 except device_errors.CommandFailedError: |
437 return False | 446 return False |
438 | 447 |
439 def boot_completed(): | 448 def boot_completed(): |
440 return self.GetProp('sys.boot_completed') == '1' | 449 return self.GetProp('sys.boot_completed') == '1' |
441 | 450 |
442 def wifi_enabled(): | 451 def wifi_enabled(): |
443 return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi'], | 452 return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi'], |
444 check_return=False) | 453 check_return=False) |
445 | 454 |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
495 reinstall: A boolean indicating if we should keep any existing app data. | 504 reinstall: A boolean indicating if we should keep any existing app data. |
496 timeout: timeout in seconds | 505 timeout: timeout in seconds |
497 retries: number of retries | 506 retries: number of retries |
498 | 507 |
499 Raises: | 508 Raises: |
500 CommandFailedError if the installation fails. | 509 CommandFailedError if the installation fails. |
501 CommandTimeoutError if the installation times out. | 510 CommandTimeoutError if the installation times out. |
502 DeviceUnreachableError on missing device. | 511 DeviceUnreachableError on missing device. |
503 """ | 512 """ |
504 package_name = apk_helper.GetPackageName(apk_path) | 513 package_name = apk_helper.GetPackageName(apk_path) |
505 device_paths = self.GetApplicationPaths(package_name) | 514 device_paths = self._GetApplicationPathsInternal(package_name) |
506 if device_paths: | 515 if device_paths: |
507 if len(device_paths) > 1: | 516 if len(device_paths) > 1: |
508 logging.warning( | 517 logging.warning( |
509 'Installing single APK (%s) when split APKs (%s) are currently ' | 518 'Installing single APK (%s) when split APKs (%s) are currently ' |
510 'installed.', apk_path, ' '.join(device_paths)) | 519 'installed.', apk_path, ' '.join(device_paths)) |
511 (files_to_push, _) = self._GetChangedAndStaleFiles( | 520 apks_to_install, host_checksums = ( |
512 apk_path, device_paths[0]) | 521 self._ComputeStaleApks(package_name, [apk_path])) |
513 should_install = bool(files_to_push) | 522 should_install = bool(apks_to_install) |
514 if should_install and not reinstall: | 523 if should_install and not reinstall: |
515 self.adb.Uninstall(package_name) | 524 self.Uninstall(package_name) |
516 else: | 525 else: |
517 should_install = True | 526 should_install = True |
527 host_checksums = None | |
518 if should_install: | 528 if should_install: |
jbudorick
2015/08/11 15:21:04
nit: I know this is how it was before, but can you
agrieve
2015/08/11 15:26:08
Done.
| |
529 # We won't know the resulting device apk names. | |
530 self._cache['package_apk_paths'].pop(package_name, 0) | |
519 self.adb.Install(apk_path, reinstall=reinstall) | 531 self.adb.Install(apk_path, reinstall=reinstall) |
532 self._cache['package_apk_checksums'][package_name] = host_checksums | |
520 | 533 |
521 @decorators.WithTimeoutAndRetriesDefaults( | 534 @decorators.WithTimeoutAndRetriesDefaults( |
522 INSTALL_DEFAULT_TIMEOUT, | 535 INSTALL_DEFAULT_TIMEOUT, |
523 INSTALL_DEFAULT_RETRIES) | 536 INSTALL_DEFAULT_RETRIES) |
524 def InstallSplitApk(self, base_apk, split_apks, reinstall=False, | 537 def InstallSplitApk(self, base_apk, split_apks, reinstall=False, |
525 timeout=None, retries=None): | 538 timeout=None, retries=None): |
526 """Install a split APK. | 539 """Install a split APK. |
527 | 540 |
528 Noop if all of the APK splits are already installed. | 541 Noop if all of the APK splits are already installed. |
529 | 542 |
530 Args: | 543 Args: |
531 base_apk: A string of the path to the base APK. | 544 base_apk: A string of the path to the base APK. |
532 split_apks: A list of strings of paths of all of the APK splits. | 545 split_apks: A list of strings of paths of all of the APK splits. |
533 reinstall: A boolean indicating if we should keep any existing app data. | 546 reinstall: A boolean indicating if we should keep any existing app data. |
534 timeout: timeout in seconds | 547 timeout: timeout in seconds |
535 retries: number of retries | 548 retries: number of retries |
536 | 549 |
537 Raises: | 550 Raises: |
538 CommandFailedError if the installation fails. | 551 CommandFailedError if the installation fails. |
539 CommandTimeoutError if the installation times out. | 552 CommandTimeoutError if the installation times out. |
540 DeviceUnreachableError on missing device. | 553 DeviceUnreachableError on missing device. |
541 DeviceVersionError if device SDK is less than Android L. | 554 DeviceVersionError if device SDK is less than Android L. |
542 """ | 555 """ |
543 self._CheckSdkLevel(constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP) | 556 self._CheckSdkLevel(constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP) |
544 | 557 |
545 all_apks = [base_apk] + split_select.SelectSplits( | 558 all_apks = [base_apk] + split_select.SelectSplits( |
546 self, base_apk, split_apks) | 559 self, base_apk, split_apks) |
547 package_name = apk_helper.GetPackageName(base_apk) | 560 package_name = apk_helper.GetPackageName(base_apk) |
548 device_apk_paths = self.GetApplicationPaths(package_name) | 561 device_apk_paths = self._GetApplicationPathsInternal(package_name) |
549 | 562 |
550 if device_apk_paths: | 563 if device_apk_paths: |
551 partial_install_package = package_name | 564 partial_install_package = package_name |
552 device_checksums = md5sum.CalculateDeviceMd5Sums(device_apk_paths, self) | 565 apks_to_install, host_checksums = ( |
553 host_checksums = md5sum.CalculateHostMd5Sums(all_apks) | 566 self._ComputeStaleApks(package_name, all_apks)) |
554 apks_to_install = [k for (k, v) in host_checksums.iteritems() | |
555 if v not in device_checksums.values()] | |
556 if apks_to_install and not reinstall: | 567 if apks_to_install and not reinstall: |
557 self.adb.Uninstall(package_name) | 568 self.Uninstall(package_name) |
558 partial_install_package = None | 569 partial_install_package = None |
559 apks_to_install = all_apks | 570 apks_to_install = all_apks |
560 else: | 571 else: |
561 partial_install_package = None | 572 partial_install_package = None |
562 apks_to_install = all_apks | 573 apks_to_install = all_apks |
574 host_checksums = None | |
563 if apks_to_install: | 575 if apks_to_install: |
jbudorick
2015/08/11 15:21:04
nit: same
agrieve
2015/08/11 15:26:08
Done.
| |
576 # We won't know the resulting device apk names. | |
577 self._cache['package_apk_paths'].pop(package_name, 0) | |
564 self.adb.InstallMultiple( | 578 self.adb.InstallMultiple( |
565 apks_to_install, partial=partial_install_package, reinstall=reinstall) | 579 apks_to_install, partial=partial_install_package, |
580 reinstall=reinstall) | |
581 self._cache['package_apk_checksums'][package_name] = host_checksums | |
582 | |
583 @decorators.WithTimeoutAndRetriesFromInstance() | |
584 def Uninstall(self, package_name, keep_data=False, timeout=None, | |
585 retries=None): | |
586 """Remove the app |package_name| from the device. | |
587 | |
588 Args: | |
589 package_name: The package to uninstall. | |
590 keep_data: (optional) Whether to keep the data and cache directories. | |
591 timeout: Timeout in seconds. | |
592 retries: Number of retries. | |
593 | |
594 Raises: | |
595 CommandFailedError if the uninstallation fails. | |
596 CommandTimeoutError if the uninstallation times out. | |
597 DeviceUnreachableError on missing device. | |
598 """ | |
599 try: | |
600 self.adb.Uninstall(package_name, keep_data) | |
601 self._cache['package_apk_paths'][package_name] = [] | |
602 self._cache['package_apk_checksums'][package_name] = set() | |
603 except: | |
jbudorick
2015/08/11 15:21:04
Is there really a functional difference between th
agrieve
2015/08/11 15:26:08
There is. In the first case, we're caching the fac
| |
604 # Clear cache since we can't be sure of the state. | |
605 self._cache['package_apk_paths'].pop(package_name, 0) | |
606 self._cache['package_apk_checksums'].pop(package_name, 0) | |
607 raise | |
566 | 608 |
567 def _CheckSdkLevel(self, required_sdk_level): | 609 def _CheckSdkLevel(self, required_sdk_level): |
568 """Raises an exception if the device does not have the required SDK level. | 610 """Raises an exception if the device does not have the required SDK level. |
569 """ | 611 """ |
570 if self.build_version_sdk < required_sdk_level: | 612 if self.build_version_sdk < required_sdk_level: |
571 raise device_errors.DeviceVersionError( | 613 raise device_errors.DeviceVersionError( |
572 ('Requires SDK level %s, device is SDK level %s' % | 614 ('Requires SDK level %s, device is SDK level %s' % |
573 (required_sdk_level, self.build_version_sdk)), | 615 (required_sdk_level, self.build_version_sdk)), |
574 device_serial=self.adb.GetDeviceSerial()) | 616 device_serial=self.adb.GetDeviceSerial()) |
575 | 617 |
(...skipping 331 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
907 | 949 |
908 Raises: | 950 Raises: |
909 CommandTimeoutError on timeout. | 951 CommandTimeoutError on timeout. |
910 DeviceUnreachableError on missing device. | 952 DeviceUnreachableError on missing device. |
911 """ | 953 """ |
912 # Check that the package exists before clearing it for android builds below | 954 # Check that the package exists before clearing it for android builds below |
913 # JB MR2. Necessary because calling pm clear on a package that doesn't exist | 955 # JB MR2. Necessary because calling pm clear on a package that doesn't exist |
914 # may never return. | 956 # may never return. |
915 if ((self.build_version_sdk >= | 957 if ((self.build_version_sdk >= |
916 constants.ANDROID_SDK_VERSION_CODES.JELLY_BEAN_MR2) | 958 constants.ANDROID_SDK_VERSION_CODES.JELLY_BEAN_MR2) |
917 or self.GetApplicationPaths(package)): | 959 or self._GetApplicationPathsInternal(package)): |
918 self.RunShellCommand(['pm', 'clear', package], check_return=True) | 960 self.RunShellCommand(['pm', 'clear', package], check_return=True) |
919 | 961 |
920 @decorators.WithTimeoutAndRetriesFromInstance() | 962 @decorators.WithTimeoutAndRetriesFromInstance() |
921 def SendKeyEvent(self, keycode, timeout=None, retries=None): | 963 def SendKeyEvent(self, keycode, timeout=None, retries=None): |
922 """Sends a keycode to the device. | 964 """Sends a keycode to the device. |
923 | 965 |
924 See the pylib.constants.keyevent module for suitable keycode values. | 966 See the pylib.constants.keyevent module for suitable keycode values. |
925 | 967 |
926 Args: | 968 Args: |
927 keycode: A integer keycode to send to the device. | 969 keycode: A integer keycode to send to the device. |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1031 to_push = [] | 1073 to_push = [] |
1032 for host_abs_path, host_checksum in host_checksums.iteritems(): | 1074 for host_abs_path, host_checksum in host_checksums.iteritems(): |
1033 device_abs_path = '%s/%s' % ( | 1075 device_abs_path = '%s/%s' % ( |
1034 real_device_path, os.path.relpath(host_abs_path, real_host_path)) | 1076 real_device_path, os.path.relpath(host_abs_path, real_host_path)) |
1035 device_checksum = device_checksums.pop(device_abs_path, None) | 1077 device_checksum = device_checksums.pop(device_abs_path, None) |
1036 if device_checksum != host_checksum: | 1078 if device_checksum != host_checksum: |
1037 to_push.append((host_abs_path, device_abs_path)) | 1079 to_push.append((host_abs_path, device_abs_path)) |
1038 to_delete = device_checksums.keys() | 1080 to_delete = device_checksums.keys() |
1039 return (to_push, to_delete) | 1081 return (to_push, to_delete) |
1040 | 1082 |
1083 def _ComputeDeviceChecksumsForApks(self, package_name): | |
1084 ret = self._cache['package_apk_checksums'].get(package_name) | |
1085 if ret is None: | |
jbudorick
2015/08/11 15:21:04
Does this work in the Uninstall case if you've set
agrieve
2015/08/11 15:26:08
Yes. set() is None == False
jbudorick
2015/08/11 15:29:53
er... I'm pretty sure that's wrong. None is the si
| |
1086 device_paths = self._GetApplicationPathsInternal(package_name) | |
1087 file_to_checksums = md5sum.CalculateDeviceMd5Sums(device_paths, self) | |
1088 ret = set(file_to_checksums.values()) | |
1089 self._cache['package_apk_checksums'][package_name] = ret | |
1090 return ret | |
1091 | |
1092 def _ComputeStaleApks(self, package_name, host_apk_paths): | |
1093 host_checksums = md5sum.CalculateHostMd5Sums(host_apk_paths) | |
1094 device_checksums = self._ComputeDeviceChecksumsForApks(package_name) | |
1095 stale_apks = [k for (k, v) in host_checksums.iteritems() | |
1096 if v not in device_checksums] | |
1097 return stale_apks, set(host_checksums.values()) | |
1098 | |
1041 def _PushFilesImpl(self, host_device_tuples, files): | 1099 def _PushFilesImpl(self, host_device_tuples, files): |
1042 size = sum(host_utils.GetRecursiveDiskUsage(h) for h, _ in files) | 1100 size = sum(host_utils.GetRecursiveDiskUsage(h) for h, _ in files) |
1043 file_count = len(files) | 1101 file_count = len(files) |
1044 dir_size = sum(host_utils.GetRecursiveDiskUsage(h) | 1102 dir_size = sum(host_utils.GetRecursiveDiskUsage(h) |
1045 for h, _ in host_device_tuples) | 1103 for h, _ in host_device_tuples) |
1046 dir_file_count = 0 | 1104 dir_file_count = 0 |
1047 for h, _ in host_device_tuples: | 1105 for h, _ in host_device_tuples: |
1048 if os.path.isdir(h): | 1106 if os.path.isdir(h): |
1049 dir_file_count += sum(len(f) for _r, _d, f in os.walk(h)) | 1107 dir_file_count += sum(len(f) for _r, _d, f in os.walk(h)) |
1050 else: | 1108 else: |
(...skipping 715 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1766 def GetClientCache(self, client_name): | 1824 def GetClientCache(self, client_name): |
1767 """Returns client cache.""" | 1825 """Returns client cache.""" |
1768 if client_name not in self._client_caches: | 1826 if client_name not in self._client_caches: |
1769 self._client_caches[client_name] = {} | 1827 self._client_caches[client_name] = {} |
1770 return self._client_caches[client_name] | 1828 return self._client_caches[client_name] |
1771 | 1829 |
1772 def _ClearCache(self): | 1830 def _ClearCache(self): |
1773 """Clears all caches.""" | 1831 """Clears all caches.""" |
1774 for client in self._client_caches: | 1832 for client in self._client_caches: |
1775 self._client_caches[client].clear() | 1833 self._client_caches[client].clear() |
1776 self._cache.clear() | 1834 self._cache = { |
1835 # Map of packageId -> list of on-device .apk paths | |
1836 'package_apk_paths': {}, | |
1837 # Map of packageId -> set of on-device .apk checksums | |
1838 'package_apk_checksums': {}, | |
1839 } | |
jbudorick
2015/08/11 15:21:04
nit: don't indent the closing brace
agrieve
2015/08/11 15:26:08
Done.
| |
1777 | 1840 |
1778 @classmethod | 1841 @classmethod |
1779 def parallel(cls, devices=None, async=False): | 1842 def parallel(cls, devices=None, async=False): |
1780 """Creates a Parallelizer to operate over the provided list of devices. | 1843 """Creates a Parallelizer to operate over the provided list of devices. |
1781 | 1844 |
1782 If |devices| is either |None| or an empty list, the Parallelizer will | 1845 If |devices| is either |None| or an empty list, the Parallelizer will |
1783 operate over all attached devices that have not been blacklisted. | 1846 operate over all attached devices that have not been blacklisted. |
1784 | 1847 |
1785 Args: | 1848 Args: |
1786 devices: A list of either DeviceUtils instances or objects from | 1849 devices: A list of either DeviceUtils instances or objects from |
(...skipping 28 matching lines...) Expand all Loading... | |
1815 return [cls(adb) for adb in adb_wrapper.AdbWrapper.Devices() | 1878 return [cls(adb) for adb in adb_wrapper.AdbWrapper.Devices() |
1816 if not blacklisted(adb)] | 1879 if not blacklisted(adb)] |
1817 | 1880 |
1818 @decorators.WithTimeoutAndRetriesFromInstance() | 1881 @decorators.WithTimeoutAndRetriesFromInstance() |
1819 def RestartAdbd(self, timeout=None, retries=None): | 1882 def RestartAdbd(self, timeout=None, retries=None): |
1820 logging.info('Restarting adbd on device.') | 1883 logging.info('Restarting adbd on device.') |
1821 with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script: | 1884 with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script: |
1822 self.WriteFile(script.name, _RESTART_ADBD_SCRIPT) | 1885 self.WriteFile(script.name, _RESTART_ADBD_SCRIPT) |
1823 self.RunShellCommand(['source', script.name], as_root=True) | 1886 self.RunShellCommand(['source', script.name], as_root=True) |
1824 self.adb.WaitForDevice() | 1887 self.adb.WaitForDevice() |
OLD | NEW |