summaryrefslogtreecommitdiffstats
path: root/build
diff options
context:
space:
mode:
authormikecase <mikecase@chromium.org>2015-06-19 13:43:39 -0700
committerCommit bot <commit-bot@chromium.org>2015-06-19 20:44:05 +0000
commit89d5ad7274ce871af5f4cd3f9f0a09b9ff90cf02 (patch)
tree287501ed37d8126356d48f06b29d7d87abcaefaa /build
parent7e337bc2da8af6b1921d80d0e7581b2f12575792 (diff)
downloadchromium_src-89d5ad7274ce871af5f4cd3f9f0a09b9ff90cf02.zip
chromium_src-89d5ad7274ce871af5f4cd3f9f0a09b9ff90cf02.tar.gz
chromium_src-89d5ad7274ce871af5f4cd3f9f0a09b9ff90cf02.tar.bz2
Add InstallSplitApk function to device utils.
Adding a InstallSplitApk function to device utils so that our test runner will be able to support install/testing split apks. BUG= Review URL: https://codereview.chromium.org/1166113002 Cr-Commit-Position: refs/heads/master@{#335336}
Diffstat (limited to 'build')
-rwxr-xr-xbuild/android/gyp/apk_install.py48
-rw-r--r--build/android/gyp/util/build_device.py3
-rw-r--r--build/android/pylib/device/device_utils.py119
-rwxr-xr-xbuild/android/pylib/device/device_utils_test.py108
-rw-r--r--build/android/pylib/sdk/split_select.py66
-rwxr-xr-xbuild/android/update_verification.py2
6 files changed, 246 insertions, 100 deletions
diff --git a/build/android/gyp/apk_install.py b/build/android/gyp/apk_install.py
index c909867..a512e50 100755
--- a/build/android/gyp/apk_install.py
+++ b/build/android/gyp/apk_install.py
@@ -23,28 +23,6 @@ sys.path.append(BUILD_ANDROID_DIR)
from pylib import constants
from pylib.utils import apk_helper
-_DPI_TO_DENSITY = {
- 120: 'ldpi',
- 160: 'mdpi',
- 240: 'hdpi',
- 320: 'xhdpi',
- 480: 'xxhdpi',
- }
-
-
-def RetrieveDeviceConfig(device):
- """Probes the given device for its split-select config.
-
- For example: en-rUS-xhdpi:armeabi-v7a
- Run "split-select --help" for more info about the format.
- """
- language = device.GetProp('persist.sys.language')
- country = device.GetProp('persist.sys.country')
- density_dpi = int(device.GetProp('ro.sf.lcd_density'))
- density = _DPI_TO_DENSITY.get(density_dpi, 'tvdpi')
- abi = device.product_cpu_abi
- return '%s-r%s-%s:%s' % (language, country, density, abi)
-
def GetNewMetadata(device, apk_package):
"""Gets the metadata on the device for the apk_package apk."""
@@ -114,34 +92,10 @@ def main():
# the build, then the APK has to be installed (regardless of the md5 record).
force_install = HasInstallMetadataChanged(device, apk_package, metadata_path)
- def SelectSplits(target_config, base_apk, split_apks, android_sdk_tools):
- cmd = [os.path.join(android_sdk_tools, 'split-select'),
- '--target', target_config,
- '--base', base_apk,
- ]
- for split in split_apks:
- cmd.extend(('--split', split))
-
- # split-select outputs one path per line and a blank line at the end.
- output = build_utils.CheckOutput(cmd)
- return [x for x in output.split('\n') if x]
def Install():
if options.split_apk_path:
- requiredSdkVersion = constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP
- actualSdkVersion = device.device.build_version_sdk
- if actualSdkVersion < requiredSdkVersion:
- raise Exception(('--split-apk-path requires sdk version %s. Device has '
- 'version %s') % (requiredSdkVersion, actualSdkVersion))
- device_config = RetrieveDeviceConfig(device.device)
- active_splits = SelectSplits(
- device_config,
- options.apk_path,
- options.split_apk_path,
- options.android_sdk_tools)
-
- all_apks = [options.apk_path] + active_splits
- device.device.adb.InstallMultiple(all_apks, reinstall=True)
+ device.InstallSplitApk(options.apk_path, options.split_apk_path)
else:
device.Install(options.apk_path, reinstall=True)
diff --git a/build/android/gyp/util/build_device.py b/build/android/gyp/util/build_device.py
index 7e0d57b..8ab1112 100644
--- a/build/android/gyp/util/build_device.py
+++ b/build/android/gyp/util/build_device.py
@@ -42,6 +42,9 @@ class BuildDevice(object):
def Install(self, *args, **kwargs):
return self.device.Install(*args, **kwargs)
+ def InstallSplitApk(self, *args, **kwargs):
+ return self.device.InstallSplitApk(*args, **kwargs)
+
def GetInstallMetadata(self, apk_package):
"""Gets the metadata on the device for the apk_package apk."""
# Matches lines like:
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
diff --git a/build/android/pylib/device/device_utils_test.py b/build/android/pylib/device/device_utils_test.py
index 2517f66..8cb4e3c 100755
--- a/build/android/pylib/device/device_utils_test.py
+++ b/build/android/pylib/device/device_utils_test.py
@@ -27,6 +27,7 @@ from pylib.device import adb_wrapper
from pylib.device import device_errors
from pylib.device import device_utils
from pylib.device import intent
+from pylib.sdk import split_select
from pylib.utils import mock_calls
# RunCommand from third_party/android_testrunner/run_command.py is mocked
@@ -310,30 +311,30 @@ class DeviceUtilsGetExternalStoragePathTest(DeviceUtilsTest):
self.device.GetExternalStoragePath()
-class DeviceUtilsGetApplicationPathTest(DeviceUtilsTest):
+class DeviceUtilsGetApplicationPathsTest(DeviceUtilsTest):
- def testGetApplicationPath_exists(self):
+ def testGetApplicationPaths_exists(self):
with self.assertCalls(
(self.call.adb.Shell('getprop ro.build.version.sdk'), '19\n'),
(self.call.adb.Shell('pm path android'),
'package:/path/to/android.apk\n')):
- self.assertEquals('/path/to/android.apk',
- self.device.GetApplicationPath('android'))
+ self.assertEquals(['/path/to/android.apk'],
+ self.device.GetApplicationPaths('android'))
- def testGetApplicationPath_notExists(self):
+ def testGetApplicationPaths_notExists(self):
with self.assertCalls(
(self.call.adb.Shell('getprop ro.build.version.sdk'), '19\n'),
(self.call.adb.Shell('pm path not.installed.app'), '')):
- self.assertEquals(None,
- self.device.GetApplicationPath('not.installed.app'))
+ self.assertEquals([],
+ self.device.GetApplicationPaths('not.installed.app'))
- def testGetApplicationPath_fails(self):
+ def testGetApplicationPaths_fails(self):
with self.assertCalls(
(self.call.adb.Shell('getprop ro.build.version.sdk'), '19\n'),
(self.call.adb.Shell('pm path android'),
self.CommandError('ERROR. Is package manager running?\n'))):
with self.assertRaises(device_errors.CommandFailedError):
- self.device.GetApplicationPath('android')
+ self.device.GetApplicationPaths('android')
class DeviceUtilsGetApplicationDataDirectoryTest(DeviceUtilsTest):
@@ -365,8 +366,8 @@ class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsTest):
(self.call.device.GetExternalStoragePath(), '/fake/storage/path'),
(self.call.adb.Shell('test -d /fake/storage/path'), ''),
# pm_ready
- (self.call.device.GetApplicationPath('android'),
- 'package:/some/fake/path'),
+ (self.call.device.GetApplicationPaths('android'),
+ ['package:/some/fake/path']),
# boot_completed
(self.call.device.GetProp('sys.boot_completed'), '1')):
self.device.WaitUntilFullyBooted(wifi=False)
@@ -378,8 +379,8 @@ class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsTest):
(self.call.device.GetExternalStoragePath(), '/fake/storage/path'),
(self.call.adb.Shell('test -d /fake/storage/path'), ''),
# pm_ready
- (self.call.device.GetApplicationPath('android'),
- 'package:/some/fake/path'),
+ (self.call.device.GetApplicationPaths('android'),
+ ['package:/some/fake/path']),
# boot_completed
(self.call.device.GetProp('sys.boot_completed'), '1'),
# wifi_enabled
@@ -402,8 +403,8 @@ class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsTest):
(self.call.device.GetExternalStoragePath(), '/fake/storage/path'),
(self.call.adb.Shell('test -d /fake/storage/path'), ''),
# pm_ready
- (self.call.device.GetApplicationPath('android'),
- 'package:/some/fake/path'),
+ (self.call.device.GetApplicationPaths('android'),
+ ['package:/some/fake/path']),
# boot_completed
(self.call.device.GetProp('sys.boot_completed'), '1')):
self.device.WaitUntilFullyBooted(wifi=False)
@@ -439,11 +440,11 @@ class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsTest):
(self.call.device.GetExternalStoragePath(), '/fake/storage/path'),
(self.call.adb.Shell('test -d /fake/storage/path'), ''),
# pm_ready
- (self.call.device.GetApplicationPath('android'), self.CommandError()),
+ (self.call.device.GetApplicationPaths('android'), self.CommandError()),
# pm_ready
- (self.call.device.GetApplicationPath('android'), self.CommandError()),
+ (self.call.device.GetApplicationPaths('android'), self.CommandError()),
# pm_ready
- (self.call.device.GetApplicationPath('android'), self.TimeoutError())):
+ (self.call.device.GetApplicationPaths('android'), self.TimeoutError())):
with self.assertRaises(device_errors.CommandTimeoutError):
self.device.WaitUntilFullyBooted(wifi=False)
@@ -454,8 +455,8 @@ class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsTest):
(self.call.device.GetExternalStoragePath(), '/fake/storage/path'),
(self.call.adb.Shell('test -d /fake/storage/path'), ''),
# pm_ready
- (self.call.device.GetApplicationPath('android'),
- 'package:/some/fake/path'),
+ (self.call.device.GetApplicationPaths('android'),
+ ['package:/some/fake/path']),
# boot_completed
(self.call.device.GetProp('sys.boot_completed'), '0'),
# boot_completed
@@ -472,8 +473,8 @@ class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsTest):
(self.call.device.GetExternalStoragePath(), '/fake/storage/path'),
(self.call.adb.Shell('test -d /fake/storage/path'), ''),
# pm_ready
- (self.call.device.GetApplicationPath('android'),
- 'package:/some/fake/path'),
+ (self.call.device.GetApplicationPaths('android'),
+ ['package:/some/fake/path']),
# boot_completed
(self.call.device.GetProp('sys.boot_completed'), '1'),
# wifi_enabled
@@ -519,7 +520,7 @@ class DeviceUtilsInstallTest(DeviceUtilsTest):
with self.assertCalls(
(mock.call.pylib.utils.apk_helper.GetPackageName('/fake/test/app.apk'),
'this.is.a.test.package'),
- (self.call.device.GetApplicationPath('this.is.a.test.package'), None),
+ (self.call.device.GetApplicationPaths('this.is.a.test.package'), []),
self.call.adb.Install('/fake/test/app.apk', reinstall=False)):
self.device.Install('/fake/test/app.apk', retries=0)
@@ -527,8 +528,8 @@ class DeviceUtilsInstallTest(DeviceUtilsTest):
with self.assertCalls(
(mock.call.pylib.utils.apk_helper.GetPackageName('/fake/test/app.apk'),
'this.is.a.test.package'),
- (self.call.device.GetApplicationPath('this.is.a.test.package'),
- '/fake/data/app/this.is.a.test.package.apk'),
+ (self.call.device.GetApplicationPaths('this.is.a.test.package'),
+ ['/fake/data/app/this.is.a.test.package.apk']),
(self.call.device._GetChangedAndStaleFiles(
'/fake/test/app.apk', '/fake/data/app/this.is.a.test.package.apk'),
([('/fake/test/app.apk', '/fake/data/app/this.is.a.test.package.apk')],
@@ -541,8 +542,8 @@ class DeviceUtilsInstallTest(DeviceUtilsTest):
with self.assertCalls(
(mock.call.pylib.utils.apk_helper.GetPackageName('/fake/test/app.apk'),
'this.is.a.test.package'),
- (self.call.device.GetApplicationPath('this.is.a.test.package'),
- '/fake/data/app/this.is.a.test.package.apk'),
+ (self.call.device.GetApplicationPaths('this.is.a.test.package'),
+ ['/fake/data/app/this.is.a.test.package.apk']),
(self.call.device._GetChangedAndStaleFiles(
'/fake/test/app.apk', '/fake/data/app/this.is.a.test.package.apk'),
([('/fake/test/app.apk', '/fake/data/app/this.is.a.test.package.apk')],
@@ -554,8 +555,8 @@ class DeviceUtilsInstallTest(DeviceUtilsTest):
with self.assertCalls(
(mock.call.pylib.utils.apk_helper.GetPackageName('/fake/test/app.apk'),
'this.is.a.test.package'),
- (self.call.device.GetApplicationPath('this.is.a.test.package'),
- '/fake/data/app/this.is.a.test.package.apk'),
+ (self.call.device.GetApplicationPaths('this.is.a.test.package'),
+ ['/fake/data/app/this.is.a.test.package.apk']),
(self.call.device._GetChangedAndStaleFiles(
'/fake/test/app.apk', '/fake/data/app/this.is.a.test.package.apk'),
([], []))):
@@ -565,12 +566,51 @@ class DeviceUtilsInstallTest(DeviceUtilsTest):
with self.assertCalls(
(mock.call.pylib.utils.apk_helper.GetPackageName('/fake/test/app.apk'),
'this.is.a.test.package'),
- (self.call.device.GetApplicationPath('this.is.a.test.package'), None),
+ (self.call.device.GetApplicationPaths('this.is.a.test.package'), []),
(self.call.adb.Install('/fake/test/app.apk', reinstall=False),
self.CommandError('Failure\r\n'))):
with self.assertRaises(device_errors.CommandFailedError):
self.device.Install('/fake/test/app.apk', retries=0)
+class DeviceUtilsInstallSplitApkTest(DeviceUtilsTest):
+
+ def testInstallSplitApk_noPriorInstall(self):
+ with self.assertCalls(
+ (self.call.device._CheckSdkLevel(21)),
+ (mock.call.pylib.sdk.split_select.SelectSplits(
+ self.device, 'base.apk',
+ ['split1.apk', 'split2.apk', 'split3.apk']),
+ ['split2.apk']),
+ (mock.call.pylib.utils.apk_helper.GetPackageName('base.apk'),
+ 'this.is.a.test.package'),
+ (self.call.device.GetApplicationPaths('this.is.a.test.package'), []),
+ (self.call.adb.InstallMultiple(
+ ['base.apk', 'split2.apk'], partial=None, reinstall=False))):
+ self.device.InstallSplitApk('base.apk',
+ ['split1.apk', 'split2.apk', 'split3.apk'], retries=0)
+
+ def testInstallSplitApk_partialInstall(self):
+ with self.assertCalls(
+ (self.call.device._CheckSdkLevel(21)),
+ (mock.call.pylib.sdk.split_select.SelectSplits(
+ self.device, 'base.apk',
+ ['split1.apk', 'split2.apk', 'split3.apk']),
+ ['split2.apk']),
+ (mock.call.pylib.utils.apk_helper.GetPackageName('base.apk'),
+ 'test.package'),
+ (self.call.device.GetApplicationPaths('test.package'),
+ ['base-on-device.apk', 'split2-on-device.apk']),
+ (mock.call.pylib.utils.md5sum.CalculateDeviceMd5Sums(
+ ['base-on-device.apk', 'split2-on-device.apk'], self.device),
+ {'base-on-device.apk': 'AAA', 'split2-on-device.apk': 'BBB'}),
+ (mock.call.pylib.utils.md5sum.CalculateHostMd5Sums(
+ ['base.apk', 'split2.apk']),
+ {'base.apk': 'AAA', 'split2.apk': 'CCC'}),
+ (self.call.adb.InstallMultiple(
+ ['split2.apk'], partial='test.package', reinstall=True))):
+ self.device.InstallSplitApk('base.apk',
+ ['split1.apk', 'split2.apk', 'split3.apk'], reinstall=True, retries=0)
+
class DeviceUtilsRunShellCommandTest(DeviceUtilsTest):
@@ -1081,8 +1121,8 @@ class DeviceUtilsClearApplicationStateTest(DeviceUtilsTest):
def testClearApplicationState_packageDoesntExist(self):
with self.assertCalls(
(self.call.adb.Shell('getprop ro.build.version.sdk'), '17\n'),
- (self.call.device.GetApplicationPath('this.package.does.not.exist'),
- None)):
+ (self.call.device.GetApplicationPaths('this.package.does.not.exist'),
+ [])):
self.device.ClearApplicationState('this.package.does.not.exist')
def testClearApplicationState_packageDoesntExistOnAndroidJBMR2OrAbove(self):
@@ -1095,8 +1135,8 @@ class DeviceUtilsClearApplicationStateTest(DeviceUtilsTest):
def testClearApplicationState_packageExists(self):
with self.assertCalls(
(self.call.adb.Shell('getprop ro.build.version.sdk'), '17\n'),
- (self.call.device.GetApplicationPath('this.package.exists'),
- '/data/app/this.package.exists.apk'),
+ (self.call.device.GetApplicationPaths('this.package.exists'),
+ ['/data/app/this.package.exists.apk']),
(self.call.adb.Shell('pm clear this.package.exists'),
'Success\r\n')):
self.device.ClearApplicationState('this.package.exists')
diff --git a/build/android/pylib/sdk/split_select.py b/build/android/pylib/sdk/split_select.py
new file mode 100644
index 0000000..8752129
--- /dev/null
+++ b/build/android/pylib/sdk/split_select.py
@@ -0,0 +1,66 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""This module wraps Android's split-select tool."""
+# pylint: disable=unused-argument
+
+import os
+
+from pylib import cmd_helper
+from pylib import constants
+from pylib.utils import timeout_retry
+
+_SPLIT_SELECT_PATH = os.path.join(constants.ANDROID_SDK_TOOLS, 'split-select')
+_DEFAULT_TIMEOUT = 30
+_DEFAULT_RETRIES = 2
+
+def _RunSplitSelectCmd(args, timeout=None, retries=None):
+ """Runs a split-select command.
+
+ Args:
+ args: A list of arguments for split-select.
+ timeout: Timeout in seconds.
+ retries: Number of retries.
+
+ Returns:
+ The output of the command.
+ """
+ cmd = [_SPLIT_SELECT_PATH] + args
+ status, output = cmd_helper.GetCmdStatusAndOutputWithTimeout(
+ cmd, timeout_retry.CurrentTimeoutThread().GetRemainingTime())
+ if status != 0:
+ raise Exception('Failed running command %s' % str(cmd))
+ return output
+
+def _SplitConfig(device):
+ """Returns a config specifying which APK splits are required by the device.
+
+ Args:
+ device: A DeviceUtils object.
+ """
+ return ('%s-r%s-%s:%s' %
+ (device.language,
+ device.country,
+ device.screen_density,
+ device.product_cpu_abi))
+
+def SelectSplits(device, base_apk, split_apks,
+ timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
+ """Determines which APK splits the device requires.
+
+ Args:
+ device: A DeviceUtils object.
+ base_apk: The path of the base APK.
+ split_apks: A list of paths of APK splits.
+ timeout: Timeout in seconds.
+ retries: Number of retries.
+
+ Returns:
+ The list of APK splits that the device requires.
+ """
+ config = _SplitConfig(device)
+ args = ['--target', config, '--base', base_apk]
+ for split in split_apks:
+ args.extend(['--split', split])
+ return _RunSplitSelectCmd(args, timeout=timeout, retries=retries).splitlines() \ No newline at end of file
diff --git a/build/android/update_verification.py b/build/android/update_verification.py
index 06cd2d6..05d083b 100755
--- a/build/android/update_verification.py
+++ b/build/android/update_verification.py
@@ -49,7 +49,7 @@ def TestUpdate(device, old_apk, new_apk, app_data, package_name):
# Restore command is not synchronous
raw_input('Select "Restore my data" on the device. Then press enter to '
'continue.')
- device_path = device.GetApplicationPath(package_name)
+ device_path = device.GetApplicationPaths(package_name)
if not device_path:
raise Exception('Expected package %s to already be installed. '
'Package name might have changed!' % package_name)