Merge "updater_sample: update tools" am: daa86e9024
am: 9fdeb57ab0

Change-Id: I1e517b533628e4ae8d27b00cc9a3852fa961f267
diff --git a/updater_sample/README.md b/updater_sample/README.md
index 12f803f..2c1f0ce 100644
--- a/updater_sample/README.md
+++ b/updater_sample/README.md
@@ -69,14 +69,15 @@
       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 145c62e..6bf2a23 100644
--- a/updater_sample/tests/res/raw/ota_002_package.zip
+++ b/updater_sample/tests/res/raw/ota_002_package.zip
Binary files differ
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 f00f19c..cf4469b 100644
--- a/updater_sample/tests/res/raw/update_config_stream_002.json
+++ b/updater_sample/tests/res/raw/update_config_stream_002.json
@@ -4,29 +4,34 @@
     "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 80506ee..009610e 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 @@
                 .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 2912e20..d9e5465 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.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.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 @@
 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 @@
     @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 @@
                 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 0578124..4efa9f1 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 sys
 import zipfile
 
+import ota_from_target_files  # pylint: disable=import-error
+
 
 class GenUpdateConfig(object):
     """
@@ -41,7 +45,6 @@
 
     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 @@
     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 951d4c4..c907cf2 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 @@
         """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 @@
                               GenUpdateConfig.AB_INSTALL_TYPE_STREAMING)
         gen.run()
         return gen.config, ota_package
-
-
-if __name__ == '__main__':
-    unittest.main()