Merge "Add verification before downloading whole package" am: 2e7393dbde
am: e84953e9aa

Change-Id: I71194f48fb58883ab3b64371c4cf9042bf6f84bb
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}.