Merge "generic: BOARD_VNDK_VERSION := current"
diff --git a/core/definitions.mk b/core/definitions.mk
index b0dcb01..bd2afac 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -2242,7 +2242,7 @@
$(hide) mkdir -p $(PRIVATE_CLASS_INTERMEDIATES_DIR) $(PRIVATE_ANNO_INTERMEDIATES_DIR) $(if $(PRIVATE_SRCJARS),$(PRIVATE_SRCJAR_INTERMEDIATES_DIR))
$(if $(PRIVATE_SRCJARS),\
$(EXTRACT_SRCJARS) $(PRIVATE_SRCJAR_INTERMEDIATES_DIR) $(PRIVATE_SRCJAR_LIST_FILE) $(PRIVATE_SRCJARS))
-$(hide) if [ -s $(PRIVATE_JAVA_SOURCE_LIST) ] ; then \
+$(hide) if [ -s $(PRIVATE_JAVA_SOURCE_LIST) $(if $(PRIVATE_SRCJARS),-o -s $(PRIVATE_SRCJAR_LIST_FILE) )] ; then \
$(SOONG_JAVAC_WRAPPER) $(JAVAC_WRAPPER) $(1) -encoding UTF-8 \
$(if $(findstring true,$(PRIVATE_WARNINGS_ENABLE)),$(xlint_unchecked),) \
$(if $(PRIVATE_USE_SYSTEM_MODULES), \
@@ -2287,16 +2287,13 @@
@mkdir -p $(dir $@)
@rm -rf $(dir $@)/classes-turbine
@mkdir $(dir $@)/classes-turbine
-$(hide) if [ -s $(PRIVATE_JAVA_SOURCE_LIST) ] ; then \
+$(hide) if [ -s $(PRIVATE_JAVA_SOURCE_LIST) -o -n "$(PRIVATE_SRCJARS)" ] ; then \
$(JAVA) -jar $(TURBINE) \
--output $@.premerged --temp_dir $(dir $@)/classes-turbine \
--sources \@$(PRIVATE_JAVA_SOURCE_LIST) --source_jars $(PRIVATE_SRCJARS) \
- --javacopts $(PRIVATE_JAVACFLAGS) $(COMMON_JDK_FLAGS) \
- $(addprefix --bootclasspath ,$(strip \
- $(call normalize-path-list,$(PRIVATE_BOOTCLASSPATH)) \
- $(PRIVATE_EMPTY_BOOTCLASSPATH))) \
- $(addprefix --classpath ,$(strip \
- $(call normalize-path-list,$(PRIVATE_ALL_JAVA_HEADER_LIBRARIES)))) \
+ --javacopts $(PRIVATE_JAVACFLAGS) $(COMMON_JDK_FLAGS) -- \
+ $(addprefix --bootclasspath ,$(strip $(PRIVATE_BOOTCLASSPATH))) \
+ $(addprefix --classpath ,$(strip $(PRIVATE_ALL_JAVA_HEADER_LIBRARIES))) \
|| ( rm -rf $(dir $@)/classes-turbine ; exit 41 ) && \
$(MERGE_ZIPS) -j --ignore-duplicates -stripDir META-INF $@.tmp $@.premerged $(call reverse-list,$(PRIVATE_STATIC_JAVA_HEADER_LIBRARIES)) ; \
else \
diff --git a/target/board/generic_arm64/BoardConfig.mk b/target/board/generic_arm64/BoardConfig.mk
index e066e3a..be8ea39 100644
--- a/target/board/generic_arm64/BoardConfig.mk
+++ b/target/board/generic_arm64/BoardConfig.mk
@@ -99,3 +99,5 @@
# Enable A/B update
TARGET_NO_RECOVERY := true
BOARD_BUILD_SYSTEM_ROOT_IMAGE := true
+
+BOARD_VNDK_VERSION := current
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index 3a0a788..7ce1ec8 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -159,6 +159,7 @@
import os.path
import shlex
import shutil
+import struct
import subprocess
import sys
import tempfile
@@ -955,8 +956,15 @@
return metadata
-class StreamingPropertyFiles(object):
- """Computes the ota-streaming-property-files string for streaming A/B OTA.
+class PropertyFiles(object):
+ """A class that computes the property-files string for an OTA package.
+
+ A property-files string is a comma-separated string that contains the
+ offset/size info for an OTA package. The entries, which must be ZIP_STORED,
+ can be fetched directly with the package URL along with the offset/size info.
+ These strings can be used for streaming A/B OTAs, or allowing an updater to
+ download package metadata entry directly, without paying the cost of
+ downloading entire package.
Computing the final property-files string requires two passes. Because doing
the whole package signing (with signapk.jar) will possibly reorder the ZIP
@@ -966,7 +974,7 @@
This class provides functions to be called for each pass. The general flow is
as follows.
- property_files = StreamingPropertyFiles()
+ property_files = PropertyFiles()
# The first pass, which writes placeholders before doing initial signing.
property_files.Compute()
SignOutput()
@@ -981,17 +989,9 @@
"""
def __init__(self):
- self.required = (
- # payload.bin and payload_properties.txt must exist.
- 'payload.bin',
- 'payload_properties.txt',
- )
- self.optional = (
- # care_map.txt is available only if dm-verity is enabled.
- 'care_map.txt',
- # compatibility.zip is available only if target supports Treble.
- 'compatibility.zip',
- )
+ self.name = None
+ self.required = ()
+ self.optional = ()
def Compute(self, input_zip):
"""Computes and returns a property-files string with placeholders.
@@ -1064,6 +1064,7 @@
return '%s:%d:%d' % (os.path.basename(name), offset, size)
tokens = []
+ tokens.extend(self._GetPrecomputed(zip_file))
for entry in self.required:
tokens.append(ComputeEntryOffsetSize(entry))
for entry in self.optional:
@@ -1082,8 +1083,127 @@
return ','.join(tokens)
+ def _GetPrecomputed(self, input_zip):
+ """Computes the additional tokens to be included into the property-files.
-def FinalizeMetadata(metadata, input_file, output_file):
+ This applies to tokens without actual ZIP entries, such as
+ payload_metadadata.bin. We want to expose the offset/size to updaters, so
+ that they can download the payload metadata directly with the info.
+
+ Args:
+ input_zip: The input zip file.
+
+ Returns:
+ A list of strings (tokens) to be added to the property-files string.
+ """
+ # pylint: disable=no-self-use
+ # pylint: disable=unused-argument
+ return []
+
+
+class StreamingPropertyFiles(PropertyFiles):
+ """A subclass for computing the property-files for streaming A/B OTAs."""
+
+ def __init__(self):
+ super(StreamingPropertyFiles, self).__init__()
+ self.name = 'ota-streaming-property-files'
+ self.required = (
+ # payload.bin and payload_properties.txt must exist.
+ 'payload.bin',
+ 'payload_properties.txt',
+ )
+ self.optional = (
+ # care_map.txt is available only if dm-verity is enabled.
+ 'care_map.txt',
+ # compatibility.zip is available only if target supports Treble.
+ 'compatibility.zip',
+ )
+
+
+class AbOtaPropertyFiles(StreamingPropertyFiles):
+ """The property-files for A/B OTA that includes payload_metadata.bin info.
+
+ Since P, we expose one more token (aka property-file), in addition to the ones
+ for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'.
+ 'payload_metadata.bin' is the header part of a payload ('payload.bin'), which
+ doesn't exist as a separate ZIP entry, but can be used to verify if the
+ payload can be applied on the given device.
+
+ For backward compatibility, we keep both of the 'ota-streaming-property-files'
+ and the newly added 'ota-property-files' in P. The new token will only be
+ available in 'ota-property-files'.
+ """
+
+ def __init__(self):
+ super(AbOtaPropertyFiles, self).__init__()
+ self.name = 'ota-property-files'
+
+ def _GetPrecomputed(self, input_zip):
+ offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip)
+ return ['payload_metadata.bin:{}:{}'.format(offset, size)]
+
+ @staticmethod
+ def _GetPayloadMetadataOffsetAndSize(input_zip):
+ """Computes the offset and size of the payload metadata for a given package.
+
+ (From system/update_engine/update_metadata.proto)
+ A delta update file contains all the deltas needed to update a system from
+ one specific version to another specific version. The update format is
+ represented by this struct pseudocode:
+
+ struct delta_update_file {
+ char magic[4] = "CrAU";
+ uint64 file_format_version;
+ uint64 manifest_size; // Size of protobuf DeltaArchiveManifest
+
+ // Only present if format_version > 1:
+ uint32 metadata_signature_size;
+
+ // The Bzip2 compressed DeltaArchiveManifest
+ char manifest[metadata_signature_size];
+
+ // The signature of the metadata (from the beginning of the payload up to
+ // this location, not including the signature itself). This is a
+ // serialized Signatures message.
+ char medatada_signature_message[metadata_signature_size];
+
+ // Data blobs for files, no specific format. The specific offset
+ // and length of each data blob is recorded in the DeltaArchiveManifest.
+ struct {
+ char data[];
+ } blobs[];
+
+ // These two are not signed:
+ uint64 payload_signatures_message_size;
+ char payload_signatures_message[];
+ };
+
+ 'payload-metadata.bin' contains all the bytes from the beginning of the
+ payload, till the end of 'medatada_signature_message'.
+ """
+ payload_info = input_zip.getinfo('payload.bin')
+ payload_offset = payload_info.header_offset + len(payload_info.FileHeader())
+ payload_size = payload_info.file_size
+
+ with input_zip.open('payload.bin', 'r') as payload_fp:
+ header_bin = payload_fp.read(24)
+
+ # network byte order (big-endian)
+ header = struct.unpack("!IQQL", header_bin)
+
+ # 'CrAU'
+ magic = header[0]
+ assert magic == 0x43724155, "Invalid magic: {:x}".format(magic)
+
+ manifest_size = header[2]
+ metadata_signature_size = header[3]
+ metadata_total = 24 + manifest_size + metadata_signature_size
+ assert metadata_total < payload_size
+
+ return (payload_offset, metadata_total)
+
+
+def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
"""Finalizes the metadata and signs an A/B OTA package.
In order to stream an A/B OTA package, we need 'ota-streaming-property-files'
@@ -1101,14 +1221,14 @@
input_file: The input ZIP filename that doesn't contain the package METADATA
entry yet.
output_file: The final output ZIP filename.
+ needed_property_files: The list of PropertyFiles' to be generated.
"""
output_zip = zipfile.ZipFile(
input_file, 'a', compression=zipfile.ZIP_DEFLATED)
- property_files = StreamingPropertyFiles()
-
# Write the current metadata entry with placeholders.
- metadata['ota-streaming-property-files'] = property_files.Compute(output_zip)
+ for property_files in needed_property_files:
+ metadata[property_files.name] = property_files.Compute(output_zip)
WriteMetadata(metadata, output_zip)
common.ZipClose(output_zip)
@@ -1122,14 +1242,14 @@
# Open the signed zip. Compute the final metadata that's needed for streaming.
with zipfile.ZipFile(prelim_signing, 'r') as prelim_signing_zip:
- expected_length = len(metadata['ota-streaming-property-files'])
- metadata['ota-streaming-property-files'] = property_files.Finalize(
- prelim_signing_zip, expected_length)
+ for property_files in needed_property_files:
+ metadata[property_files.name] = property_files.Finalize(
+ prelim_signing_zip, len(metadata[property_files.name]))
# Replace the METADATA entry.
common.ZipDelete(prelim_signing, METADATA_NAME)
- output_zip = zipfile.ZipFile(prelim_signing, 'a',
- compression=zipfile.ZIP_DEFLATED)
+ output_zip = zipfile.ZipFile(
+ prelim_signing, 'a', compression=zipfile.ZIP_DEFLATED)
WriteMetadata(metadata, output_zip)
common.ZipClose(output_zip)
@@ -1138,8 +1258,8 @@
# Reopen the final signed zip to double check the streaming metadata.
with zipfile.ZipFile(output_file, 'r') as output_zip:
- property_files.Verify(
- output_zip, metadata['ota-streaming-property-files'].strip())
+ for property_files in needed_property_files:
+ property_files.Verify(output_zip, metadata[property_files.name].strip())
def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
@@ -1555,7 +1675,15 @@
# FinalizeMetadata().
common.ZipClose(output_zip)
- FinalizeMetadata(metadata, staging_file, output_file)
+ # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers
+ # all the info of the latter. However, system updaters and OTA servers need to
+ # take time to switch to the new flag. We keep both of the flags for
+ # P-timeframe, and will remove StreamingPropertyFiles in later release.
+ needed_property_files = (
+ AbOtaPropertyFiles(),
+ StreamingPropertyFiles(),
+ )
+ FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
def main(argv):
diff --git a/tools/releasetools/test_ota_from_target_files.py b/tools/releasetools/test_ota_from_target_files.py
index c8e87bf..4c0b890 100644
--- a/tools/releasetools/test_ota_from_target_files.py
+++ b/tools/releasetools/test_ota_from_target_files.py
@@ -17,17 +17,18 @@
import copy
import os
import os.path
+import subprocess
import unittest
import zipfile
import common
import test_utils
from ota_from_target_files import (
- _LoadOemDicts, BuildInfo, GetPackageMetadata,
+ _LoadOemDicts, AbOtaPropertyFiles, BuildInfo, GetPackageMetadata,
GetTargetFilesZipForSecondaryImages,
GetTargetFilesZipWithoutPostinstallConfig,
- Payload, PayloadSigner, POSTINSTALL_CONFIG, StreamingPropertyFiles,
- WriteFingerprintAssertion)
+ Payload, PayloadSigner, POSTINSTALL_CONFIG, PropertyFiles,
+ StreamingPropertyFiles, WriteFingerprintAssertion)
def construct_target_files(secondary=False):
@@ -590,7 +591,23 @@
self.assertNotIn(POSTINSTALL_CONFIG, verify_zip.namelist())
-class StreamingPropertyFilesTest(unittest.TestCase):
+class TestPropertyFiles(PropertyFiles):
+ """A class that extends PropertyFiles for testing purpose."""
+
+ def __init__(self):
+ super(TestPropertyFiles, self).__init__()
+ self.name = 'ota-test-property-files'
+ self.required = (
+ 'required-entry1',
+ 'required-entry2',
+ )
+ self.optional = (
+ 'optional-entry1',
+ 'optional-entry2',
+ )
+
+
+class PropertyFilesTest(unittest.TestCase):
def tearDown(self):
common.Cleanup()
@@ -607,7 +624,7 @@
return zip_file
@staticmethod
- def _parse_streaming_metadata_string(data):
+ def _parse_property_files_string(data):
result = {}
for token in data.split(','):
name, info = token.split(':', 1)
@@ -627,47 +644,57 @@
def test_Compute(self):
entries = (
- 'payload.bin',
- 'payload_properties.txt',
+ 'required-entry1',
+ 'required-entry2',
)
zip_file = self._construct_zip_package(entries)
- property_files = StreamingPropertyFiles()
+ property_files = TestPropertyFiles()
with zipfile.ZipFile(zip_file, 'r') as zip_fp:
- streaming_metadata = property_files.Compute(zip_fp)
+ property_files_string = property_files.Compute(zip_fp)
- tokens = self._parse_streaming_metadata_string(streaming_metadata)
+ tokens = self._parse_property_files_string(property_files_string)
self.assertEqual(3, len(tokens))
self._verify_entries(zip_file, tokens, entries)
- def test_Compute_withCareMapTxtAndCompatibilityZip(self):
+ def test_Compute_withOptionalEntries(self):
entries = (
- 'payload.bin',
- 'payload_properties.txt',
- 'care_map.txt',
- 'compatibility.zip',
+ 'required-entry1',
+ 'required-entry2',
+ 'optional-entry1',
+ 'optional-entry2',
)
zip_file = self._construct_zip_package(entries)
- property_files = StreamingPropertyFiles()
+ property_files = TestPropertyFiles()
with zipfile.ZipFile(zip_file, 'r') as zip_fp:
- streaming_metadata = property_files.Compute(zip_fp)
+ property_files_string = property_files.Compute(zip_fp)
- tokens = self._parse_streaming_metadata_string(streaming_metadata)
+ tokens = self._parse_property_files_string(property_files_string)
self.assertEqual(5, len(tokens))
self._verify_entries(zip_file, tokens, entries)
+ def test_Compute_missingRequiredEntry(self):
+ entries = (
+ 'required-entry2',
+ )
+ zip_file = self._construct_zip_package(entries)
+ property_files = TestPropertyFiles()
+ with zipfile.ZipFile(zip_file, 'r') as zip_fp:
+ self.assertRaises(KeyError, property_files.Compute, zip_fp)
+
def test_Finalize(self):
entries = [
- 'payload.bin',
- 'payload_properties.txt',
+ 'required-entry1',
+ 'required-entry2',
'META-INF/com/android/metadata',
]
zip_file = self._construct_zip_package(entries)
- property_files = StreamingPropertyFiles()
+ property_files = TestPropertyFiles()
with zipfile.ZipFile(zip_file, 'r') as zip_fp:
+ # pylint: disable=protected-access
raw_metadata = property_files._GetPropertyFilesString(
zip_fp, reserve_space=False)
streaming_metadata = property_files.Finalize(zip_fp, len(raw_metadata))
- tokens = self._parse_streaming_metadata_string(streaming_metadata)
+ tokens = self._parse_property_files_string(streaming_metadata)
self.assertEqual(3, len(tokens))
# 'META-INF/com/android/metadata' will be key'd as 'metadata' in the
@@ -677,15 +704,17 @@
def test_Finalize_assertReservedLength(self):
entries = (
- 'payload.bin',
- 'payload_properties.txt',
- 'care_map.txt',
+ 'required-entry1',
+ 'required-entry2',
+ 'optional-entry1',
+ 'optional-entry2',
'META-INF/com/android/metadata',
)
zip_file = self._construct_zip_package(entries)
- property_files = StreamingPropertyFiles()
+ property_files = TestPropertyFiles()
with zipfile.ZipFile(zip_file, 'r') as zip_fp:
# First get the raw metadata string (i.e. without padding space).
+ # pylint: disable=protected-access
raw_metadata = property_files._GetPropertyFilesString(
zip_fp, reserve_space=False)
raw_length = len(raw_metadata)
@@ -710,15 +739,17 @@
def test_Verify(self):
entries = (
- 'payload.bin',
- 'payload_properties.txt',
- 'care_map.txt',
+ 'required-entry1',
+ 'required-entry2',
+ 'optional-entry1',
+ 'optional-entry2',
'META-INF/com/android/metadata',
)
zip_file = self._construct_zip_package(entries)
- property_files = StreamingPropertyFiles()
+ property_files = TestPropertyFiles()
with zipfile.ZipFile(zip_file, 'r') as zip_fp:
# First get the raw metadata string (i.e. without padding space).
+ # pylint: disable=protected-access
raw_metadata = property_files._GetPropertyFilesString(
zip_fp, reserve_space=False)
@@ -730,6 +761,235 @@
AssertionError, property_files.Verify, zip_fp, raw_metadata + 'x')
+class StreamingPropertyFilesTest(PropertyFilesTest):
+ """Additional sanity checks specialized for StreamingPropertyFiles."""
+
+ def test_init(self):
+ property_files = StreamingPropertyFiles()
+ self.assertEqual('ota-streaming-property-files', property_files.name)
+ self.assertEqual(
+ (
+ 'payload.bin',
+ 'payload_properties.txt',
+ ),
+ property_files.required)
+ self.assertEqual(
+ (
+ 'care_map.txt',
+ 'compatibility.zip',
+ ),
+ property_files.optional)
+
+ def test_Compute(self):
+ entries = (
+ 'payload.bin',
+ 'payload_properties.txt',
+ 'care_map.txt',
+ 'compatibility.zip',
+ )
+ zip_file = self._construct_zip_package(entries)
+ property_files = StreamingPropertyFiles()
+ with zipfile.ZipFile(zip_file, 'r') as zip_fp:
+ property_files_string = property_files.Compute(zip_fp)
+
+ tokens = self._parse_property_files_string(property_files_string)
+ self.assertEqual(5, len(tokens))
+ self._verify_entries(zip_file, tokens, entries)
+
+ def test_Finalize(self):
+ entries = [
+ 'payload.bin',
+ 'payload_properties.txt',
+ 'care_map.txt',
+ 'compatibility.zip',
+ 'META-INF/com/android/metadata',
+ ]
+ zip_file = self._construct_zip_package(entries)
+ property_files = StreamingPropertyFiles()
+ with zipfile.ZipFile(zip_file, 'r') as zip_fp:
+ # pylint: disable=protected-access
+ raw_metadata = property_files._GetPropertyFilesString(
+ zip_fp, reserve_space=False)
+ streaming_metadata = property_files.Finalize(zip_fp, len(raw_metadata))
+ tokens = self._parse_property_files_string(streaming_metadata)
+
+ self.assertEqual(5, len(tokens))
+ # 'META-INF/com/android/metadata' will be key'd as 'metadata' in the
+ # streaming metadata.
+ entries[4] = 'metadata'
+ self._verify_entries(zip_file, tokens, entries)
+
+ def test_Verify(self):
+ entries = (
+ 'payload.bin',
+ 'payload_properties.txt',
+ 'care_map.txt',
+ 'compatibility.zip',
+ 'META-INF/com/android/metadata',
+ )
+ zip_file = self._construct_zip_package(entries)
+ property_files = StreamingPropertyFiles()
+ with zipfile.ZipFile(zip_file, 'r') as zip_fp:
+ # First get the raw metadata string (i.e. without padding space).
+ # pylint: disable=protected-access
+ raw_metadata = property_files._GetPropertyFilesString(
+ zip_fp, reserve_space=False)
+
+ # Should pass the test if verification passes.
+ property_files.Verify(zip_fp, raw_metadata)
+
+ # Or raise on verification failure.
+ self.assertRaises(
+ AssertionError, property_files.Verify, zip_fp, raw_metadata + 'x')
+
+
+class AbOtaPropertyFilesTest(PropertyFilesTest):
+ """Additional sanity checks specialized for AbOtaPropertyFiles."""
+
+ # The size for payload and metadata signature size.
+ SIGNATURE_SIZE = 256
+
+ def setUp(self):
+ self.testdata_dir = test_utils.get_testdata_dir()
+ self.assertTrue(os.path.exists(self.testdata_dir))
+
+ common.OPTIONS.wipe_user_data = False
+ common.OPTIONS.payload_signer = None
+ common.OPTIONS.payload_signer_args = None
+ common.OPTIONS.package_key = os.path.join(self.testdata_dir, 'testkey')
+ common.OPTIONS.key_passwords = {
+ common.OPTIONS.package_key : None,
+ }
+
+ def test_init(self):
+ property_files = AbOtaPropertyFiles()
+ self.assertEqual('ota-property-files', property_files.name)
+ self.assertEqual(
+ (
+ 'payload.bin',
+ 'payload_properties.txt',
+ ),
+ property_files.required)
+ self.assertEqual(
+ (
+ 'care_map.txt',
+ 'compatibility.zip',
+ ),
+ property_files.optional)
+
+ def test_GetPayloadMetadataOffsetAndSize(self):
+ target_file = construct_target_files()
+ payload = Payload()
+ payload.Generate(target_file)
+
+ payload_signer = PayloadSigner()
+ payload.Sign(payload_signer)
+
+ output_file = common.MakeTempFile(suffix='.zip')
+ with zipfile.ZipFile(output_file, 'w') as output_zip:
+ payload.WriteToZip(output_zip)
+
+ # Find out the payload metadata offset and size.
+ property_files = AbOtaPropertyFiles()
+ with zipfile.ZipFile(output_file) as input_zip:
+ # pylint: disable=protected-access
+ payload_offset, metadata_total = (
+ property_files._GetPayloadMetadataOffsetAndSize(input_zip))
+
+ # Read in the metadata signature directly.
+ with open(output_file, 'rb') as verify_fp:
+ verify_fp.seek(payload_offset + metadata_total - self.SIGNATURE_SIZE)
+ metadata_signature = verify_fp.read(self.SIGNATURE_SIZE)
+
+ # Now we extract the metadata hash via brillo_update_payload script, which
+ # will serve as the oracle result.
+ payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
+ metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
+ cmd = ['brillo_update_payload', 'hash',
+ '--unsigned_payload', payload.payload_file,
+ '--signature_size', str(self.SIGNATURE_SIZE),
+ '--metadata_hash_file', metadata_sig_file,
+ '--payload_hash_file', payload_sig_file]
+ proc = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ stdoutdata, _ = proc.communicate()
+ self.assertEqual(
+ 0, proc.returncode,
+ 'Failed to run brillo_update_payload: {}'.format(stdoutdata))
+
+ signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
+
+ # Finally we can compare the two signatures.
+ with open(signed_metadata_sig_file, 'rb') as verify_fp:
+ self.assertEqual(verify_fp.read(), metadata_signature)
+
+ @staticmethod
+ def _construct_zip_package_withValidPayload(with_metadata=False):
+ # Cannot use _construct_zip_package() since we need a "valid" payload.bin.
+ target_file = construct_target_files()
+ payload = Payload()
+ payload.Generate(target_file)
+
+ payload_signer = PayloadSigner()
+ payload.Sign(payload_signer)
+
+ zip_file = common.MakeTempFile(suffix='.zip')
+ with zipfile.ZipFile(zip_file, 'w') as zip_fp:
+ # 'payload.bin',
+ payload.WriteToZip(zip_fp)
+
+ # Other entries.
+ entries = ['care_map.txt', 'compatibility.zip']
+
+ # Put META-INF/com/android/metadata if needed.
+ if with_metadata:
+ entries.append('META-INF/com/android/metadata')
+
+ for entry in entries:
+ zip_fp.writestr(
+ entry, entry.replace('.', '-').upper(), zipfile.ZIP_STORED)
+
+ return zip_file
+
+ def test_Compute(self):
+ zip_file = self._construct_zip_package_withValidPayload()
+ property_files = AbOtaPropertyFiles()
+ with zipfile.ZipFile(zip_file, 'r') as zip_fp:
+ property_files_string = property_files.Compute(zip_fp)
+
+ tokens = self._parse_property_files_string(property_files_string)
+ # "6" indcludes the four entries above, one metadata entry, and one entry
+ # for payload-metadata.bin.
+ self.assertEqual(6, len(tokens))
+ self._verify_entries(
+ zip_file, tokens, ('care_map.txt', 'compatibility.zip'))
+
+ def test_Finalize(self):
+ zip_file = self._construct_zip_package_withValidPayload(with_metadata=True)
+ property_files = AbOtaPropertyFiles()
+ with zipfile.ZipFile(zip_file, 'r') as zip_fp:
+ # pylint: disable=protected-access
+ raw_metadata = property_files._GetPropertyFilesString(
+ zip_fp, reserve_space=False)
+ property_files_string = property_files.Finalize(zip_fp, len(raw_metadata))
+
+ tokens = self._parse_property_files_string(property_files_string)
+ # "6" indcludes the four entries above, one metadata entry, and one entry
+ # for payload-metadata.bin.
+ self.assertEqual(6, len(tokens))
+ self._verify_entries(
+ zip_file, tokens, ('care_map.txt', 'compatibility.zip'))
+
+ def test_Verify(self):
+ zip_file = self._construct_zip_package_withValidPayload(with_metadata=True)
+ property_files = AbOtaPropertyFiles()
+ with zipfile.ZipFile(zip_file, 'r') as zip_fp:
+ # pylint: disable=protected-access
+ raw_metadata = property_files._GetPropertyFilesString(
+ zip_fp, reserve_space=False)
+
+ property_files.Verify(zip_fp, raw_metadata)
+
+
class PayloadSignerTest(unittest.TestCase):
SIGFILE = 'sigfile.bin'