Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(259)

Side by Side Diff: build/android/pylib/device/device_utils.py

Issue 1289503002: Add device md5sum caching to device_utils.py (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | build/android/pylib/device/device_utils_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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 # We need to flush the cache on uninstall, so wrap self.adb.Uninstall
197 # in order to ensure we detect calls to it.
198 old_adb_uninstall = self.adb.Uninstall
199 def WrappedAdbUninstall(package_name, *args, **kwargs):
200 try:
201 old_adb_uninstall(package_name, *args, **kwargs)
202 except:
203 # Clear cache since we can't be sure of the state.
204 self._cache['package_apk_paths'].pop(package_name, 0)
205 self._cache['package_apk_checksums'].pop(package_name, 0)
206 raise
207 self._cache['package_apk_paths'][package_name] = []
208 self._cache['package_apk_checksums'][package_name] = set()
209 self.adb.Uninstall = WrappedAdbUninstall
210
211 self._ClearCache()
212
196 def __eq__(self, other): 213 def __eq__(self, other):
197 """Checks whether |other| refers to the same device as |self|. 214 """Checks whether |other| refers to the same device as |self|.
198 215
199 Args: 216 Args:
200 other: The object to compare to. This can be a basestring, an instance 217 other: The object to compare to. This can be a basestring, an instance
201 of adb_wrapper.AdbWrapper, or an instance of DeviceUtils. 218 of adb_wrapper.AdbWrapper, or an instance of DeviceUtils.
202 Returns: 219 Returns:
203 Whether |other| refers to the same device as |self|. 220 Whether |other| refers to the same device as |self|.
204 """ 221 """
205 return self.adb.GetDeviceSerial() == str(other) 222 return self.adb.GetDeviceSerial() == str(other)
(...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after
359 @decorators.WithTimeoutAndRetriesFromInstance() 376 @decorators.WithTimeoutAndRetriesFromInstance()
360 def GetApplicationPaths(self, package, timeout=None, retries=None): 377 def GetApplicationPaths(self, package, timeout=None, retries=None):
361 """Get the paths of the installed apks on the device for the given package. 378 """Get the paths of the installed apks on the device for the given package.
362 379
363 Args: 380 Args:
364 package: Name of the package. 381 package: Name of the package.
365 382
366 Returns: 383 Returns:
367 List of paths to the apks on the device for the given package. 384 List of paths to the apks on the device for the given package.
368 """ 385 """
386 return self._GetApplicationPathsInternal(package)
387
388 def _GetApplicationPathsInternal(self, package, skip_cache=False):
389 cached_result = self._cache['package_apk_paths'].get(package)
390 if cached_result is not None and not skip_cache:
391 return list(cached_result)
369 # 'pm path' is liable to incorrectly exit with a nonzero number starting 392 # 'pm path' is liable to incorrectly exit with a nonzero number starting
370 # in Lollipop. 393 # in Lollipop.
371 # TODO(jbudorick): Check if this is fixed as new Android versions are 394 # TODO(jbudorick): Check if this is fixed as new Android versions are
372 # released to put an upper bound on this. 395 # released to put an upper bound on this.
373 should_check_return = (self.build_version_sdk < 396 should_check_return = (self.build_version_sdk <
374 constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP) 397 constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP)
375 output = self.RunShellCommand( 398 output = self.RunShellCommand(
376 ['pm', 'path', package], check_return=should_check_return) 399 ['pm', 'path', package], check_return=should_check_return)
377 apks = [] 400 apks = []
378 for line in output: 401 for line in output:
379 if not line.startswith('package:'): 402 if not line.startswith('package:'):
380 raise device_errors.CommandFailedError( 403 raise device_errors.CommandFailedError(
381 'pm path returned: %r' % '\n'.join(output), str(self)) 404 'pm path returned: %r' % '\n'.join(output), str(self))
382 apks.append(line[len('package:'):]) 405 apks.append(line[len('package:'):])
406 self._cache['package_apk_paths'][package] = list(apks)
383 return apks 407 return apks
384 408
385 @decorators.WithTimeoutAndRetriesFromInstance() 409 @decorators.WithTimeoutAndRetriesFromInstance()
386 def GetApplicationDataDirectory(self, package, timeout=None, retries=None): 410 def GetApplicationDataDirectory(self, package, timeout=None, retries=None):
387 """Get the data directory on the device for the given package. 411 """Get the data directory on the device for the given package.
388 412
389 Args: 413 Args:
390 package: Name of the package. 414 package: Name of the package.
391 415
392 Returns: 416 Returns:
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
425 def sd_card_ready(): 449 def sd_card_ready():
426 try: 450 try:
427 self.RunShellCommand(['test', '-d', self.GetExternalStoragePath()], 451 self.RunShellCommand(['test', '-d', self.GetExternalStoragePath()],
428 check_return=True) 452 check_return=True)
429 return True 453 return True
430 except device_errors.AdbCommandFailedError: 454 except device_errors.AdbCommandFailedError:
431 return False 455 return False
432 456
433 def pm_ready(): 457 def pm_ready():
434 try: 458 try:
435 return self.GetApplicationPaths('android') 459 return self._GetApplicationPathsInternal('android', skip_cache=True)
436 except device_errors.CommandFailedError: 460 except device_errors.CommandFailedError:
437 return False 461 return False
438 462
439 def boot_completed(): 463 def boot_completed():
440 return self.GetProp('sys.boot_completed') == '1' 464 return self.GetProp('sys.boot_completed') == '1'
441 465
442 def wifi_enabled(): 466 def wifi_enabled():
443 return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi'], 467 return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi'],
444 check_return=False) 468 check_return=False)
445 469
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
495 reinstall: A boolean indicating if we should keep any existing app data. 519 reinstall: A boolean indicating if we should keep any existing app data.
496 timeout: timeout in seconds 520 timeout: timeout in seconds
497 retries: number of retries 521 retries: number of retries
498 522
499 Raises: 523 Raises:
500 CommandFailedError if the installation fails. 524 CommandFailedError if the installation fails.
501 CommandTimeoutError if the installation times out. 525 CommandTimeoutError if the installation times out.
502 DeviceUnreachableError on missing device. 526 DeviceUnreachableError on missing device.
503 """ 527 """
504 package_name = apk_helper.GetPackageName(apk_path) 528 package_name = apk_helper.GetPackageName(apk_path)
505 device_paths = self.GetApplicationPaths(package_name) 529 device_paths = self._GetApplicationPathsInternal(package_name)
506 if device_paths: 530 if device_paths:
507 if len(device_paths) > 1: 531 if len(device_paths) > 1:
508 logging.warning( 532 logging.warning(
509 'Installing single APK (%s) when split APKs (%s) are currently ' 533 'Installing single APK (%s) when split APKs (%s) are currently '
510 'installed.', apk_path, ' '.join(device_paths)) 534 'installed.', apk_path, ' '.join(device_paths))
511 (files_to_push, _) = self._GetChangedAndStaleFiles( 535 apks_to_install, host_checksums = (
512 apk_path, device_paths[0]) 536 self._ComputeStaleApks(package_name, [apk_path]))
513 should_install = bool(files_to_push) 537 should_install = bool(apks_to_install)
514 if should_install and not reinstall: 538 if should_install and not reinstall:
515 self.adb.Uninstall(package_name) 539 self.adb.Uninstall(package_name)
516 else: 540 else:
517 should_install = True 541 should_install = True
542 host_checksums = None
518 if should_install: 543 if should_install:
544 # We won't know the resulting device apk names.
545 self._cache['package_apk_paths'].pop(package_name, 0)
519 self.adb.Install(apk_path, reinstall=reinstall) 546 self.adb.Install(apk_path, reinstall=reinstall)
547 self._cache['package_apk_checksums'][package_name] = host_checksums
520 548
521 @decorators.WithTimeoutAndRetriesDefaults( 549 @decorators.WithTimeoutAndRetriesDefaults(
522 INSTALL_DEFAULT_TIMEOUT, 550 INSTALL_DEFAULT_TIMEOUT,
523 INSTALL_DEFAULT_RETRIES) 551 INSTALL_DEFAULT_RETRIES)
524 def InstallSplitApk(self, base_apk, split_apks, reinstall=False, 552 def InstallSplitApk(self, base_apk, split_apks, reinstall=False,
525 timeout=None, retries=None): 553 timeout=None, retries=None):
526 """Install a split APK. 554 """Install a split APK.
527 555
528 Noop if all of the APK splits are already installed. 556 Noop if all of the APK splits are already installed.
529 557
530 Args: 558 Args:
531 base_apk: A string of the path to the base APK. 559 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. 560 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. 561 reinstall: A boolean indicating if we should keep any existing app data.
534 timeout: timeout in seconds 562 timeout: timeout in seconds
535 retries: number of retries 563 retries: number of retries
536 564
537 Raises: 565 Raises:
538 CommandFailedError if the installation fails. 566 CommandFailedError if the installation fails.
539 CommandTimeoutError if the installation times out. 567 CommandTimeoutError if the installation times out.
540 DeviceUnreachableError on missing device. 568 DeviceUnreachableError on missing device.
541 DeviceVersionError if device SDK is less than Android L. 569 DeviceVersionError if device SDK is less than Android L.
542 """ 570 """
543 self._CheckSdkLevel(constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP) 571 self._CheckSdkLevel(constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP)
544 572
545 all_apks = [base_apk] + split_select.SelectSplits( 573 all_apks = [base_apk] + split_select.SelectSplits(
546 self, base_apk, split_apks) 574 self, base_apk, split_apks)
547 package_name = apk_helper.GetPackageName(base_apk) 575 package_name = apk_helper.GetPackageName(base_apk)
548 device_apk_paths = self.GetApplicationPaths(package_name) 576 device_apk_paths = self._GetApplicationPathsInternal(package_name)
549 577
550 if device_apk_paths: 578 if device_apk_paths:
551 partial_install_package = package_name 579 partial_install_package = package_name
552 device_checksums = md5sum.CalculateDeviceMd5Sums(device_apk_paths, self) 580 apks_to_install, host_checksums = (
553 host_checksums = md5sum.CalculateHostMd5Sums(all_apks) 581 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: 582 if apks_to_install and not reinstall:
557 self.adb.Uninstall(package_name) 583 self.adb.Uninstall(package_name)
558 partial_install_package = None 584 partial_install_package = None
559 apks_to_install = all_apks 585 apks_to_install = all_apks
560 else: 586 else:
561 partial_install_package = None 587 partial_install_package = None
562 apks_to_install = all_apks 588 apks_to_install = all_apks
589 host_checksums = None
563 if apks_to_install: 590 if apks_to_install:
591 # We won't know the resulting device apk names.
592 self._cache['package_apk_paths'].pop(package_name, 0)
564 self.adb.InstallMultiple( 593 self.adb.InstallMultiple(
565 apks_to_install, partial=partial_install_package, reinstall=reinstall) 594 apks_to_install, partial=partial_install_package,
595 reinstall=reinstall)
596 self._cache['package_apk_checksums'][package_name] = host_checksums
566 597
567 def _CheckSdkLevel(self, required_sdk_level): 598 def _CheckSdkLevel(self, required_sdk_level):
568 """Raises an exception if the device does not have the required SDK level. 599 """Raises an exception if the device does not have the required SDK level.
569 """ 600 """
570 if self.build_version_sdk < required_sdk_level: 601 if self.build_version_sdk < required_sdk_level:
571 raise device_errors.DeviceVersionError( 602 raise device_errors.DeviceVersionError(
572 ('Requires SDK level %s, device is SDK level %s' % 603 ('Requires SDK level %s, device is SDK level %s' %
573 (required_sdk_level, self.build_version_sdk)), 604 (required_sdk_level, self.build_version_sdk)),
574 device_serial=self.adb.GetDeviceSerial()) 605 device_serial=self.adb.GetDeviceSerial())
575 606
(...skipping 331 matching lines...) Expand 10 before | Expand all | Expand 10 after
907 938
908 Raises: 939 Raises:
909 CommandTimeoutError on timeout. 940 CommandTimeoutError on timeout.
910 DeviceUnreachableError on missing device. 941 DeviceUnreachableError on missing device.
911 """ 942 """
912 # Check that the package exists before clearing it for android builds below 943 # 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 944 # JB MR2. Necessary because calling pm clear on a package that doesn't exist
914 # may never return. 945 # may never return.
915 if ((self.build_version_sdk >= 946 if ((self.build_version_sdk >=
916 constants.ANDROID_SDK_VERSION_CODES.JELLY_BEAN_MR2) 947 constants.ANDROID_SDK_VERSION_CODES.JELLY_BEAN_MR2)
917 or self.GetApplicationPaths(package)): 948 or self._GetApplicationPathsInternal(package)):
918 self.RunShellCommand(['pm', 'clear', package], check_return=True) 949 self.RunShellCommand(['pm', 'clear', package], check_return=True)
919 950
920 @decorators.WithTimeoutAndRetriesFromInstance() 951 @decorators.WithTimeoutAndRetriesFromInstance()
921 def SendKeyEvent(self, keycode, timeout=None, retries=None): 952 def SendKeyEvent(self, keycode, timeout=None, retries=None):
922 """Sends a keycode to the device. 953 """Sends a keycode to the device.
923 954
924 See the pylib.constants.keyevent module for suitable keycode values. 955 See the pylib.constants.keyevent module for suitable keycode values.
925 956
926 Args: 957 Args:
927 keycode: A integer keycode to send to the device. 958 keycode: A integer keycode to send to the device.
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
1031 to_push = [] 1062 to_push = []
1032 for host_abs_path, host_checksum in host_checksums.iteritems(): 1063 for host_abs_path, host_checksum in host_checksums.iteritems():
1033 device_abs_path = '%s/%s' % ( 1064 device_abs_path = '%s/%s' % (
1034 real_device_path, os.path.relpath(host_abs_path, real_host_path)) 1065 real_device_path, os.path.relpath(host_abs_path, real_host_path))
1035 device_checksum = device_checksums.pop(device_abs_path, None) 1066 device_checksum = device_checksums.pop(device_abs_path, None)
1036 if device_checksum != host_checksum: 1067 if device_checksum != host_checksum:
1037 to_push.append((host_abs_path, device_abs_path)) 1068 to_push.append((host_abs_path, device_abs_path))
1038 to_delete = device_checksums.keys() 1069 to_delete = device_checksums.keys()
1039 return (to_push, to_delete) 1070 return (to_push, to_delete)
1040 1071
1072 def _ComputeDeviceChecksumsForApks(self, package_name):
1073 ret = self._cache['package_apk_checksums'].get(package_name)
1074 if ret is None:
1075 device_paths = self._GetApplicationPathsInternal(package_name)
1076 file_to_checksums = md5sum.CalculateDeviceMd5Sums(device_paths, self)
1077 ret = set(file_to_checksums.values())
1078 self._cache['package_apk_checksums'][package_name] = ret
1079 return ret
1080
1081 def _ComputeStaleApks(self, package_name, host_apk_paths):
1082 host_checksums = md5sum.CalculateHostMd5Sums(host_apk_paths)
1083 device_checksums = self._ComputeDeviceChecksumsForApks(package_name)
1084 stale_apks = [k for (k, v) in host_checksums.iteritems()
1085 if v not in device_checksums]
1086 return stale_apks, set(host_checksums.values())
1087
1041 def _PushFilesImpl(self, host_device_tuples, files): 1088 def _PushFilesImpl(self, host_device_tuples, files):
1042 size = sum(host_utils.GetRecursiveDiskUsage(h) for h, _ in files) 1089 size = sum(host_utils.GetRecursiveDiskUsage(h) for h, _ in files)
1043 file_count = len(files) 1090 file_count = len(files)
1044 dir_size = sum(host_utils.GetRecursiveDiskUsage(h) 1091 dir_size = sum(host_utils.GetRecursiveDiskUsage(h)
1045 for h, _ in host_device_tuples) 1092 for h, _ in host_device_tuples)
1046 dir_file_count = 0 1093 dir_file_count = 0
1047 for h, _ in host_device_tuples: 1094 for h, _ in host_device_tuples:
1048 if os.path.isdir(h): 1095 if os.path.isdir(h):
1049 dir_file_count += sum(len(f) for _r, _d, f in os.walk(h)) 1096 dir_file_count += sum(len(f) for _r, _d, f in os.walk(h))
1050 else: 1097 else:
(...skipping 715 matching lines...) Expand 10 before | Expand all | Expand 10 after
1766 def GetClientCache(self, client_name): 1813 def GetClientCache(self, client_name):
1767 """Returns client cache.""" 1814 """Returns client cache."""
1768 if client_name not in self._client_caches: 1815 if client_name not in self._client_caches:
1769 self._client_caches[client_name] = {} 1816 self._client_caches[client_name] = {}
1770 return self._client_caches[client_name] 1817 return self._client_caches[client_name]
1771 1818
1772 def _ClearCache(self): 1819 def _ClearCache(self):
1773 """Clears all caches.""" 1820 """Clears all caches."""
1774 for client in self._client_caches: 1821 for client in self._client_caches:
1775 self._client_caches[client].clear() 1822 self._client_caches[client].clear()
1776 self._cache.clear() 1823 self._cache = {
1824 # Map of packageId -> list of on-device .apk paths
1825 'package_apk_paths': {},
1826 # Map of packageId -> set of on-device .apk checksums
1827 'package_apk_checksums': {},
1828 }
1777 1829
1778 @classmethod 1830 @classmethod
1779 def parallel(cls, devices=None, async=False): 1831 def parallel(cls, devices=None, async=False):
1780 """Creates a Parallelizer to operate over the provided list of devices. 1832 """Creates a Parallelizer to operate over the provided list of devices.
1781 1833
1782 If |devices| is either |None| or an empty list, the Parallelizer will 1834 If |devices| is either |None| or an empty list, the Parallelizer will
1783 operate over all attached devices that have not been blacklisted. 1835 operate over all attached devices that have not been blacklisted.
1784 1836
1785 Args: 1837 Args:
1786 devices: A list of either DeviceUtils instances or objects from 1838 devices: A list of either DeviceUtils instances or objects from
(...skipping 28 matching lines...) Expand all
1815 return [cls(adb) for adb in adb_wrapper.AdbWrapper.Devices() 1867 return [cls(adb) for adb in adb_wrapper.AdbWrapper.Devices()
1816 if not blacklisted(adb)] 1868 if not blacklisted(adb)]
1817 1869
1818 @decorators.WithTimeoutAndRetriesFromInstance() 1870 @decorators.WithTimeoutAndRetriesFromInstance()
1819 def RestartAdbd(self, timeout=None, retries=None): 1871 def RestartAdbd(self, timeout=None, retries=None):
1820 logging.info('Restarting adbd on device.') 1872 logging.info('Restarting adbd on device.')
1821 with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script: 1873 with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script:
1822 self.WriteFile(script.name, _RESTART_ADBD_SCRIPT) 1874 self.WriteFile(script.name, _RESTART_ADBD_SCRIPT)
1823 self.RunShellCommand(['source', script.name], as_root=True) 1875 self.RunShellCommand(['source', script.name], as_root=True)
1824 self.adb.WaitForDevice() 1876 self.adb.WaitForDevice()
OLDNEW
« no previous file with comments | « no previous file | build/android/pylib/device/device_utils_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698