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

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

Issue 1234153004: Cache device apk checksums in device_utils.py (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@gtest-fast
Patch Set: Created 5 years, 5 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 158 matching lines...) Expand 10 before | Expand all | Expand 10 after
169 elif isinstance(device, pylib.android_commands.AndroidCommands): 169 elif isinstance(device, pylib.android_commands.AndroidCommands):
170 self.adb = adb_wrapper.AdbWrapper(device.GetDevice()) 170 self.adb = adb_wrapper.AdbWrapper(device.GetDevice())
171 self.old_interface = device 171 self.old_interface = device
172 else: 172 else:
173 raise ValueError('Unsupported device value: %r' % device) 173 raise ValueError('Unsupported device value: %r' % device)
174 self._commands_installed = None 174 self._commands_installed = None
175 self._default_timeout = default_timeout 175 self._default_timeout = default_timeout
176 self._default_retries = default_retries 176 self._default_retries = default_retries
177 self._cache = {} 177 self._cache = {}
178 self._client_caches = {} 178 self._client_caches = {}
179 self._package_to_device_apk_checksums_cache = {}
jbudorick 2015/07/16 16:31:33 This should use self._cache, which we already have
agrieve 2015/07/16 19:01:20 I think these caches are better split out since th
180 self._package_to_device_apk_paths_cache = {}
179 assert hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR) 181 assert hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR)
180 assert hasattr(self, decorators.DEFAULT_RETRIES_ATTR) 182 assert hasattr(self, decorators.DEFAULT_RETRIES_ATTR)
181 183
182 def __eq__(self, other): 184 def __eq__(self, other):
183 """Checks whether |other| refers to the same device as |self|. 185 """Checks whether |other| refers to the same device as |self|.
184 186
185 Args: 187 Args:
186 other: The object to compare to. This can be a basestring, an instance 188 other: The object to compare to. This can be a basestring, an instance
187 of adb_wrapper.AdbWrapper, or an instance of DeviceUtils. 189 of adb_wrapper.AdbWrapper, or an instance of DeviceUtils.
188 Returns: 190 Returns:
(...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after
336 value = self.RunShellCommand('echo $EXTERNAL_STORAGE', 338 value = self.RunShellCommand('echo $EXTERNAL_STORAGE',
337 single_line=True, 339 single_line=True,
338 check_return=True) 340 check_return=True)
339 if not value: 341 if not value:
340 raise device_errors.CommandFailedError('$EXTERNAL_STORAGE is not set', 342 raise device_errors.CommandFailedError('$EXTERNAL_STORAGE is not set',
341 str(self)) 343 str(self))
342 self._cache['external_storage'] = value 344 self._cache['external_storage'] = value
343 return value 345 return value
344 346
345 @decorators.WithTimeoutAndRetriesFromInstance() 347 @decorators.WithTimeoutAndRetriesFromInstance()
346 def GetApplicationPaths(self, package, timeout=None, retries=None): 348 def GetApplicationPaths(self, package, timeout=None, retries=None,
349 skip_cache=False):
jbudorick 2015/07/16 16:31:33 Do we envision external clients using this, or doe
agrieve 2015/07/16 19:01:21 Made it an internal helper.
347 """Get the paths of the installed apks on the device for the given package. 350 """Get the paths of the installed apks on the device for the given package.
348 351
349 Args: 352 Args:
350 package: Name of the package. 353 package: Name of the package.
351 354
352 Returns: 355 Returns:
353 List of paths to the apks on the device for the given package. 356 List of paths to the apks on the device for the given package.
354 """ 357 """
358 cached_result = self._package_to_device_apk_paths_cache.get(package)
359 if cached_result is not None and not skip_cache:
360 return list(cached_result)
355 # 'pm path' is liable to incorrectly exit with a nonzero number starting 361 # 'pm path' is liable to incorrectly exit with a nonzero number starting
356 # in Lollipop. 362 # in Lollipop.
357 # TODO(jbudorick): Check if this is fixed as new Android versions are 363 # TODO(jbudorick): Check if this is fixed as new Android versions are
358 # released to put an upper bound on this. 364 # released to put an upper bound on this.
359 should_check_return = (self.build_version_sdk < 365 should_check_return = (self.build_version_sdk <
360 constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP) 366 constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP)
361 output = self.RunShellCommand( 367 output = self.RunShellCommand(
362 ['pm', 'path', package], check_return=should_check_return) 368 ['pm', 'path', package], check_return=should_check_return)
363 apks = [] 369 apks = []
364 for line in output: 370 for line in output:
365 if not line.startswith('package:'): 371 if not line.startswith('package:'):
366 raise device_errors.CommandFailedError( 372 raise device_errors.CommandFailedError(
367 'pm path returned: %r' % '\n'.join(output), str(self)) 373 'pm path returned: %r' % '\n'.join(output), str(self))
368 apks.append(line[len('package:'):]) 374 apks.append(line[len('package:'):])
375 self._package_to_device_apk_paths_cache[package] = list(apks)
369 return apks 376 return apks
370 377
371 @decorators.WithTimeoutAndRetriesFromInstance() 378 @decorators.WithTimeoutAndRetriesFromInstance()
372 def GetApplicationDataDirectory(self, package, timeout=None, retries=None): 379 def GetApplicationDataDirectory(self, package, timeout=None, retries=None):
373 """Get the data directory on the device for the given package. 380 """Get the data directory on the device for the given package.
374 381
375 Args: 382 Args:
376 package: Name of the package. 383 package: Name of the package.
377 384
378 Returns: 385 Returns:
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
411 def sd_card_ready(): 418 def sd_card_ready():
412 try: 419 try:
413 self.RunShellCommand(['test', '-d', self.GetExternalStoragePath()], 420 self.RunShellCommand(['test', '-d', self.GetExternalStoragePath()],
414 check_return=True) 421 check_return=True)
415 return True 422 return True
416 except device_errors.AdbCommandFailedError: 423 except device_errors.AdbCommandFailedError:
417 return False 424 return False
418 425
419 def pm_ready(): 426 def pm_ready():
420 try: 427 try:
421 return self.GetApplicationPaths('android') 428 return self.GetApplicationPaths('android', skip_cache=True)
422 except device_errors.CommandFailedError: 429 except device_errors.CommandFailedError:
423 return False 430 return False
424 431
425 def boot_completed(): 432 def boot_completed():
426 return self.GetProp('sys.boot_completed') == '1' 433 return self.GetProp('sys.boot_completed') == '1'
427 434
428 def wifi_enabled(): 435 def wifi_enabled():
429 return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi'], 436 return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi'],
430 check_return=False) 437 check_return=False)
431 438
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
487 CommandTimeoutError if the installation times out. 494 CommandTimeoutError if the installation times out.
488 DeviceUnreachableError on missing device. 495 DeviceUnreachableError on missing device.
489 """ 496 """
490 package_name = apk_helper.GetPackageName(apk_path) 497 package_name = apk_helper.GetPackageName(apk_path)
491 device_paths = self.GetApplicationPaths(package_name) 498 device_paths = self.GetApplicationPaths(package_name)
492 if device_paths: 499 if device_paths:
493 if len(device_paths) > 1: 500 if len(device_paths) > 1:
494 logging.warning( 501 logging.warning(
495 'Installing single APK (%s) when split APKs (%s) are currently ' 502 'Installing single APK (%s) when split APKs (%s) are currently '
496 'installed.', apk_path, ' '.join(device_paths)) 503 'installed.', apk_path, ' '.join(device_paths))
497 (files_to_push, _) = self._GetChangedAndStaleFiles( 504 apks_to_install, host_checksums = (
498 apk_path, device_paths[0]) 505 self._ComputeStaleApks(package_name, [apk_path]))
499 should_install = bool(files_to_push) 506 should_install = bool(apks_to_install)
500 if should_install and not reinstall: 507 if should_install and not reinstall:
501 self.adb.Uninstall(package_name) 508 self._Uninstall(package_name)
502 else: 509 else:
503 should_install = True 510 should_install = True
511 host_checksums = None
504 if should_install: 512 if should_install:
505 self.adb.Install(apk_path, reinstall=reinstall) 513 # We won't know the resulting device apk names.
514 self._package_to_device_apk_paths_cache.pop(package_name, 0)
515 try:
516 self.adb.Install(apk_path, reinstall=reinstall)
517 self._package_to_device_apk_checksums_cache[package_name] = (
518 host_checksums)
519 except:
520 self._package_to_device_apk_paths_cache.pop(package_name, 0)
521 raise
506 522
507 @decorators.WithTimeoutAndRetriesDefaults( 523 @decorators.WithTimeoutAndRetriesDefaults(
508 INSTALL_DEFAULT_TIMEOUT, 524 INSTALL_DEFAULT_TIMEOUT,
509 INSTALL_DEFAULT_RETRIES) 525 INSTALL_DEFAULT_RETRIES)
510 def InstallSplitApk(self, base_apk, split_apks, reinstall=False, 526 def InstallSplitApk(self, base_apk, split_apks, reinstall=False,
511 timeout=None, retries=None): 527 timeout=None, retries=None):
512 """Install a split APK. 528 """Install a split APK.
513 529
514 Noop if all of the APK splits are already installed. 530 Noop if all of the APK splits are already installed.
515 531
(...skipping 12 matching lines...) Expand all
528 """ 544 """
529 self._CheckSdkLevel(constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP) 545 self._CheckSdkLevel(constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP)
530 546
531 all_apks = [base_apk] + split_select.SelectSplits( 547 all_apks = [base_apk] + split_select.SelectSplits(
532 self, base_apk, split_apks) 548 self, base_apk, split_apks)
533 package_name = apk_helper.GetPackageName(base_apk) 549 package_name = apk_helper.GetPackageName(base_apk)
534 device_apk_paths = self.GetApplicationPaths(package_name) 550 device_apk_paths = self.GetApplicationPaths(package_name)
535 551
536 if device_apk_paths: 552 if device_apk_paths:
537 partial_install_package = package_name 553 partial_install_package = package_name
538 device_checksums = md5sum.CalculateDeviceMd5Sums(device_apk_paths, self) 554 apks_to_install, host_checksums = (
539 host_checksums = md5sum.CalculateHostMd5Sums(all_apks) 555 self._ComputeStaleApks(package_name, all_apks))
540 apks_to_install = [k for (k, v) in host_checksums.iteritems()
541 if v not in device_checksums.values()]
542 if apks_to_install and not reinstall: 556 if apks_to_install and not reinstall:
543 self.adb.Uninstall(package_name) 557 self._Uninstall(package_name)
544 partial_install_package = None 558 partial_install_package = None
545 apks_to_install = all_apks 559 apks_to_install = all_apks
546 else: 560 else:
547 partial_install_package = None 561 partial_install_package = None
548 apks_to_install = all_apks 562 apks_to_install = all_apks
563 host_checksums = None
549 if apks_to_install: 564 if apks_to_install:
550 self.adb.InstallMultiple( 565 # We won't know the resulting device apk names.
551 apks_to_install, partial=partial_install_package, reinstall=reinstall) 566 self._package_to_device_apk_paths_cache.pop(package_name, 0)
567 try:
568 self.adb.InstallMultiple(
569 apks_to_install, partial=partial_install_package,
570 reinstall=reinstall)
571 self._package_to_device_apk_checksums_cache[package_name] = (
572 host_checksums)
573 except:
574 self._package_to_device_apk_paths_cache.pop(package_name, 0)
575 raise
576
577 def _Uninstall(self, package_name):
jbudorick 2015/07/16 16:31:32 This should _not_ be named "Uninstall". As-is, it'
agrieve 2015/07/16 19:01:21 Deleted in favour of hooking self.adb.Uninstall
578 try:
579 self.adb.Uninstall(package_name)
580 except:
581 # Clear cache since we can't be sure of the state.
582 self._package_to_device_apk_paths_cache.pop(package_name, 0)
583 self._package_to_device_apk_checksums_cache.pop(package_name, 0)
584 raise
585 self._package_to_device_apk_paths_cache[package_name] = []
586 self._package_to_device_apk_checksums_cache[package_name] = set()
552 587
553 def _CheckSdkLevel(self, required_sdk_level): 588 def _CheckSdkLevel(self, required_sdk_level):
554 """Raises an exception if the device does not have the required SDK level. 589 """Raises an exception if the device does not have the required SDK level.
555 """ 590 """
556 if self.build_version_sdk < required_sdk_level: 591 if self.build_version_sdk < required_sdk_level:
557 raise device_errors.DeviceVersionError( 592 raise device_errors.DeviceVersionError(
558 ('Requires SDK level %s, device is SDK level %s' % 593 ('Requires SDK level %s, device is SDK level %s' %
559 (required_sdk_level, self.build_version_sdk)), 594 (required_sdk_level, self.build_version_sdk)),
560 device_serial=self.adb.GetDeviceSerial()) 595 device_serial=self.adb.GetDeviceSerial())
561 596
(...skipping 448 matching lines...) Expand 10 before | Expand all | Expand 10 after
1010 to_push = [] 1045 to_push = []
1011 for host_abs_path, host_checksum in host_checksums.iteritems(): 1046 for host_abs_path, host_checksum in host_checksums.iteritems():
1012 device_abs_path = '%s/%s' % ( 1047 device_abs_path = '%s/%s' % (
1013 real_device_path, os.path.relpath(host_abs_path, real_host_path)) 1048 real_device_path, os.path.relpath(host_abs_path, real_host_path))
1014 device_checksum = device_checksums.pop(device_abs_path, None) 1049 device_checksum = device_checksums.pop(device_abs_path, None)
1015 if device_checksum != host_checksum: 1050 if device_checksum != host_checksum:
1016 to_push.append((host_abs_path, device_abs_path)) 1051 to_push.append((host_abs_path, device_abs_path))
1017 to_delete = device_checksums.keys() 1052 to_delete = device_checksums.keys()
1018 return (to_push, to_delete) 1053 return (to_push, to_delete)
1019 1054
1055 def _ComputeDeviceChecksumsForApks(self, package_name):
1056 ret = self._package_to_device_apk_checksums_cache.get(package_name)
1057 if ret is None:
1058 device_paths = self.GetApplicationPaths(package_name)
1059 file_to_checksums = md5sum.CalculateDeviceMd5Sums(device_paths, self)
1060 ret = set(file_to_checksums.values())
1061 self._package_to_device_apk_checksums_cache[package_name] = ret
1062 return ret
1063
1064 def _ComputeStaleApks(self, package_name, host_apk_paths):
1065 host_checksums = md5sum.CalculateHostMd5Sums(host_apk_paths)
1066 device_checksums = self._ComputeDeviceChecksumsForApks(package_name)
1067 stale_apks = [k for (k, v) in host_checksums.iteritems()
1068 if v not in device_checksums]
1069 return stale_apks, set(host_checksums.values())
1070
1020 def _PushFilesImpl(self, host_device_tuples, files): 1071 def _PushFilesImpl(self, host_device_tuples, files):
1021 size = sum(host_utils.GetRecursiveDiskUsage(h) for h, _ in files) 1072 size = sum(host_utils.GetRecursiveDiskUsage(h) for h, _ in files)
1022 file_count = len(files) 1073 file_count = len(files)
1023 dir_size = sum(host_utils.GetRecursiveDiskUsage(h) 1074 dir_size = sum(host_utils.GetRecursiveDiskUsage(h)
1024 for h, _ in host_device_tuples) 1075 for h, _ in host_device_tuples)
1025 dir_file_count = 0 1076 dir_file_count = 0
1026 for h, _ in host_device_tuples: 1077 for h, _ in host_device_tuples:
1027 if os.path.isdir(h): 1078 if os.path.isdir(h):
1028 dir_file_count += sum(len(f) for _r, _d, f in os.walk(h)) 1079 dir_file_count += sum(len(f) for _r, _d, f in os.walk(h))
1029 else: 1080 else:
(...skipping 675 matching lines...) Expand 10 before | Expand all | Expand 10 after
1705 """Returns client cache.""" 1756 """Returns client cache."""
1706 if client_name not in self._client_caches: 1757 if client_name not in self._client_caches:
1707 self._client_caches[client_name] = {} 1758 self._client_caches[client_name] = {}
1708 return self._client_caches[client_name] 1759 return self._client_caches[client_name]
1709 1760
1710 def _ClearCache(self): 1761 def _ClearCache(self):
1711 """Clears all caches.""" 1762 """Clears all caches."""
1712 for client in self._client_caches: 1763 for client in self._client_caches:
1713 self._client_caches[client].clear() 1764 self._client_caches[client].clear()
1714 self._cache.clear() 1765 self._cache.clear()
1766 self._package_to_device_apk_paths_cache.clear()
1767 self._package_to_device_apk_checksums_cache.clear()
1715 1768
1716 @classmethod 1769 @classmethod
1717 def parallel(cls, devices=None, async=False): 1770 def parallel(cls, devices=None, async=False):
1718 """Creates a Parallelizer to operate over the provided list of devices. 1771 """Creates a Parallelizer to operate over the provided list of devices.
1719 1772
1720 If |devices| is either |None| or an empty list, the Parallelizer will 1773 If |devices| is either |None| or an empty list, the Parallelizer will
1721 operate over all attached devices that have not been blacklisted. 1774 operate over all attached devices that have not been blacklisted.
1722 1775
1723 Args: 1776 Args:
1724 devices: A list of either DeviceUtils instances or objects from 1777 devices: A list of either DeviceUtils instances or objects from
(...skipping 20 matching lines...) Expand all
1745 def HealthyDevices(cls): 1798 def HealthyDevices(cls):
1746 blacklist = device_blacklist.ReadBlacklist() 1799 blacklist = device_blacklist.ReadBlacklist()
1747 def blacklisted(adb): 1800 def blacklisted(adb):
1748 if adb.GetDeviceSerial() in blacklist: 1801 if adb.GetDeviceSerial() in blacklist:
1749 logging.warning('Device %s is blacklisted.', adb.GetDeviceSerial()) 1802 logging.warning('Device %s is blacklisted.', adb.GetDeviceSerial())
1750 return True 1803 return True
1751 return False 1804 return False
1752 1805
1753 return [cls(adb) for adb in adb_wrapper.AdbWrapper.Devices() 1806 return [cls(adb) for adb in adb_wrapper.AdbWrapper.Devices()
1754 if not blacklisted(adb)] 1807 if not blacklisted(adb)]
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