From 96eb59e4b13b07a18fc1a6a85786f2c287bd21db Mon Sep 17 00:00:00 2001 From: Zhomart Mukhamejanov Date: Fri, 4 May 2018 12:17:01 -0700 Subject: [PATCH] updater_sample: update tools - Allow gen_update_config.py to use ota_from_target_files from $ANDROID_BUILD_TOP/build/make/tools/releasetools/ - tests/res/raw/ota_002_package.zip re-generated using functions from $ANDROID_BUILD_TOP/build/make/tools/releasetools/test_ota_from_target_files.py - sample app tests updated Test: ./tools/gen_update_config_test.py Change-Id: I5c492ec22782ba54fe481f592a44e797c695684e Signed-off-by: Zhomart Mukhamejanov --- updater_sample/README.md | 7 ++- .../tests/res/raw/ota_002_package.zip | Bin 638 -> 2181 bytes .../res/raw/update_config_stream_002.json | 23 ++++--- .../util/FileDownloaderTest.java | 4 +- .../util/PayloadSpecsTest.java | 56 +++++------------- updater_sample/tools/gen_update_config.py | 51 +++++++--------- ...nfig_test.py => test_gen_update_config.py} | 26 ++++---- 7 files changed, 74 insertions(+), 93 deletions(-) rename updater_sample/tools/{gen_update_config_test.py => test_gen_update_config.py} (69%) diff --git a/updater_sample/README.md b/updater_sample/README.md index 12f803ff..2c1f0ced 100644 --- a/updater_sample/README.md +++ b/updater_sample/README.md @@ -69,14 +69,15 @@ purpose only. update zip file - [x] Add `UpdateConfig` for working with json config files - [x] Add applying non-streaming update -- [ ] Prepare streaming update (partially downloading package) -- [ ] Add applying streaming update +- [x] Prepare streaming update (partially downloading package) +- [x] Add applying streaming update +- [x] Add stop/reset the update - [ ] Add tests for `MainActivity` -- [ ] Add stop/reset the update - [ ] Verify system partition checksum for package - [ ] HAL compatibility check - [ ] Change partition demo - [ ] Add non-A/B updates demo +- [ ] Add docs for passing HTTP headers to `UpdateEngine#applyPayload` ## Running tests diff --git a/updater_sample/tests/res/raw/ota_002_package.zip b/updater_sample/tests/res/raw/ota_002_package.zip index 145c62e6a54c3789d9b965407c1b857c0ff35ce3..6bf2a23b2e405e0b2ff7be6b50e17203bd3a54b3 100644 GIT binary patch literal 2181 zcmZ{l4OCNQ7{}i`2HssvCWV5DJkxLfrui<`m@fv<)BI(a-03HkL1%pf~y653kGr{R>Uu5rN(0Lp{%;zz`9T zXpwjr5Fr-X|LW-FJB|Jse+RuGZFnrYS~(Ir9{qc-+vGDOZ{nBnb$L4>#KUMdqACp$ z^7EfQqhh5|b*Nl#Q0erj%$RLZ>(L;*%7oHq%ILPKAN?iOcV_yk9&_rKwe)KkSM1FW z0u1Db2W05YCwG&SyxX6ct2>gis?aDSyar=%>doTYUY~Yx6EH-P2Fo`uU>^!8_AixU08zmiU`)!&3rJ zAyTwU;P~+C<)`xp=AANhcs;XgxBrn>(%!qEI@B$3sG;Kfms^lx91M9|aDF-JV<&f? zONuHpZwuV^b`Jub^ZDyICHF+CIABz!2|w3G?)1y2u{T8@^)art{`j6~?Q)anz1 zoT{$iE7y2W%6=UP^z>U=54YkOA*Y8TPBtuXd`cS-JTA)Gt72IDa)<9fVz+$F&^|gy z&6=N&X>PGpy4s=$pomXA8AIBhe^uiu3 zh3kZEI)&x-<(290zH+R@DQGJEaV6O;WfTJTPG*d{xZ)qfi9w$z)GIC=gnCYQu z>3lyi1Aw5mz_xe*0U{1219F4M4p7vW`qf73Na3=yVgkOo&doB51i5`l!I0j)=K#Qw z0e}Ha#Qr;gp%ET}cq#cxUVQ68yWmo_m#rnJayfS)tS zmnuhaWBvAkUr+j$610A!*S76Tr&Zr!dtI+xdVTrLg0t$T(sDcLell>x;=al0F5bPN z_mtIrG$nE`p78X8ON<{TTDHWT@S2P@MSZa^g&@q7;2$~;WNxnDw2~9d!r%=@7x3>N zuim-T^Gs`1eWY!N_mbjxTYP|UNS3y{>9x$Nw@iJ47-bR_8Hb^JW*%#$indl05XO(W za9*kp+|cNkaI?o2P@VeotQ z{b*TOZLRo)iCKQAnQ{P|cAc>ViDe5x=- zA$>C`NfjPpLZhNMMzK7HC(dW^(#v4xoY*gh#b@qwrZ6G&EcY=>~JoR7dMMeWs8cY zA+v2XtgRL74-x9;W97RdTW(OvG;+PO_ylGuTb9BUuaK;jyhxc#Ge`?D$`#ok~vNVy1-0;t6lOhv!z$x!~OKoW=mCEvj%X zzthX^oY8$g$A6jW%3*=Bus{BJYv+=44V@(qImx;9&c)~YIg9^<;r|-=U+#+e?$|2; PxMMpLn@%Y6oLBz%0LMrle@B-96!qDgt%`+! z>Ofcwp+3H#D8C@Js3bGBSg)j_1gbSYAjm(!H7LZ>H5k>D9VRw2IDooAmRKOW=W-9Rc3*+zi)tJh^LdM zk7r0Es@{kd>YXs#WPy5pT|*poJ^kGDfrjWO=A{(nXQt@qrj{h8B$gzCBwZXs90R-= znM9az2Ncj8Flb-|QOLoDrV%~FAleuh8W;@_8qvcK%?$MLLYPqoWFm(iy0g*44WV-d kNGEzIqH9MFI)wHoKqlUR4De=U11V<(LPuaAse=Ow0Nb&K{r~^~ diff --git a/updater_sample/tests/res/raw/update_config_stream_002.json b/updater_sample/tests/res/raw/update_config_stream_002.json index f00f19ce..cf4469b1 100644 --- a/updater_sample/tests/res/raw/update_config_stream_002.json +++ b/updater_sample/tests/res/raw/update_config_stream_002.json @@ -3,30 +3,35 @@ "ab_install_type": "STREAMING", "ab_streaming_metadata": { "property_files": [ + { + "filename": "payload_metadata.bin", + "offset": 41, + "size": 827 + }, { "filename": "payload.bin", "offset": 41, - "size": 7 + "size": 1392 }, { "filename": "payload_properties.txt", - "offset": 100, - "size": 18 + "offset": 1485, + "size": 147 }, { "filename": "care_map.txt", - "offset": 160, - "size": 8 + "offset": 1674, + "size": 12 }, { "filename": "compatibility.zip", - "offset": 215, - "size": 13 + "offset": 1733, + "size": 17 }, { "filename": "metadata", - "offset": 287, - "size": 8 + "offset": 1809, + "size": 29 } ] }, diff --git a/updater_sample/tests/src/com/example/android/systemupdatersample/util/FileDownloaderTest.java b/updater_sample/tests/src/com/example/android/systemupdatersample/util/FileDownloaderTest.java index 80506ee6..009610e8 100644 --- a/updater_sample/tests/src/com/example/android/systemupdatersample/util/FileDownloaderTest.java +++ b/updater_sample/tests/src/com/example/android/systemupdatersample/util/FileDownloaderTest.java @@ -70,11 +70,11 @@ public class FileDownloaderTest { .toFile(); Files.deleteIfExists(outFile.toPath()); // download a chunk of ota.zip - FileDownloader downloader = new FileDownloader(url, 160, 8, outFile); + FileDownloader downloader = new FileDownloader(url, 1674, 12, outFile); downloader.download(); String downloadedContent = String.join("\n", Files.readAllLines(outFile.toPath())); // archive contains text files with uppercase filenames - assertEquals("CARE_MAP", downloadedContent); + assertEquals("CARE_MAP-TXT", downloadedContent); } } diff --git a/updater_sample/tests/src/com/example/android/systemupdatersample/util/PayloadSpecsTest.java b/updater_sample/tests/src/com/example/android/systemupdatersample/util/PayloadSpecsTest.java index 2912e209..d9e54652 100644 --- a/updater_sample/tests/src/com/example/android/systemupdatersample/util/PayloadSpecsTest.java +++ b/updater_sample/tests/src/com/example/android/systemupdatersample/util/PayloadSpecsTest.java @@ -17,8 +17,6 @@ package com.example.android.systemupdatersample.util; import static com.example.android.systemupdatersample.util.PackageFiles.PAYLOAD_BINARY_FILE_NAME; -import static com.example.android.systemupdatersample.util.PackageFiles - .PAYLOAD_PROPERTIES_FILE_NAME; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -29,6 +27,7 @@ import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import com.example.android.systemupdatersample.PayloadSpec; +import com.example.android.systemupdatersample.tests.R; import com.google.common.base.Charsets; import com.google.common.io.Files; @@ -39,12 +38,8 @@ import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.zip.CRC32; -import java.util.zip.ZipEntry; -import java.util.zip.ZipOutputStream; +import java.nio.file.Paths; /** * Tests if PayloadSpecs parses update package zip file correctly. @@ -54,12 +49,11 @@ import java.util.zip.ZipOutputStream; public class PayloadSpecsTest { private static final String PROPERTIES_CONTENTS = "k1=val1\nkey2=val2"; - private static final String PAYLOAD_CONTENTS = "hello\nworld"; - private static final int PAYLOAD_SIZE = PAYLOAD_CONTENTS.length(); private File mTestDir; private Context mTargetContext; + private Context mTestContext; @Rule public final ExpectedException thrown = ExpectedException.none(); @@ -67,21 +61,30 @@ public class PayloadSpecsTest { @Before public void setUp() { mTargetContext = InstrumentationRegistry.getTargetContext(); + mTestContext = InstrumentationRegistry.getContext(); mTestDir = mTargetContext.getFilesDir(); } @Test public void forNonStreaming_works() throws Exception { - File packageFile = createMockZipFile(); + // Prepare the target file + File packageFile = Paths + .get(mTargetContext.getCacheDir().getAbsolutePath(), "ota.zip") + .toFile(); + java.nio.file.Files.deleteIfExists(packageFile.toPath()); + java.nio.file.Files.copy(mTestContext.getResources().openRawResource(R.raw.ota_002_package), + packageFile.toPath()); PayloadSpec spec = PayloadSpecs.forNonStreaming(packageFile); assertEquals("correct url", "file://" + packageFile.getAbsolutePath(), spec.getUrl()); assertEquals("correct payload offset", 30 + PAYLOAD_BINARY_FILE_NAME.length(), spec.getOffset()); - assertEquals("correct payload size", PAYLOAD_SIZE, spec.getSize()); - assertArrayEquals("correct properties", - new String[]{"k1=val1", "key2=val2"}, spec.getProperties().toArray(new String[0])); + assertEquals("correct payload size", 1392, spec.getSize()); + assertEquals(4, spec.getProperties().size()); + assertEquals( + "FILE_HASH=sEAK/NMbU7GGe01xt55FsPafIPk8IYyBOAd6SiDpiMs=", + spec.getProperties().get(0)); } @Test @@ -105,33 +108,6 @@ public class PayloadSpecsTest { new String[]{"k1=val1", "key2=val2"}, spec.getProperties().toArray(new String[0])); } - /** - * Creates package zip file that contains payload.bin and payload_properties.txt - */ - private File createMockZipFile() throws IOException { - File testFile = new File(mTestDir, "test.zip"); - try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(testFile))) { - // Add payload.bin entry. - ZipEntry entry = new ZipEntry(PAYLOAD_BINARY_FILE_NAME); - entry.setMethod(ZipEntry.STORED); - entry.setCompressedSize(PAYLOAD_SIZE); - entry.setSize(PAYLOAD_SIZE); - CRC32 crc = new CRC32(); - crc.update(PAYLOAD_CONTENTS.getBytes(StandardCharsets.UTF_8)); - entry.setCrc(crc.getValue()); - zos.putNextEntry(entry); - zos.write(PAYLOAD_CONTENTS.getBytes(StandardCharsets.UTF_8)); - zos.closeEntry(); - - // Add payload properties entry. - ZipEntry propertiesEntry = new ZipEntry(PAYLOAD_PROPERTIES_FILE_NAME); - zos.putNextEntry(propertiesEntry); - zos.write(PROPERTIES_CONTENTS.getBytes(StandardCharsets.UTF_8)); - zos.closeEntry(); - } - return testFile; - } - private File createMockPropertiesFile() throws IOException { File propertiesFile = new File(mTestDir, PackageFiles.PAYLOAD_PROPERTIES_FILE_NAME); Files.asCharSink(propertiesFile, Charsets.UTF_8).write(PROPERTIES_CONTENTS); diff --git a/updater_sample/tools/gen_update_config.py b/updater_sample/tools/gen_update_config.py index 05781247..4efa9f1c 100755 --- a/updater_sample/tools/gen_update_config.py +++ b/updater_sample/tools/gen_update_config.py @@ -17,11 +17,13 @@ """ Given a OTA package file, produces update config JSON file. -Example: tools/gen_update_config.py \\ - --ab_install_type=STREAMING \\ - ota-build-001.zip \\ - my-config-001.json \\ - http://foo.bar/ota-builds/ota-build-001.zip +Example: + $ PYTHONPATH=$ANDROID_BUILD_TOP/build/make/tools/releasetools:$PYTHONPATH \\ + bootable/recovery/updater_sample/tools/gen_update_config.py \\ + --ab_install_type=STREAMING \\ + ota-build-001.zip \\ + my-config-001.json \\ + http://foo.bar/ota-builds/ota-build-001.zip """ import argparse @@ -30,6 +32,8 @@ import os.path import sys import zipfile +import ota_from_target_files # pylint: disable=import-error + class GenUpdateConfig(object): """ @@ -41,7 +45,6 @@ class GenUpdateConfig(object): AB_INSTALL_TYPE_STREAMING = 'STREAMING' AB_INSTALL_TYPE_NON_STREAMING = 'NON_STREAMING' - METADATA_NAME = 'META-INF/com/android/metadata' def __init__(self, package, url, ab_install_type): self.package = package @@ -82,37 +85,27 @@ class GenUpdateConfig(object): def _gen_ab_streaming_metadata(self): """Builds metadata for files required for streaming update.""" with zipfile.ZipFile(self.package, 'r') as package_zip: - property_files = self._get_property_files(package_zip) - metadata = { - 'property_files': property_files + 'property_files': self._get_property_files(package_zip) } return metadata - def _get_property_files(self, zip_file): + @staticmethod + def _get_property_files(package_zip): """Constructs the property-files list for A/B streaming metadata.""" - def compute_entry_offset_size(name): - """Computes the zip entry offset and size.""" - info = zip_file.getinfo(name) - offset = info.header_offset + len(info.FileHeader()) - size = info.file_size - return { - 'filename': os.path.basename(name), - 'offset': offset, - 'size': size, - } - + ab_ota = ota_from_target_files.AbOtaPropertyFiles() + property_str = ab_ota.GetPropertyFilesString(package_zip, False) property_files = [] - for entry in self.streaming_required: - property_files.append(compute_entry_offset_size(entry)) - for entry in self.streaming_optional: - if entry in zip_file.namelist(): - property_files.append(compute_entry_offset_size(entry)) - - # 'META-INF/com/android/metadata' is required - property_files.append(compute_entry_offset_size(GenUpdateConfig.METADATA_NAME)) + for file in property_str.split(','): + filename, offset, size = file.split(':') + inner_file = { + 'filename': filename, + 'offset': int(offset), + 'size': int(size) + } + property_files.append(inner_file) return property_files diff --git a/updater_sample/tools/gen_update_config_test.py b/updater_sample/tools/test_gen_update_config.py similarity index 69% rename from updater_sample/tools/gen_update_config_test.py rename to updater_sample/tools/test_gen_update_config.py index 951d4c4a..c907cf2f 100755 --- a/updater_sample/tools/gen_update_config_test.py +++ b/updater_sample/tools/test_gen_update_config.py @@ -15,7 +15,11 @@ # limitations under the License. """ -Tests gen_update_config.py +Tests gen_update_config.py. + +Example: + $ PYTHONPATH=$ANDROID_BUILD_TOP/build/make/tools/releasetools:$PYTHONPATH \\ + python3 -m unittest test_gen_update_config """ import os.path @@ -29,15 +33,21 @@ class GenUpdateConfigTest(unittest.TestCase): # pylint: disable=missing-docstrin """tests if streaming property files' offset and size are generated properly""" config, package = self._generate_config() property_files = config['ab_streaming_metadata']['property_files'] - self.assertEqual(len(property_files), 5) + self.assertEqual(len(property_files), 6) with open(package, 'rb') as pkg_file: for prop in property_files: filename, offset, size = prop['filename'], prop['offset'], prop['size'] pkg_file.seek(offset) - data = pkg_file.read(size).decode('ascii') - # data in the archive are just uppercase filenames without extension - expected_data = filename.split('.')[0].upper() - self.assertEqual(data, expected_data) + raw_data = pkg_file.read(size) + if filename in ['payload.bin', 'payload_metadata.bin']: + pass + elif filename == 'payload_properties.txt': + pass + elif filename == 'metadata': + self.assertEqual(raw_data.decode('ascii'), 'META-INF/COM/ANDROID/METADATA') + else: + expected_data = filename.replace('.', '-').upper() + self.assertEqual(raw_data.decode('ascii'), expected_data) @staticmethod def _generate_config(): @@ -49,7 +59,3 @@ class GenUpdateConfigTest(unittest.TestCase): # pylint: disable=missing-docstrin GenUpdateConfig.AB_INSTALL_TYPE_STREAMING) gen.run() return gen.config, ota_package - - -if __name__ == '__main__': - unittest.main()