Add verification before downloading whole package
UpdateEngine has a feature that verifies
payload without downloading the whole update package.
If UpdateEngine detects invalid payload,
the sample app aborts the update.
No JUnit tests, because it accesses files on the
device and migrating tests to robolectric
is not worth for this sample app.
Bug: 77150191
Test: device
Change-Id: Ib8ce73508a02cf5fdcb326d8ba46c1d05ed5efe5
diff --git a/updater_sample/README.md b/updater_sample/README.md
index 5894cf8..bc66a9b 100644
--- a/updater_sample/README.md
+++ b/updater_sample/README.md
@@ -220,7 +220,7 @@
- [x] Add Sample app update state (separate from update_engine status)
- [x] Add smart update completion detection using onStatusUpdate
- [x] Add pause/resume demo
-- [-] Verify system partition checksum for package
+- [x] Verify system partition checksum for package
## Running tests
diff --git a/updater_sample/src/com/example/android/systemupdatersample/services/PrepareUpdateService.java b/updater_sample/src/com/example/android/systemupdatersample/services/PrepareUpdateService.java
index 06581be..29eb13d 100644
--- a/updater_sample/src/com/example/android/systemupdatersample/services/PrepareUpdateService.java
+++ b/updater_sample/src/com/example/android/systemupdatersample/services/PrepareUpdateService.java
@@ -42,7 +42,9 @@
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
+import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.Arrays;
import java.util.Optional;
/**
@@ -144,6 +146,13 @@
private PayloadSpec execute(UpdateConfig config)
throws IOException, PreparationFailedException {
+ if (config.getAbConfig().getVerifyPayloadMetadata()) {
+ Log.i(TAG, "Verifying payload metadata with UpdateEngine.");
+ if (!verifyPayloadMetadata(config)) {
+ throw new PreparationFailedException("Payload metadata is not compatible");
+ }
+ }
+
if (config.getInstallType() == UpdateConfig.AB_INSTALL_TYPE_NON_STREAMING) {
return mPayloadSpecs.forNonStreaming(config.getUpdatePackageFile());
}
@@ -179,6 +188,48 @@
}
/**
+ * Downloads only payload_metadata.bin and verifies with
+ * {@link UpdateEngine#verifyPayloadMetadata}.
+ * Returns {@code true} if the payload is verified or the result is unknown because of
+ * exception from UpdateEngine.
+ * By downloading only small portion of the package, it allows to verify if UpdateEngine
+ * will install the update.
+ */
+ private boolean verifyPayloadMetadata(UpdateConfig config) {
+ Optional<UpdateConfig.PackageFile> metadataPackageFile =
+ Arrays.stream(config.getAbConfig().getPropertyFiles())
+ .filter(p -> p.getFilename().equals(
+ PackageFiles.PAYLOAD_METADATA_FILE_NAME))
+ .findFirst();
+ if (!metadataPackageFile.isPresent()) {
+ Log.w(TAG, String.format("ab_config.property_files doesn't contain %s",
+ PackageFiles.PAYLOAD_METADATA_FILE_NAME));
+ return true;
+ }
+ Path metadataPath = Paths.get(OTA_PACKAGE_DIR, PackageFiles.PAYLOAD_METADATA_FILE_NAME);
+ try {
+ Files.deleteIfExists(metadataPath);
+ FileDownloader d = new FileDownloader(
+ config.getUrl(),
+ metadataPackageFile.get().getOffset(),
+ metadataPackageFile.get().getSize(),
+ metadataPath.toFile());
+ d.download();
+ } catch (IOException e) {
+ Log.w(TAG, String.format("Downloading %s from %s failed",
+ PackageFiles.PAYLOAD_METADATA_FILE_NAME,
+ config.getUrl()), e);
+ return true;
+ }
+ try {
+ return mUpdateEngine.verifyPayloadMetadata(metadataPath.toAbsolutePath().toString());
+ } catch (Exception e) {
+ Log.w(TAG, "UpdateEngine#verifyPayloadMetadata failed", e);
+ return true;
+ }
+ }
+
+ /**
* Downloads files defined in {@link UpdateConfig#getAbConfig()}
* and exists in {@code PRE_STREAMING_FILES_SET}, and put them
* in directory {@code dir}.