diff options
Diffstat (limited to 'build/android/pylib/device/device_utils.py')
-rw-r--r-- | build/android/pylib/device/device_utils.py | 119 |
1 files changed, 101 insertions, 18 deletions
diff --git a/build/android/pylib/device/device_utils.py b/build/android/pylib/device/device_utils.py index 7420068..558461f 100644 --- a/build/android/pylib/device/device_utils.py +++ b/build/android/pylib/device/device_utils.py @@ -33,6 +33,7 @@ from pylib.device import device_errors from pylib.device import intent from pylib.device import logcat_monitor from pylib.device.commands import install_commands +from pylib.sdk import split_select from pylib.utils import apk_helper from pylib.utils import base_error from pylib.utils import device_temp_file @@ -339,14 +340,14 @@ class DeviceUtils(object): return value @decorators.WithTimeoutAndRetriesFromInstance() - def GetApplicationPath(self, package, timeout=None, retries=None): - """Get the path of the installed apk on the device for the given package. + def GetApplicationPaths(self, package, timeout=None, retries=None): + """Get the paths of the installed apks on the device for the given package. Args: package: Name of the package. Returns: - Path to the apk on the device if it exists, None otherwise. + List of paths to the apks on the device for the given package. """ # 'pm path' is liable to incorrectly exit with a nonzero number starting # in Lollipop. @@ -354,14 +355,15 @@ class DeviceUtils(object): # released to put an upper bound on this. should_check_return = (self.build_version_sdk < constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP) - output = self.RunShellCommand(['pm', 'path', package], single_line=True, - check_return=should_check_return) - if not output: - return None - if not output.startswith('package:'): - raise device_errors.CommandFailedError('pm path returned: %r' % output, - str(self)) - return output[len('package:'):] + output = self.RunShellCommand( + ['pm', 'path', package], check_return=should_check_return) + apks = [] + for line in output: + if not line.startswith('package:'): + raise device_errors.CommandFailedError( + 'pm path returned: %r' % '\n'.join(output), str(self)) + apks.append(line[len('package:'):]) + return apks @decorators.WithTimeoutAndRetriesFromInstance() def GetApplicationDataDirectory(self, package, timeout=None, retries=None): @@ -413,7 +415,7 @@ class DeviceUtils(object): def pm_ready(): try: - return self.GetApplicationPath('android') + return self.GetApplicationPaths('android') except device_errors.CommandFailedError: return False @@ -483,10 +485,14 @@ class DeviceUtils(object): DeviceUnreachableError on missing device. """ package_name = apk_helper.GetPackageName(apk_path) - device_path = self.GetApplicationPath(package_name) - if device_path is not None: + device_paths = self.GetApplicationPaths(package_name) + if device_paths: + if len(device_paths) > 1: + logging.warning( + 'Installing single APK (%s) when split APKs (%s) are currently ' + 'installed.', apk_path, ' '.join(device_paths)) (files_to_push, _) = self._GetChangedAndStaleFiles( - apk_path, device_path) + apk_path, device_paths[0]) should_install = bool(files_to_push) if should_install and not reinstall: self.adb.Uninstall(package_name) @@ -495,6 +501,62 @@ class DeviceUtils(object): if should_install: self.adb.Install(apk_path, reinstall=reinstall) + @decorators.WithTimeoutAndRetriesDefaults( + INSTALL_DEFAULT_TIMEOUT, + INSTALL_DEFAULT_RETRIES) + def InstallSplitApk(self, base_apk, split_apks, reinstall=False, + timeout=None, retries=None): + """Install a split APK. + + Noop if all of the APK splits are already installed. + + Args: + base_apk: A string of the path to the base APK. + split_apks: A list of strings of paths of all of the APK splits. + reinstall: A boolean indicating if we should keep any existing app data. + timeout: timeout in seconds + retries: number of retries + + Raises: + CommandFailedError if the installation fails. + CommandTimeoutError if the installation times out. + DeviceUnreachableError on missing device. + DeviceVersionError if device SDK is less than Android L. + """ + self._CheckSdkLevel(constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP) + + all_apks = [base_apk] + split_select.SelectSplits( + self, base_apk, split_apks) + package_name = apk_helper.GetPackageName(base_apk) + device_apk_paths = self.GetApplicationPaths(package_name) + + if device_apk_paths: + partial_install_package = package_name + device_checksums = md5sum.CalculateDeviceMd5Sums(device_apk_paths, self) + host_checksums = md5sum.CalculateHostMd5Sums(all_apks) + apks_to_install = [k for (k, v) in host_checksums.iteritems() + if v not in device_checksums.values()] + if apks_to_install and not reinstall: + self.adb.Uninstall(package_name) + partial_install_package = None + apks_to_install = all_apks + else: + partial_install_package = None + apks_to_install = all_apks + if apks_to_install: + self.adb.InstallMultiple( + apks_to_install, partial=partial_install_package, reinstall=reinstall) + + def _CheckSdkLevel(self, required_sdk_level): + """Raises an exception if the device does not have the required SDK level. + """ + if self.build_version_sdk < required_sdk_level: + raise device_errors.DeviceVersionError( + ('Requires SDK level %s, device is SDK level %s' % + (required_sdk_level, self.build_version_sdk)), + device_serial=self.adb.GetDeviceSerial()) + + @decorators.WithTimeoutAndRetriesFromInstance() def RunShellCommand(self, cmd, check_return=False, cwd=None, env=None, as_root=False, single_line=False, large_output=False, @@ -806,7 +868,7 @@ class DeviceUtils(object): # may never return. if ((self.build_version_sdk >= constants.ANDROID_SDK_VERSION_CODES.JELLY_BEAN_MR2) - or self.GetApplicationPath(package)): + or self.GetApplicationPaths(package)): self.RunShellCommand(['pm', 'clear', package], check_return=True) @decorators.WithTimeoutAndRetriesFromInstance() @@ -1293,6 +1355,28 @@ class DeviceUtils(object): else: return False + @property + def language(self): + """Returns the language setting on the device.""" + return self.GetProp('persist.sys.language', cache=False) + + @property + def country(self): + """Returns the country setting on the device.""" + return self.GetProp('persist.sys.country', cache=False) + + @property + def screen_density(self): + """Returns the screen density of the device.""" + DPI_TO_DENSITY = { + 120: 'ldpi', + 160: 'mdpi', + 240: 'hdpi', + 320: 'xhdpi', + 480: 'xxhdpi', + } + dpi = int(self.GetProp('ro.sf.lcd_density', cache=True)) + return DPI_TO_DENSITY.get(dpi, 'tvdpi') @property def build_description(self): @@ -1633,5 +1717,4 @@ class DeviceUtils(object): return False return [cls(adb) for adb in adb_wrapper.AdbWrapper.Devices() - if not blacklisted(adb)] - + if not blacklisted(adb)]
\ No newline at end of file |