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

Unified Diff: build/android/pylib/device/device_utils.py

Issue 1166113002: Add InstallSplitApk function to device utils. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 6 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « build/android/gyp/util/build_device.py ('k') | build/android/pylib/device/device_utils_test.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: build/android/pylib/device/device_utils.py
diff --git a/build/android/pylib/device/device_utils.py b/build/android/pylib/device/device_utils.py
index 967809ff8e2860d5846f181b780e1ed7c341dc33..c08d24956ea4b13aa191c44cca8b1d88cedefb86 100644
--- a/build/android/pylib/device/device_utils.py
+++ b/build/android/pylib/device/device_utils.py
@@ -202,6 +202,22 @@ class DeviceUtils(object):
"""Returns the device serial."""
return self.adb.GetDeviceSerial()
+ # pylint: disable=no-self-argument
mikecase (-- gone --) 2015/06/08 19:14:52 Guessing you won't like this function (kinda hacky
jbudorick 2015/06/08 19:38:26 should be in decorators.py
mikecase (-- gone --) 2015/06/17 22:00:42 Removed decorator. Added _CheckSdkLevel function.
+ def RequiresSdkLevel(requiredSdkLevel):
+ """Returns a decorator that checks that the device has the required
+ SDK level.
+ """
+ def decorator(f):
+ def wrapper(self, *args, **kwargs):
+ if self.build_version_sdk < requiredSdkLevel:
+ raise device_errors.DeviceVersionError(
+ ('%s requires SDK level %s, device is SDK level %s' %
+ (f.__name__, requiredSdkLevel, self.build_version_sdk)),
+ device_serial=self.adb.GetDeviceSerial())
+ f(self, *args, **kwargs)
+ return wrapper
+ return decorator
+
@decorators.WithTimeoutAndRetriesFromInstance()
def IsOnline(self, timeout=None, retries=None):
"""Checks whether the device is online.
@@ -339,14 +355,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 if they exists, None otherwise.
"""
# 'pm path' is liable to incorrectly exit with a nonzero number starting
# in Lollipop.
@@ -354,14 +370,14 @@ 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)
+ output = self.RunShellCommand(
+ ['pm', 'path', package], 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:'):]
+ if not all([s.startswith('package:') for s in output]):
+ raise device_errors.CommandFailedError(
+ 'pm path returned: %r' % '\n'.join(output), str(self))
+ return [s[len('package:'):] for s in output]
@decorators.WithTimeoutAndRetriesFromInstance()
def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None):
@@ -391,7 +407,7 @@ class DeviceUtils(object):
def pm_ready():
try:
- return self.GetApplicationPath('android')
+ return self.GetApplicationPaths('android')
except device_errors.CommandFailedError:
return False
@@ -461,9 +477,10 @@ 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:
- should_install = bool(self._GetChangedFilesImpl(apk_path, device_path))
+ device_paths = self.GetApplicationPaths(package_name)
+ if device_paths:
+ should_install = bool(
+ self._GetChangedFilesImpl(apk_path, device_paths[0]))
if should_install and not reinstall:
self.adb.Uninstall(package_name)
else:
@@ -471,6 +488,58 @@ class DeviceUtils(object):
if should_install:
self.adb.Install(apk_path, reinstall=reinstall)
+ @RequiresSdkLevel(constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP)
+ @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.
+ """
+ def select_splits():
jbudorick 2015/06/08 19:38:26 extract to its own module
mikecase (-- gone --) 2015/06/17 22:00:42 done
+ split_config = ('%s-r%s-%s:%s' %
+ (self.langauge_setting,
+ self.country_setting,
+ self.screen_density,
+ self.product_cpu_abi))
+ cmd = [os.path.join(constants.ANDROID_SDK_TOOLS, 'split-select'),
+ '--target', split_config, '--base', base_apk]
+ for split in split_apks:
+ cmd.extend(['--split', split])
+ return cmd_helper.GetCmdOutput(cmd).splitlines()
+
+ required_apk_splits = select_splits()
+ package_name = apk_helper.GetPackageName(base_apk)
+ device_apk_paths = self.GetApplicationPaths(package_name)
+ if device_apk_paths:
jbudorick 2015/06/08 19:38:26 I'm not sure about this part. It looks like it's i
mikecase (-- gone --) 2015/06/17 22:00:42 I think you have to install them all or nothing. I
+ device_checksums = md5sum.CalculateDeviceMd5Sums(
+ device_apk_paths, self).values()
+ host_checksums = md5sum.CalculateHostMd5Sums(
+ [base_apk] + required_apk_splits).values()
+ should_install = sorted(host_checksums) != sorted(device_checksums)
+ if should_install and not reinstall:
+ self.adb.Uninstall(package_name)
+ else:
+ should_install = True
+ if should_install:
+ self.adb.InstallMultiple([base_apk] + required_apk_splits,
+ reinstall=reinstall)
+
@decorators.WithTimeoutAndRetriesFromInstance()
def RunShellCommand(self, cmd, check_return=False, cwd=None, env=None,
as_root=False, single_line=False, large_output=False,
@@ -782,7 +851,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()
@@ -1244,6 +1313,28 @@ class DeviceUtils(object):
else:
return False
+ @property
+ def langauge_setting(self):
+ """Returns the language setting on the device."""
+ return self.GetProp('persist.sys.language', cache=False)
+
+ @property
+ def country_setting(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):
@@ -1584,5 +1675,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 previous file with comments | « build/android/gyp/util/build_device.py ('k') | build/android/pylib/device/device_utils_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698