aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools
diff options
context:
space:
mode:
authorGravatar Lukacs Berki <lberki@google.com>2015-08-20 14:27:42 +0000
committerGravatar Lukacs Berki <lberki@google.com>2015-08-20 14:51:01 +0000
commitfaff718f8ce4c9debb27d2b0a796414e133bfdfa (patch)
treed7a27a450aaf0f1c7b16a06dc59ad5e931fa7f35 /tools
parentd39e1e1fe157c52b310fe59467ae05a8b6522509 (diff)
Make the installer script support incremental installation with split .apks (only for devices with Android M).
Until now, we always reinstalled every split .apk. It was simple, but also not very fast. -- MOS_MIGRATED_REVID=101120400
Diffstat (limited to 'tools')
-rw-r--r--tools/android/incremental_install.py80
-rw-r--r--tools/android/incremental_install_test.py73
2 files changed, 142 insertions, 11 deletions
diff --git a/tools/android/incremental_install.py b/tools/android/incremental_install.py
index aa2f710f7b..088af54f0d 100644
--- a/tools/android/incremental_install.py
+++ b/tools/android/incremental_install.py
@@ -174,7 +174,7 @@ class Adb(object):
def GetInstallTime(self, package):
"""Get the installation time of a package."""
_, stdout, _, _ = self._Shell("dumpsys package %s" % package)
- match = re.search("lastUpdateTime=(.*)$", stdout, re.MULTILINE)
+ match = re.search("firstInstallTime=(.*)$", stdout, re.MULTILINE)
if match:
return match.group(1)
else:
@@ -240,6 +240,12 @@ class Adb(object):
if "Success" not in stderr and "Success" not in stdout:
raise AdbError(args, ret, stdout, stderr)
+ def Uninstall(self, pkg):
+ """Invoke 'adb uninstall'."""
+ self._Exec(["uninstall", pkg])
+ # No error checking. If this fails, we assume that the app was not installed
+ # in the first place.
+
def Delete(self, remote):
"""Delete the given file (or directory) on the device."""
self.DeleteMultiple([remote])
@@ -586,6 +592,71 @@ def VerifyInstallTimestamp(adb, app_package):
"'mobile-install'?" % app_package)
+def SplitIncrementalInstall(adb, app_package, execroot, split_main_apk,
+ split_apks):
+ """Does incremental installation using split packages."""
+ app_dir = os.path.join(DEVICE_DIRECTORY, app_package)
+ device_manifest_path = "%s/split_manifest" % app_dir
+ device_manifest = adb.Pull(device_manifest_path)
+ expected_timestamp = adb.Pull("%s/install_timestamp" % app_dir)
+ actual_timestamp = adb.GetInstallTime(app_package)
+ device_checksums = {}
+ if device_manifest is not None:
+ for manifest_line in device_manifest.split("\n"):
+ if manifest_line:
+ name, checksum = manifest_line.split(" ")
+ device_checksums[name] = checksum
+
+ install_checksums = {}
+ install_checksums["__MAIN__"] = Checksum(
+ os.path.join(execroot, split_main_apk))
+ for apk in split_apks:
+ install_checksums[apk] = Checksum(os.path.join(execroot, apk))
+
+ reinstall_main = False
+ if (device_manifest is None or actual_timestamp is None or
+ actual_timestamp != expected_timestamp or
+ install_checksums["__MAIN__"] != device_checksums["__MAIN__"] or
+ set(device_checksums.keys()) != set(install_checksums.keys())):
+ # The main app is not up to date or not present or something happened
+ # with the on-device manifest. Start from scratch. Notably, we cannot
+ # uninstall a split package, so if the set of packages changes, we also
+ # need to do a full reinstall.
+ reinstall_main = True
+ device_checksums = {}
+
+ apks_to_update = [
+ apk for apk in split_apks if
+ apk not in device_checksums or
+ device_checksums[apk] != install_checksums[apk]]
+
+ if not apks_to_update and not reinstall_main:
+ # Nothing to do
+ return
+
+ # Delete the device manifest so that if something goes wrong, we do a full
+ # reinstall next time
+ adb.Delete(device_manifest_path)
+
+ if reinstall_main:
+ logging.info("Installing main APK...")
+ adb.Uninstall(app_package)
+ adb.InstallMultiple(os.path.join(execroot, split_main_apk))
+ adb.PushString(
+ adb.GetInstallTime(app_package),
+ "%s/install_timestamp" % app_dir).result()
+
+ logging.info("Reinstalling %s APKs...", len(apks_to_update))
+
+ for apk in apks_to_update:
+ adb.InstallMultiple(os.path.join(execroot, apk), app_package)
+
+ install_manifest = [
+ name + " " + checksum for name, checksum in install_checksums.iteritems()]
+ adb.PushString("\n".join(install_manifest),
+ "%s/split_manifest" % app_dir).result()
+
+
def IncrementalInstall(adb_path, execroot, stub_datafile, output_marker,
adb_jobs, start_type, dexmanifest=None, apk=None,
native_libs=None, resource_apk=None,
@@ -615,11 +686,8 @@ def IncrementalInstall(adb_path, execroot, stub_datafile, output_marker,
app_package = GetAppPackage(os.path.join(execroot, stub_datafile))
app_dir = os.path.join(DEVICE_DIRECTORY, app_package)
if split_main_apk:
- adb.InstallMultiple(os.path.join(execroot, split_main_apk))
- for split_apk in split_apks:
- # TODO(build-team): This always reinstalls everything, which defeats the
- # purpose of this whole system.
- adb.InstallMultiple(os.path.join(execroot, split_apk), app_package)
+ SplitIncrementalInstall(adb, app_package, execroot, split_main_apk,
+ split_apks)
else:
if not apk:
VerifyInstallTimestamp(adb, app_package)
diff --git a/tools/android/incremental_install_test.py b/tools/android/incremental_install_test.py
index c92fae7240..87500fc24f 100644
--- a/tools/android/incremental_install_test.py
+++ b/tools/android/incremental_install_test.py
@@ -28,9 +28,10 @@ class MockAdb(object):
def __init__(self):
# Map of file name -> contents.
self.files = {}
+ self.split_apks = set()
self._error = None
self.package_timestamp = None
- self._last_package_timestamp = 0
+ self._last_package_timestamp = 1
self.shell_cmdlns = []
self.abi = "armeabi-v7a"
@@ -64,6 +65,19 @@ class MockAdb(object):
self.package_timestamp = self._last_package_timestamp
self._last_package_timestamp += 1
return self._CreatePopenMock(0, "Success", "")
+ elif cmd == "install-multiple":
+ if args[3] == "-p":
+ with open(args[5]) as f:
+ content = f.read()
+ self.split_apks.add(content)
+ else:
+ self.package_timestamp = self._last_package_timestamp
+ self._last_package_timestamp += 1
+ return self._CreatePopenMock(0, "Success", "")
+ elif cmd == "uninstall":
+ self._CreatePopenMock(0, "Success", "")
+ self.split_apks = set()
+ self.package_timestamp = None
elif cmd == "shell":
# "/test/adb shell ..."
# mkdir, rm, am (application manager), or monkey
@@ -74,7 +88,7 @@ class MockAdb(object):
elif shell_cmdln.startswith("dumpsys package "):
return self._CreatePopenMock(
0,
- "lastUpdateTime=%s" % self.package_timestamp,
+ "firstInstallTime=%s" % self.package_timestamp,
"")
elif shell_cmdln.startswith("rm"):
file_path = shell_cmdln.split()[2]
@@ -161,8 +175,11 @@ class IncrementalInstallTest(unittest.TestCase):
self._mock_adb.files.pop(self._GetDeviceAppPath(f), None)
def _CallIncrementalInstall(self, incremental, native_libs=None,
+ split_main_apk=None, split_apks=None,
start_type="no"):
- if incremental:
+ if split_main_apk:
+ apk = split_main_apk
+ elif incremental:
apk = None
else:
apk = self._APK
@@ -174,6 +191,8 @@ class IncrementalInstallTest(unittest.TestCase):
dexmanifest=self._DEXMANIFEST,
apk=apk,
resource_apk=self._RESOURCE_APK,
+ split_main_apk=split_main_apk,
+ split_apks=split_apks,
native_libs=native_libs,
output_marker=self._OUTPUT_MARKER,
adb_jobs=1,
@@ -201,6 +220,50 @@ class IncrementalInstallTest(unittest.TestCase):
self.assertEquals("content3", self._GetDeviceFile("dex/ip3"))
self.assertEquals("resource apk", self._GetDeviceFile("resources.ap_"))
+ def testSplitInstallToPristineDevice(self):
+ with open("split1", "w") as f:
+ f.write("split_content1")
+
+ with open("main", "w") as f:
+ f.write("main_Content")
+
+ self._CallIncrementalInstall(
+ incremental=False, split_main_apk="main", split_apks=["split1"])
+ self.assertEquals(set(["split_content1"]), self._mock_adb.split_apks)
+
+ def testSplitInstallUnchanged(self):
+ with open("split1", "w") as f:
+ f.write("split_content1")
+
+ with open("main", "w") as f:
+ f.write("main_Content")
+
+ self._CallIncrementalInstall(
+ incremental=False, split_main_apk="main", split_apks=["split1"])
+ self.assertEquals(set(["split_content1"]), self._mock_adb.split_apks)
+ self._mock_adb.split_apks = set()
+ self._CallIncrementalInstall(
+ incremental=False, split_main_apk="main", split_apks=["split1"])
+ self.assertEquals(set([]), self._mock_adb.split_apks)
+
+ def testSplitInstallChanges(self):
+ with open("split1", "w") as f:
+ f.write("split_content1")
+
+ with open("main", "w") as f:
+ f.write("main_Content")
+
+ self._CallIncrementalInstall(
+ incremental=False, split_main_apk="main", split_apks=["split1"])
+ self.assertEquals(set(["split_content1"]), self._mock_adb.split_apks)
+
+ with open("split1", "w") as f:
+ f.write("split_content2")
+ self._mock_adb.split_apks = set()
+ self._CallIncrementalInstall(
+ incremental=False, split_main_apk="main", split_apks=["split1"])
+ self.assertEquals(set(["split_content2"]), self._mock_adb.split_apks)
+
def testMissingNativeManifestWithIncrementalInstall(self):
self._CreateZip()
with open("liba.so", "w") as f:
@@ -356,8 +419,8 @@ class IncrementalInstallTest(unittest.TestCase):
"zip1 zip2 ip2 1")
self._PutDeviceFile("dex/ip1", "content1")
self._PutDeviceFile("dex/ip2", "content2")
- self._PutDeviceFile("install_timestamp", "0")
- self._mock_adb.package_timestamp = "0"
+ self._PutDeviceFile("install_timestamp", "1")
+ self._mock_adb.package_timestamp = "1"
self._CreateZip("zip1", ("zp1", "content1"))
self._CreateLocalManifest("zip1 zp1 ip1 0")