diff options
-rw-r--r-- | updater_sample/README.md | 2 | ||||
-rw-r--r-- | updater_sample/src/com/example/android/systemupdatersample/services/PrepareStreamingService.java | 85 |
2 files changed, 60 insertions, 27 deletions
diff --git a/updater_sample/README.md b/updater_sample/README.md index 7e25b070c..95e57dbe9 100644 --- a/updater_sample/README.md +++ b/updater_sample/README.md @@ -84,8 +84,8 @@ which HTTP headers are supported. - [x] Add applying streaming update - [x] Add stop/reset the update - [x] Add demo for passing HTTP headers to `UpdateEngine#applyPayload` +- [x] [Package compatibility check](https://source.android.com/devices/architecture/vintf/match-rules) - [ ] Add tests for `MainActivity` -- [ ] HAL compatibility check - [ ] Change partition demo - [ ] Verify system partition checksum for package - [ ] Add non-A/B updates demo diff --git a/updater_sample/src/com/example/android/systemupdatersample/services/PrepareStreamingService.java b/updater_sample/src/com/example/android/systemupdatersample/services/PrepareStreamingService.java index 840a6d607..222bb0a58 100644 --- a/updater_sample/src/com/example/android/systemupdatersample/services/PrepareStreamingService.java +++ b/updater_sample/src/com/example/android/systemupdatersample/services/PrepareStreamingService.java @@ -16,6 +16,7 @@ package com.example.android.systemupdatersample.services; +import static com.example.android.systemupdatersample.util.PackageFiles.COMPATIBILITY_ZIP_FILE_NAME; import static com.example.android.systemupdatersample.util.PackageFiles.OTA_PACKAGE_DIR; import static com.example.android.systemupdatersample.util.PackageFiles.PAYLOAD_BINARY_FILE_NAME; import static com.example.android.systemupdatersample.util.PackageFiles.PAYLOAD_PROPERTIES_FILE_NAME; @@ -25,6 +26,7 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.Handler; +import android.os.RecoverySystem; import android.os.ResultReceiver; import android.util.Log; @@ -36,7 +38,9 @@ import com.example.android.systemupdatersample.util.PayloadSpecs; import com.example.android.systemupdatersample.util.UpdateConfigs; import com.google.common.collect.ImmutableSet; +import java.io.File; import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Paths; import java.util.Optional; @@ -119,45 +123,51 @@ public class PrepareStreamingService extends IntentService { ResultReceiver resultReceiver = intent.getParcelableExtra(EXTRA_PARAM_RESULT_RECEIVER); try { - downloadPreStreamingFiles(config, OTA_PACKAGE_DIR); - } catch (IOException e) { - Log.e(TAG, "Failed to download pre-streaming files", e); + PayloadSpec spec = execute(config); + resultReceiver.send(RESULT_CODE_SUCCESS, CallbackResultReceiver.createBundle(spec)); + } catch (Exception e) { + Log.e(TAG, "Failed to prepare streaming update", e); resultReceiver.send(RESULT_CODE_ERROR, null); - return; } + } + + /** + * 1. Downloads files for streaming updates. + * 2. Makes sure required files are present. + * 3. Checks OTA package compatibility with the device. + * 4. Constructs {@link PayloadSpec} for streaming update. + */ + private static PayloadSpec execute(UpdateConfig config) + throws IOException, PreparationFailedException { + + downloadPreStreamingFiles(config, OTA_PACKAGE_DIR); Optional<UpdateConfig.PackageFile> payloadBinary = UpdateConfigs.getPropertyFile(PAYLOAD_BINARY_FILE_NAME, config); if (!payloadBinary.isPresent()) { - Log.e(TAG, "Failed to find " + PAYLOAD_BINARY_FILE_NAME + " in config"); - resultReceiver.send(RESULT_CODE_ERROR, null); - return; + throw new PreparationFailedException( + "Failed to find " + PAYLOAD_BINARY_FILE_NAME + " in config"); } - Optional<UpdateConfig.PackageFile> properties = - UpdateConfigs.getPropertyFile(PAYLOAD_PROPERTIES_FILE_NAME, config); - - if (!properties.isPresent()) { - Log.e(TAG, "Failed to find " + PAYLOAD_PROPERTIES_FILE_NAME + " in config"); - resultReceiver.send(RESULT_CODE_ERROR, null); - return; + if (!UpdateConfigs.getPropertyFile(PAYLOAD_PROPERTIES_FILE_NAME, config).isPresent() + || !Paths.get(OTA_PACKAGE_DIR, PAYLOAD_PROPERTIES_FILE_NAME).toFile().exists()) { + throw new IOException(PAYLOAD_PROPERTIES_FILE_NAME + " not found"); } - PayloadSpec spec; - try { - spec = PayloadSpecs.forStreaming(config.getUrl(), - payloadBinary.get().getOffset(), - payloadBinary.get().getSize(), - Paths.get(OTA_PACKAGE_DIR, properties.get().getFilename()).toFile() - ); - } catch (IOException e) { - Log.e(TAG, "PayloadSpecs failed to create PayloadSpec for streaming", e); - resultReceiver.send(RESULT_CODE_ERROR, null); - return; + File compatibilityFile = Paths.get(OTA_PACKAGE_DIR, COMPATIBILITY_ZIP_FILE_NAME).toFile(); + if (compatibilityFile.isFile()) { + Log.i(TAG, "Verifying OTA package for compatibility with the device"); + if (!verifyPackageCompatibility(compatibilityFile)) { + throw new PreparationFailedException( + "OTA package is not compatible with this device"); + } } - resultReceiver.send(RESULT_CODE_SUCCESS, CallbackResultReceiver.createBundle(spec)); + return PayloadSpecs.forStreaming(config.getUrl(), + payloadBinary.get().getOffset(), + payloadBinary.get().getSize(), + Paths.get(OTA_PACKAGE_DIR, PAYLOAD_PROPERTIES_FILE_NAME).toFile()); } /** @@ -168,6 +178,10 @@ public class PrepareStreamingService extends IntentService { */ private static void downloadPreStreamingFiles(UpdateConfig config, String dir) throws IOException { + Log.d(TAG, "Deleting existing files from " + dir); + for (String file : PRE_STREAMING_FILES_SET) { + Files.deleteIfExists(Paths.get(OTA_PACKAGE_DIR, file)); + } Log.d(TAG, "Downloading files to " + dir); for (UpdateConfig.PackageFile file : config.getStreamingMetadata().getPropertyFiles()) { if (PRE_STREAMING_FILES_SET.contains(file.getFilename())) { @@ -183,6 +197,19 @@ public class PrepareStreamingService extends IntentService { } /** + * @param file physical location of {@link PackageFiles#COMPATIBILITY_ZIP_FILE_NAME} + * @return true if OTA package is compatible with this device + */ + private static boolean verifyPackageCompatibility(File file) { + try { + return RecoverySystem.verifyPackageCompatibility(file); + } catch (IOException e) { + Log.e(TAG, "Failed to verify package compatibility", e); + return false; + } + } + + /** * Used by {@link PrepareStreamingService} to pass {@link PayloadSpec} * to {@link UpdateResultCallback#onReceiveResult}. */ @@ -213,4 +240,10 @@ public class PrepareStreamingService extends IntentService { } } + private static class PreparationFailedException extends Exception { + PreparationFailedException(String message) { + super(message); + } + } + } |