Merge "Switch to clang-4579689."
diff --git a/core/clear_vars.mk b/core/clear_vars.mk
index 7d3fa75..0f700da 100644
--- a/core/clear_vars.mk
+++ b/core/clear_vars.mk
@@ -435,6 +435,13 @@
LOCAL_WHOLE_STATIC_LIBRARIES_32:=
LOCAL_WHOLE_STATIC_LIBRARIES_64:=
+# Robolectric variables
+LOCAL_INSTRUMENT_SOURCE_DIRS :=
+LOCAL_ROBOTEST_FAILURE_FATAL :=
+LOCAL_ROBOTEST_FILES :=
+LOCAL_ROBOTEST_TIMEOUT :=
+LOCAL_TEST_PACKAGE :=
+
# Aux specific variables
LOCAL_AUX_ARCH :=
LOCAL_AUX_CPU :=
diff --git a/core/config.mk b/core/config.mk
index d0a4af5..497c420 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -686,6 +686,7 @@
DEXDUMP := $(HOST_OUT_EXECUTABLES)/dexdump2$(BUILD_EXECUTABLE_SUFFIX)
PROFMAN := $(HOST_OUT_EXECUTABLES)/profman
+HIDDENAPI := $(HOST_OUT_EXECUTABLES)/hiddenapi
# relocation packer
RELOCATION_PACKER := prebuilts/misc/$(BUILD_OS)-$(HOST_PREBUILT_ARCH)/relocation_packer/relocation_packer
@@ -836,12 +837,6 @@
DEFAULT_SYSTEM_DEV_CERTIFICATE := build/target/product/security/testkey
endif
-FRAMEWORK_MANIFEST_INPUT_FILES := system/libhidl/manifest.xml
-ifdef DEVICE_FRAMEWORK_MANIFEST_FILE
- FRAMEWORK_MANIFEST_INPUT_FILES += $(DEVICE_FRAMEWORK_MANIFEST_FILE)
-endif
-$(KATI_obsolete_var DEVICE_FRAMEWORK_MANIFEST_FILE,No one should ever need to use this.)
-
BUILD_NUMBER_FROM_FILE := $$(cat $(OUT_DIR)/build_number.txt)
BUILD_DATETIME_FROM_FILE := $$(cat $(OUT_DIR)/build_date.txt)
diff --git a/core/definitions.mk b/core/definitions.mk
index 2be4f59..4eac01c 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -2808,6 +2808,37 @@
fi
endef
+define hiddenapi-copy-dex-files
+$(2): $(1) $(HIDDENAPI) $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \
+ $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST) $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
+ @rm -rf $(dir $(2))
+ @mkdir -p $(dir $(2))
+ find $(dir $(1)) -maxdepth 1 -name "classes*.dex" | sort | \
+ xargs -I{} cp -f {} $(dir $(2))
+ find $(dir $(2)) -name "classes*.dex" | sort | sed 's/^/--dex=/' | \
+ xargs $(HIDDENAPI) --light-greylist=$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \
+ --dark-greylist=$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST) \
+ --blacklist=$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
+endef
+
+define hiddenapi-copy-soong-jar
+$(2): PRIVATE_FOLDER := $(dir $(2))dex-hiddenapi
+$(2): $(1) $(HIDDENAPI) $(SOONG_ZIP) $(MERGE_ZIPS) $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \
+ $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST) $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
+ @echo "Hidden API: $$@"
+ $$(copy-file-to-target)
+ @rm -rf $${PRIVATE_FOLDER}
+ @mkdir -p $${PRIVATE_FOLDER}
+ unzip -q $(2) 'classes*.dex' -d $${PRIVATE_FOLDER}
+ find $${PRIVATE_FOLDER} -name "classes*.dex" | sort | sed 's/^/--dex=/' | \
+ xargs $(HIDDENAPI) --light-greylist=$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \
+ --dark-greylist=$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST) \
+ --blacklist=$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
+ $(SOONG_ZIP) -o $${PRIVATE_FOLDER}/classes.dex.jar -C $${PRIVATE_FOLDER} -D $${PRIVATE_FOLDER}
+ $(MERGE_ZIPS) -D -zipToNotStrip $${PRIVATE_FOLDER}/classes.dex.jar -stripFile "classes*.dex" \
+ $(2) $${PRIVATE_FOLDER}/classes.dex.jar $(1)
+endef
+
###########################################################
## Commands to call Proguard
###########################################################
diff --git a/core/dex_preopt_libart.mk b/core/dex_preopt_libart.mk
index af2355a..0fa4b8c 100644
--- a/core/dex_preopt_libart.mk
+++ b/core/dex_preopt_libart.mk
@@ -120,6 +120,7 @@
@mkdir -p $(dir $@)
ANDROID_LOG_TAGS="*:e" $(PROFMAN) \
--create-profile-from=$(PRIVATE_PROFILE_INPUT_LOCATION) \
+ --skip-apk-verification \
$(addprefix --apk=,$(LIBART_TARGET_BOOT_DEX_FILES)) \
$(addprefix --dex-location=,$(LIBART_TARGET_BOOT_DEX_LOCATIONS)) \
--reference-profile-file=$@
diff --git a/core/java.mk b/core/java.mk
index ee071c9..6f5dce4 100644
--- a/core/java.mk
+++ b/core/java.mk
@@ -73,6 +73,7 @@
full_classes_proguard_jar := $(intermediates.COMMON)/classes-proguard.jar
full_classes_combined_jar := $(intermediates.COMMON)/classes-combined.jar
built_dex_intermediate := $(intermediates.COMMON)/dex/classes.dex
+built_dex_hiddenapi := $(intermediates.COMMON)/dex-hiddenapi/classes.dex
full_classes_stubs_jar := $(intermediates.COMMON)/stubs.jar
java_source_list_file := $(intermediates.COMMON)/java-source-list
@@ -762,7 +763,14 @@
endif
endif
-$(built_dex): $(built_dex_intermediate)
+ifneq ($(filter $(LOCAL_MODULE),$(PRODUCT_BOOT_JARS)),) # is_boot_jar
+ $(eval $(call hiddenapi-copy-dex-files,$(built_dex_intermediate),$(built_dex_hiddenapi)))
+ built_dex_copy_from := $(built_dex_hiddenapi)
+else # !is_boot_jar
+ built_dex_copy_from := $(built_dex_intermediate)
+endif # is_boot_jar
+
+$(built_dex): $(built_dex_copy_from)
@echo Copying: $@
$(hide) mkdir -p $(dir $@)
$(hide) rm -f $(dir $@)/classes*.dex
diff --git a/core/soong_java_prebuilt.mk b/core/soong_java_prebuilt.mk
index 6cf9422..5c2d768 100644
--- a/core/soong_java_prebuilt.mk
+++ b/core/soong_java_prebuilt.mk
@@ -40,7 +40,11 @@
ifdef LOCAL_SOONG_DEX_JAR
ifndef LOCAL_IS_HOST_MODULE
- $(eval $(call copy-one-file,$(LOCAL_SOONG_DEX_JAR),$(common_javalib.jar)))
+ ifneq ($(filter $(LOCAL_MODULE),$(PRODUCT_BOOT_JARS)),) # is_boot_jar
+ $(eval $(call hiddenapi-copy-soong-jar,$(LOCAL_SOONG_DEX_JAR),$(common_javalib.jar)))
+ else # !is_boot_jar
+ $(eval $(call copy-one-file,$(LOCAL_SOONG_DEX_JAR),$(common_javalib.jar)))
+ endif # is_boot_jar
$(eval $(call add-dependency,$(common_javalib.jar),$(full_classes_jar) $(full_classes_header_jar)))
dex_preopt_profile_src_file := $(common_javalib.jar)
diff --git a/target/board/Android.mk b/target/board/Android.mk
index 9b2620c..7fe45eb 100644
--- a/target/board/Android.mk
+++ b/target/board/Android.mk
@@ -52,65 +52,3 @@
include $(BUILD_PREBUILT)
BUILT_VENDOR_MANIFEST := $(LOCAL_BUILT_MODULE)
endif
-
-# VNDK Version in device compatibility matrix and framework manifest
-ifeq ($(BOARD_VNDK_VERSION),current)
-VINTF_VNDK_VERSION := $(PLATFORM_VNDK_VERSION)
-else
-VINTF_VNDK_VERSION := $(BOARD_VNDK_VERSION)
-endif
-
-# Device Compatibility Matrix
-ifdef DEVICE_MATRIX_FILE
-include $(CLEAR_VARS)
-LOCAL_MODULE := device_compatibility_matrix.xml
-LOCAL_MODULE_STEM := compatibility_matrix.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/etc/vintf
-
-GEN := $(local-generated-sources-dir)/compatibility_matrix.xml
-
-$(GEN): PRIVATE_VINTF_VNDK_VERSION := $(VINTF_VNDK_VERSION)
-$(GEN): $(DEVICE_MATRIX_FILE) $(HOST_OUT_EXECUTABLES)/assemble_vintf
- REQUIRED_VNDK_VERSION=$(PRIVATE_VINTF_VNDK_VERSION) \
- BOARD_SYSTEMSDK_VERSIONS="$(BOARD_SYSTEMSDK_VERSIONS)" \
- $(HOST_OUT_EXECUTABLES)/assemble_vintf -i $< -o $@
-
-LOCAL_PREBUILT_MODULE_FILE := $(GEN)
-include $(BUILD_PREBUILT)
-BUILT_VENDOR_MATRIX := $(LOCAL_BUILT_MODULE)
-endif
-
-# Framework Manifest
-include $(CLEAR_VARS)
-LOCAL_MODULE := framework_manifest.xml
-LOCAL_MODULE_STEM := manifest.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT)/etc/vintf
-
-GEN := $(local-generated-sources-dir)/manifest.xml
-
-$(GEN): PRIVATE_FLAGS :=
-
-ifeq ($(PRODUCT_ENFORCE_VINTF_MANIFEST),true)
-ifdef BUILT_VENDOR_MATRIX
-$(GEN): $(BUILT_VENDOR_MATRIX)
-$(GEN): PRIVATE_FLAGS += -c "$(BUILT_VENDOR_MATRIX)"
-endif
-endif
-
-$(GEN): PRIVATE_VINTF_VNDK_VERSION := $(VINTF_VNDK_VERSION)
-$(GEN): PRIVATE_FRAMEWORK_MANIFEST_INPUT_FILES := $(FRAMEWORK_MANIFEST_INPUT_FILES)
-$(GEN): $(FRAMEWORK_MANIFEST_INPUT_FILES) $(HOST_OUT_EXECUTABLES)/assemble_vintf
- BOARD_SEPOLICY_VERS=$(BOARD_SEPOLICY_VERS) \
- PROVIDED_VNDK_VERSIONS="$(PRIVATE_VINTF_VNDK_VERSION) $(PRODUCT_EXTRA_VNDK_VERSIONS)" \
- PLATFORM_SYSTEMSDK_VERSIONS="$(PLATFORM_SYSTEMSDK_VERSIONS)" \
- $(HOST_OUT_EXECUTABLES)/assemble_vintf \
- -i $(call normalize-path-list,$(PRIVATE_FRAMEWORK_MANIFEST_INPUT_FILES)) \
- -o $@ $(PRIVATE_FLAGS)
-
-LOCAL_PREBUILT_MODULE_FILE := $(GEN)
-include $(BUILD_PREBUILT)
-BUILT_SYSTEM_MANIFEST := $(LOCAL_BUILT_MODULE)
-
-VINTF_VNDK_VERSION :=
diff --git a/target/product/core.mk b/target/product/core.mk
index cab8d97..bbc2b75 100644
--- a/target/product/core.mk
+++ b/target/product/core.mk
@@ -56,6 +56,7 @@
StorageManager \
Telecom \
TeleService \
+ Traceur \
VpnDialogs \
vr \
MmsService
diff --git a/target/product/core_64_bit_only.mk b/target/product/core_64_bit_only.mk
new file mode 100644
index 0000000..72d30f5
--- /dev/null
+++ b/target/product/core_64_bit_only.mk
@@ -0,0 +1,30 @@
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Inherit from this product for devices that support only 64-bit apps using:
+# $(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit_only.mk)
+# The inheritance for this must come before the inheritance chain that leads
+# to core_minimal.mk.
+
+# Copy the 64-bit zygote startup script
+PRODUCT_COPY_FILES += system/core/rootdir/init.zygote64.rc:root/init.zygote64.rc
+
+# Set the zygote property to select the 64-bit script.
+# This line must be parsed before the one in core_minimal.mk
+PRODUCT_DEFAULT_PROPERTY_OVERRIDES += ro.zygote=zygote64
+
+TARGET_SUPPORTS_32_BIT_APPS := false
+TARGET_SUPPORTS_64_BIT_APPS := true
diff --git a/tools/releasetools/check_ota_package_signature.py b/tools/releasetools/check_ota_package_signature.py
index b5e9d8b..3cac90a 100755
--- a/tools/releasetools/check_ota_package_signature.py
+++ b/tools/releasetools/check_ota_package_signature.py
@@ -154,22 +154,18 @@
print('Verifying A/B OTA payload signatures...')
# Dump pubkey from the certificate.
- pubkey = common.MakeTempFile(prefix="key-", suffix=".key")
- cmd = ['openssl', 'x509', '-pubkey', '-noout', '-in', cert, '-out', pubkey]
- proc = common.Run(cmd, stdout=subprocess.PIPE)
- stdoutdata, _ = proc.communicate()
- assert proc.returncode == 0, \
- 'Failed to dump public key from certificate: %s\n%s' % (cert, stdoutdata)
+ pubkey = common.MakeTempFile(prefix="key-", suffix=".pem")
+ with open(pubkey, 'wb') as pubkey_fp:
+ pubkey_fp.write(common.ExtractPublicKey(cert))
- package_dir = tempfile.mkdtemp(prefix='package-')
- common.OPTIONS.tempfiles.append(package_dir)
+ package_dir = common.MakeTempDir(prefix='package-')
# Signature verification with delta_generator.
payload_file = package_zip.extract('payload.bin', package_dir)
cmd = ['delta_generator',
'--in_file=' + payload_file,
'--public_key=' + pubkey]
- proc = common.Run(cmd, stdout=subprocess.PIPE)
+ proc = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdoutdata, _ = proc.communicate()
assert proc.returncode == 0, \
'Failed to verify payload with delta_generator: %s\n%s' % (package,
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index d0ee6ae..16600ed 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -25,6 +25,7 @@
import re
import shlex
import shutil
+import string
import subprocess
import sys
import tempfile
@@ -34,6 +35,7 @@
from hashlib import sha1, sha256
import blockimgdiff
+import sparse_img
class Options(object):
def __init__(self):
@@ -124,6 +126,11 @@
return subprocess.Popen(args, **kwargs)
+def RoundUpTo4K(value):
+ rounded_up = value + 4095
+ return rounded_up - (rounded_up % 4096)
+
+
def CloseInheritedPipes():
""" Gmake in MAC OS has file descriptor (PIPE) leak. We close those fds
before doing other work."""
@@ -618,6 +625,56 @@
return tmp, zipfile.ZipFile(filename, "r")
+def GetSparseImage(which, tmpdir, input_zip):
+ """Returns a SparseImage object suitable for passing to BlockImageDiff.
+
+ This function loads the specified sparse image from the given path, and
+ performs additional processing for OTA purpose. For example, it always adds
+ block 0 to clobbered blocks list. It also detects files that cannot be
+ reconstructed from the block list, for whom we should avoid applying imgdiff.
+
+ Args:
+ which: The partition name, which must be "system" or "vendor".
+ tmpdir: The directory that contains the prebuilt image and block map file.
+ input_zip: The target-files ZIP archive.
+
+ Returns:
+ A SparseImage object, with file_map info loaded.
+ """
+ assert which in ("system", "vendor")
+
+ path = os.path.join(tmpdir, "IMAGES", which + ".img")
+ mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
+
+ # The image and map files must have been created prior to calling
+ # ota_from_target_files.py (since LMP).
+ assert os.path.exists(path) and os.path.exists(mappath)
+
+ # In ext4 filesystems, block 0 might be changed even being mounted R/O. We add
+ # it to clobbered_blocks so that it will be written to the target
+ # unconditionally. Note that they are still part of care_map. (Bug: 20939131)
+ clobbered_blocks = "0"
+
+ image = sparse_img.SparseImage(path, mappath, clobbered_blocks)
+
+ # block.map may contain less blocks, because mke2fs may skip allocating blocks
+ # if they contain all zeros. We can't reconstruct such a file from its block
+ # list. Tag such entries accordingly. (Bug: 65213616)
+ for entry in image.file_map:
+ # "/system/framework/am.jar" => "SYSTEM/framework/am.jar".
+ arcname = string.replace(entry, which, which.upper(), 1)[1:]
+ # Skip artificial names, such as "__ZERO", "__NONZERO-1".
+ if arcname not in input_zip.namelist():
+ continue
+
+ info = input_zip.getinfo(arcname)
+ ranges = image.file_map[entry]
+ if RoundUpTo4K(info.file_size) > ranges.size() * 4096:
+ ranges.extra['incomplete'] = True
+
+ return image
+
+
def GetKeyPasswords(keylist):
"""Given a list of keys, prompt the user to enter passwords for
those which require them. Return a {key: password} dict. password
@@ -1747,6 +1804,31 @@
cert = "".join(cert).decode('base64')
return cert
+
+def ExtractPublicKey(cert):
+ """Extracts the public key (PEM-encoded) from the given certificate file.
+
+ Args:
+ cert: The certificate filename.
+
+ Returns:
+ The public key string.
+
+ Raises:
+ AssertionError: On non-zero return from 'openssl'.
+ """
+ # The behavior with '-out' is different between openssl 1.1 and openssl 1.0.
+ # While openssl 1.1 writes the key into the given filename followed by '-out',
+ # openssl 1.0 (both of 1.0.1 and 1.0.2) doesn't. So we collect the output from
+ # stdout instead.
+ cmd = ['openssl', 'x509', '-pubkey', '-noout', '-in', cert]
+ proc = Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ pubkey, stderrdata = proc.communicate()
+ assert proc.returncode == 0, \
+ 'Failed to dump public key from certificate: %s\n%s' % (cert, stderrdata)
+ return pubkey
+
+
def MakeRecoveryPatch(input_dir, output_sink, recovery_img, boot_img,
info_dict=None):
"""Generate a binary patch that creates the recovery image starting
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index 95b7303..12b01c4 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -140,7 +140,6 @@
import common
import edify_generator
-import sparse_img
if sys.hexversion < 0x02070000:
print("Python 2.7 or newer is required.", file=sys.stderr)
@@ -360,6 +359,122 @@
return out_file
+class Payload(object):
+ """Manages the creation and the signing of an A/B OTA Payload."""
+
+ PAYLOAD_BIN = 'payload.bin'
+ PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
+
+ def __init__(self):
+ # The place where the output from the subprocess should go.
+ self._log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
+ self.payload_file = None
+ self.payload_properties = None
+
+ def Generate(self, target_file, source_file=None, additional_args=None):
+ """Generates a payload from the given target-files zip(s).
+
+ Args:
+ target_file: The filename of the target build target-files zip.
+ source_file: The filename of the source build target-files zip; or None if
+ generating a full OTA.
+ additional_args: A list of additional args that should be passed to
+ brillo_update_payload script; or None.
+ """
+ if additional_args is None:
+ additional_args = []
+
+ payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
+ cmd = ["brillo_update_payload", "generate",
+ "--payload", payload_file,
+ "--target_image", target_file]
+ if source_file is not None:
+ cmd.extend(["--source_image", source_file])
+ cmd.extend(additional_args)
+ p = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
+ stdoutdata, _ = p.communicate()
+ assert p.returncode == 0, \
+ "brillo_update_payload generate failed: {}".format(stdoutdata)
+
+ self.payload_file = payload_file
+ self.payload_properties = None
+
+ def Sign(self, payload_signer):
+ """Generates and signs the hashes of the payload and metadata.
+
+ Args:
+ payload_signer: A PayloadSigner() instance that serves the signing work.
+
+ Raises:
+ AssertionError: On any failure when calling brillo_update_payload script.
+ """
+ assert isinstance(payload_signer, PayloadSigner)
+
+ # 1. Generate hashes of the payload and metadata files.
+ 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", self.payload_file,
+ "--signature_size", "256",
+ "--metadata_hash_file", metadata_sig_file,
+ "--payload_hash_file", payload_sig_file]
+ p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
+ p1.communicate()
+ assert p1.returncode == 0, "brillo_update_payload hash failed"
+
+ # 2. Sign the hashes.
+ signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
+ signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
+
+ # 3. Insert the signatures back into the payload file.
+ signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
+ suffix=".bin")
+ cmd = ["brillo_update_payload", "sign",
+ "--unsigned_payload", self.payload_file,
+ "--payload", signed_payload_file,
+ "--signature_size", "256",
+ "--metadata_signature_file", signed_metadata_sig_file,
+ "--payload_signature_file", signed_payload_sig_file]
+ p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
+ p1.communicate()
+ assert p1.returncode == 0, "brillo_update_payload sign failed"
+
+ # 4. Dump the signed payload properties.
+ properties_file = common.MakeTempFile(prefix="payload-properties-",
+ suffix=".txt")
+ cmd = ["brillo_update_payload", "properties",
+ "--payload", signed_payload_file,
+ "--properties_file", properties_file]
+ p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
+ p1.communicate()
+ assert p1.returncode == 0, "brillo_update_payload properties failed"
+
+ if OPTIONS.wipe_user_data:
+ with open(properties_file, "a") as f:
+ f.write("POWERWASH=1\n")
+
+ self.payload_file = signed_payload_file
+ self.payload_properties = properties_file
+
+ def WriteToZip(self, output_zip):
+ """Writes the payload to the given zip.
+
+ Args:
+ output_zip: The output ZipFile instance.
+ """
+ assert self.payload_file is not None
+ assert self.payload_properties is not None
+
+ # Add the signed payload file and properties into the zip. In order to
+ # support streaming, we pack them as ZIP_STORED. So these entries can be
+ # read directly with the offset and length pairs.
+ common.ZipWrite(output_zip, self.payload_file, arcname=Payload.PAYLOAD_BIN,
+ compress_type=zipfile.ZIP_STORED)
+ common.ZipWrite(output_zip, self.payload_properties,
+ arcname=Payload.PAYLOAD_PROPERTIES_TXT,
+ compress_type=zipfile.ZIP_STORED)
+
+
def SignOutput(temp_zip_name, output_zip_name):
pw = OPTIONS.key_passwords[OPTIONS.package_key]
@@ -452,31 +567,6 @@
source_info.GetBuildProp("ro.build.thumbprint"))
-def GetImage(which, tmpdir):
- """Returns an image object suitable for passing to BlockImageDiff.
-
- 'which' partition must be "system" or "vendor". A prebuilt image and file
- map must already exist in tmpdir.
- """
-
- assert which in ("system", "vendor")
-
- path = os.path.join(tmpdir, "IMAGES", which + ".img")
- mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
-
- # The image and map files must have been created prior to calling
- # ota_from_target_files.py (since LMP).
- assert os.path.exists(path) and os.path.exists(mappath)
-
- # Bug: http://b/20939131
- # In ext4 filesystems, block 0 might be changed even being mounted
- # R/O. We add it to clobbered_blocks so that it will be written to the
- # target unconditionally. Note that they are still part of care_map.
- clobbered_blocks = "0"
-
- return sparse_img.SparseImage(path, mappath, clobbered_blocks)
-
-
def AddCompatibilityArchiveIfTrebleEnabled(target_zip, output_zip, target_info,
source_info=None):
"""Adds compatibility info into the output zip if it's Treble-enabled target.
@@ -662,7 +752,7 @@
# has the effect of writing new data from the package to the entire
# partition, but lets us reuse the updater code that writes incrementals to
# do it.
- system_tgt = GetImage("system", OPTIONS.input_tmp)
+ system_tgt = common.GetSparseImage("system", OPTIONS.input_tmp, input_zip)
system_tgt.ResetFileMap()
system_diff = common.BlockDifference("system", system_tgt, src=None)
system_diff.WriteScript(script, output_zip)
@@ -673,7 +763,7 @@
if HasVendorPartition(input_zip):
script.ShowProgress(0.1, 0)
- vendor_tgt = GetImage("vendor", OPTIONS.input_tmp)
+ vendor_tgt = common.GetSparseImage("vendor", OPTIONS.input_tmp, input_zip)
vendor_tgt.ResetFileMap()
vendor_diff = common.BlockDifference("vendor", vendor_tgt)
vendor_diff.WriteScript(script, output_zip)
@@ -778,6 +868,10 @@
'post-build' : target_info.fingerprint,
'post-build-incremental' : target_info.GetBuildProp(
'ro.build.version.incremental'),
+ 'post-sdk-level' : target_info.GetBuildProp(
+ 'ro.build.version.sdk'),
+ 'post-security-patch-level' : target_info.GetBuildProp(
+ 'ro.build.version.security_patch'),
}
if target_info.is_ab:
@@ -846,8 +940,8 @@
target_recovery = common.GetBootableImage(
"/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
- system_src = GetImage("system", OPTIONS.source_tmp)
- system_tgt = GetImage("system", OPTIONS.target_tmp)
+ system_src = common.GetSparseImage("system", OPTIONS.source_tmp, source_zip)
+ system_tgt = common.GetSparseImage("system", OPTIONS.target_tmp, target_zip)
blockimgdiff_version = max(
int(i) for i in target_info.get("blockimgdiff_versions", "1").split(","))
@@ -872,8 +966,8 @@
if HasVendorPartition(target_zip):
if not HasVendorPartition(source_zip):
raise RuntimeError("can't generate incremental that adds /vendor")
- vendor_src = GetImage("vendor", OPTIONS.source_tmp)
- vendor_tgt = GetImage("vendor", OPTIONS.target_tmp)
+ vendor_src = common.GetSparseImage("vendor", OPTIONS.source_tmp, source_zip)
+ vendor_tgt = common.GetSparseImage("vendor", OPTIONS.target_tmp, target_zip)
# Check first block of vendor partition for remount R/W only if
# disk type is ext4
@@ -919,7 +1013,7 @@
if OPTIONS.two_step:
if not source_info.get("multistage_support"):
assert False, "two-step packages not supported by this build"
- fs = OPTIONS.source_info_dict["fstab"]["/misc"]
+ fs = source_info["fstab"]["/misc"]
assert fs.fs_type.upper() == "EMMC", \
"two-step packages only supported on devices with EMMC /misc partitions"
bcb_dev = {"bcb_dev" : fs.device}
@@ -1049,7 +1143,6 @@
if OPTIONS.wipe_user_data:
script.Print("Erasing user data...")
script.FormatPartition("/data")
- metadata["ota-wipe"] = "yes"
if OPTIONS.two_step:
script.AppendExtra("""
@@ -1123,12 +1216,6 @@
value += ' ' * (expected_length - len(value))
return value
- # The place where the output from the subprocess should go.
- log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
-
- # Get the PayloadSigner to be used in step 3.
- payload_signer = PayloadSigner()
-
# Stage the output zip package for package signing.
temp_zip_file = tempfile.NamedTemporaryFile()
output_zip = zipfile.ZipFile(temp_zip_file, "w",
@@ -1144,73 +1231,15 @@
# Metadata to comply with Android OTA package format.
metadata = GetPackageMetadata(target_info, source_info)
- # 1. Generate payload.
- payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
- cmd = ["brillo_update_payload", "generate",
- "--payload", payload_file,
- "--target_image", target_file]
- if source_file is not None:
- cmd.extend(["--source_image", source_file])
- p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
- p1.communicate()
- assert p1.returncode == 0, "brillo_update_payload generate failed"
+ # Generate payload.
+ payload = Payload()
+ payload.Generate(target_file, source_file)
- # 2. Generate hashes of the payload and metadata files.
- 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_file,
- "--signature_size", "256",
- "--metadata_hash_file", metadata_sig_file,
- "--payload_hash_file", payload_sig_file]
- p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
- p1.communicate()
- assert p1.returncode == 0, "brillo_update_payload hash failed"
+ # Sign the payload.
+ payload.Sign(PayloadSigner())
- # 3. Sign the hashes and insert them back into the payload file.
- # 3a. Sign the payload hash.
- signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
-
- # 3b. Sign the metadata hash.
- signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
-
- # 3c. Insert the signatures back into the payload file.
- signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
- suffix=".bin")
- cmd = ["brillo_update_payload", "sign",
- "--unsigned_payload", payload_file,
- "--payload", signed_payload_file,
- "--signature_size", "256",
- "--metadata_signature_file", signed_metadata_sig_file,
- "--payload_signature_file", signed_payload_sig_file]
- p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
- p1.communicate()
- assert p1.returncode == 0, "brillo_update_payload sign failed"
-
- # 4. Dump the signed payload properties.
- properties_file = common.MakeTempFile(prefix="payload-properties-",
- suffix=".txt")
- cmd = ["brillo_update_payload", "properties",
- "--payload", signed_payload_file,
- "--properties_file", properties_file]
- p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
- p1.communicate()
- assert p1.returncode == 0, "brillo_update_payload properties failed"
-
- if OPTIONS.wipe_user_data:
- with open(properties_file, "a") as f:
- f.write("POWERWASH=1\n")
- metadata["ota-wipe"] = "yes"
-
- # Add the signed payload file and properties into the zip. In order to
- # support streaming, we pack payload.bin, payload_properties.txt and
- # care_map.txt as ZIP_STORED. So these entries can be read directly with
- # the offset and length pairs.
- common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
- compress_type=zipfile.ZIP_STORED)
- common.ZipWrite(output_zip, properties_file,
- arcname="payload_properties.txt",
- compress_type=zipfile.ZIP_STORED)
+ # Write the payload into output zip.
+ payload.WriteToZip(output_zip)
# If dm-verity is supported for the device, copy contents of care_map
# into A/B OTA package.
@@ -1221,6 +1250,8 @@
namelist = target_zip.namelist()
if care_map_path in namelist:
care_map_data = target_zip.read(care_map_path)
+ # In order to support streaming, care_map.txt needs to be packed as
+ # ZIP_STORED.
common.ZipWriteStr(output_zip, "care_map.txt", care_map_data,
compress_type=zipfile.ZIP_STORED)
else:
diff --git a/tools/releasetools/rangelib.py b/tools/releasetools/rangelib.py
index 87380a5..8af61c3 100644
--- a/tools/releasetools/rangelib.py
+++ b/tools/releasetools/rangelib.py
@@ -25,6 +25,7 @@
def __init__(self, data=None):
self.monotonic = False
+ self._extra = {}
if isinstance(data, str):
self._parse_internal(data)
elif data:
@@ -56,6 +57,10 @@
def __repr__(self):
return '<RangeSet("' + self.to_string() + '")>'
+ @property
+ def extra(self):
+ return self._extra
+
@classmethod
def parse(cls, text):
"""Parse a text string consisting of a space-separated list of
diff --git a/tools/releasetools/sign_target_files_apks.py b/tools/releasetools/sign_target_files_apks.py
index 7a1126c..1f9a3ca 100755
--- a/tools/releasetools/sign_target_files_apks.py
+++ b/tools/releasetools/sign_target_files_apks.py
@@ -538,10 +538,7 @@
" as payload verification key.\n\n")
print("Using %s for payload verification." % (mapped_keys[0],))
- cmd = common.Run(
- ["openssl", "x509", "-pubkey", "-noout", "-in", mapped_keys[0]],
- stdout=subprocess.PIPE)
- pubkey, _ = cmd.communicate()
+ pubkey = common.ExtractPublicKey(mapped_keys[0])
common.ZipWriteStr(
output_tf_zip,
"SYSTEM/etc/update_engine/update-payload-key.pub.pem",
diff --git a/tools/releasetools/test_common.py b/tools/releasetools/test_common.py
index 8fb4600..6da286c 100644
--- a/tools/releasetools/test_common.py
+++ b/tools/releasetools/test_common.py
@@ -21,8 +21,10 @@
from hashlib import sha1
import common
+import test_utils
import validate_target_files
+
KiB = 1024
MiB = 1024 * KiB
GiB = 1024 * MiB
@@ -474,6 +476,18 @@
with zipfile.ZipFile(target_files, 'r') as input_zip:
self.assertRaises(ValueError, common.ReadApkCerts, input_zip)
+ def test_ExtractPublicKey(self):
+ testdata_dir = test_utils.get_testdata_dir()
+ cert = os.path.join(testdata_dir, 'testkey.x509.pem')
+ pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
+ with open(pubkey, 'rb') as pubkey_fp:
+ self.assertEqual(pubkey_fp.read(), common.ExtractPublicKey(cert))
+
+ def test_ExtractPublicKey_invalidInput(self):
+ testdata_dir = test_utils.get_testdata_dir()
+ wrong_input = os.path.join(testdata_dir, 'testkey.pk8')
+ self.assertRaises(AssertionError, common.ExtractPublicKey, wrong_input)
+
class InstallRecoveryScriptFormatTest(unittest.TestCase):
"""Checks the format of install-recovery.sh.
diff --git a/tools/releasetools/test_ota_from_target_files.py b/tools/releasetools/test_ota_from_target_files.py
index fa6655b..849ca1d 100644
--- a/tools/releasetools/test_ota_from_target_files.py
+++ b/tools/releasetools/test_ota_from_target_files.py
@@ -15,22 +15,18 @@
#
import copy
+import os
import os.path
import unittest
+import zipfile
import common
+import test_utils
from ota_from_target_files import (
- _LoadOemDicts, BuildInfo, GetPackageMetadata, PayloadSigner,
+ _LoadOemDicts, BuildInfo, GetPackageMetadata, Payload, PayloadSigner,
WriteFingerprintAssertion)
-def get_testdata_dir():
- """Returns the testdata dir, in relative to the script dir."""
- # The script dir is the one we want, which could be different from pwd.
- current_dir = os.path.dirname(os.path.realpath(__file__))
- return os.path.join(current_dir, 'testdata')
-
-
class MockScriptWriter(object):
"""A class that mocks edify_generator.EdifyGenerator.
@@ -318,6 +314,8 @@
'ro.product.device' : 'product-device',
'ro.build.fingerprint' : 'build-fingerprint-target',
'ro.build.version.incremental' : 'build-version-incremental-target',
+ 'ro.build.version.sdk' : '27',
+ 'ro.build.version.security_patch' : '2017-12-01',
'ro.build.date.utc' : '1500000000',
},
}
@@ -327,6 +325,8 @@
'ro.product.device' : 'product-device',
'ro.build.fingerprint' : 'build-fingerprint-source',
'ro.build.version.incremental' : 'build-version-incremental-source',
+ 'ro.build.version.sdk' : '25',
+ 'ro.build.version.security_patch' : '2016-12-01',
'ro.build.date.utc' : '1400000000',
},
}
@@ -349,6 +349,8 @@
'ota-required-cache' : '0',
'post-build' : 'build-fingerprint-target',
'post-build-incremental' : 'build-version-incremental-target',
+ 'post-sdk-level' : '27',
+ 'post-security-patch-level' : '2017-12-01',
'post-timestamp' : '1500000000',
'pre-device' : 'product-device',
},
@@ -367,6 +369,8 @@
'ota-required-cache' : '0',
'post-build' : 'build-fingerprint-target',
'post-build-incremental' : 'build-version-incremental-target',
+ 'post-sdk-level' : '27',
+ 'post-security-patch-level' : '2017-12-01',
'post-timestamp' : '1500000000',
'pre-device' : 'product-device',
'pre-build' : 'build-fingerprint-source',
@@ -382,6 +386,8 @@
'ota-type' : 'BLOCK',
'post-build' : 'build-fingerprint-target',
'post-build-incremental' : 'build-version-incremental-target',
+ 'post-sdk-level' : '27',
+ 'post-security-patch-level' : '2017-12-01',
'post-timestamp' : '1500000000',
'pre-device' : 'product-device',
},
@@ -397,6 +403,8 @@
'ota-type' : 'BLOCK',
'post-build' : 'build-fingerprint-target',
'post-build-incremental' : 'build-version-incremental-target',
+ 'post-sdk-level' : '27',
+ 'post-security-patch-level' : '2017-12-01',
'post-timestamp' : '1500000000',
'pre-device' : 'product-device',
'pre-build' : 'build-fingerprint-source',
@@ -414,6 +422,8 @@
'ota-wipe' : 'yes',
'post-build' : 'build-fingerprint-target',
'post-build-incremental' : 'build-version-incremental-target',
+ 'post-sdk-level' : '27',
+ 'post-security-patch-level' : '2017-12-01',
'post-timestamp' : '1500000000',
'pre-device' : 'product-device',
},
@@ -457,6 +467,8 @@
'ota-wipe' : 'yes',
'post-build' : 'build-fingerprint-target',
'post-build-incremental' : 'build-version-incremental-target',
+ 'post-sdk-level' : '27',
+ 'post-security-patch-level' : '2017-12-01',
'pre-device' : 'product-device',
'pre-build' : 'build-fingerprint-source',
'pre-build-incremental' : 'build-version-incremental-source',
@@ -479,6 +491,8 @@
'ota-type' : 'BLOCK',
'post-build' : 'build-fingerprint-target',
'post-build-incremental' : 'build-version-incremental-target',
+ 'post-sdk-level' : '27',
+ 'post-security-patch-level' : '2017-12-01',
'post-timestamp' : '1500000001',
'pre-device' : 'product-device',
'pre-build' : 'build-fingerprint-source',
@@ -493,7 +507,7 @@
SIGNED_SIGFILE = 'signed-sigfile.bin'
def setUp(self):
- self.testdata_dir = get_testdata_dir()
+ self.testdata_dir = test_utils.get_testdata_dir()
self.assertTrue(os.path.exists(self.testdata_dir))
common.OPTIONS.payload_signer = None
@@ -564,3 +578,157 @@
verify_file = os.path.join(self.testdata_dir, self.SIGNED_SIGFILE)
self._assertFilesEqual(verify_file, signed_file)
+
+
+class PayloadTest(unittest.TestCase):
+
+ 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 tearDown(self):
+ common.Cleanup()
+
+ @staticmethod
+ def _construct_target_files():
+ target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
+ with zipfile.ZipFile(target_files, 'w') as target_files_zip:
+ # META/update_engine_config.txt
+ target_files_zip.writestr(
+ 'META/update_engine_config.txt',
+ "PAYLOAD_MAJOR_VERSION=2\nPAYLOAD_MINOR_VERSION=4\n")
+
+ # META/ab_partitions.txt
+ ab_partitions = ['boot', 'system', 'vendor']
+ target_files_zip.writestr(
+ 'META/ab_partitions.txt',
+ '\n'.join(ab_partitions))
+
+ # Create dummy images for each of them.
+ for partition in ab_partitions:
+ target_files_zip.writestr('IMAGES/' + partition + '.img',
+ os.urandom(len(partition)))
+
+ return target_files
+
+ def _create_payload_full(self):
+ target_file = self._construct_target_files()
+ payload = Payload()
+ payload.Generate(target_file)
+ return payload
+
+ def _create_payload_incremental(self):
+ target_file = self._construct_target_files()
+ source_file = self._construct_target_files()
+ payload = Payload()
+ payload.Generate(target_file, source_file)
+ return payload
+
+ def test_Generate_full(self):
+ payload = self._create_payload_full()
+ self.assertTrue(os.path.exists(payload.payload_file))
+
+ def test_Generate_incremental(self):
+ payload = self._create_payload_incremental()
+ self.assertTrue(os.path.exists(payload.payload_file))
+
+ def test_Generate_additionalArgs(self):
+ target_file = self._construct_target_files()
+ source_file = self._construct_target_files()
+ payload = Payload()
+ # This should work the same as calling payload.Generate(target_file,
+ # source_file).
+ payload.Generate(
+ target_file, additional_args=["--source_image", source_file])
+ self.assertTrue(os.path.exists(payload.payload_file))
+
+ def test_Generate_invalidInput(self):
+ target_file = self._construct_target_files()
+ common.ZipDelete(target_file, 'IMAGES/vendor.img')
+ payload = Payload()
+ self.assertRaises(AssertionError, payload.Generate, target_file)
+
+ def test_Sign_full(self):
+ payload = self._create_payload_full()
+ payload.Sign(PayloadSigner())
+
+ output_file = common.MakeTempFile(suffix='.zip')
+ with zipfile.ZipFile(output_file, 'w') as output_zip:
+ payload.WriteToZip(output_zip)
+
+ import check_ota_package_signature
+ check_ota_package_signature.VerifyAbOtaPayload(
+ os.path.join(self.testdata_dir, 'testkey.x509.pem'),
+ output_file)
+
+ def test_Sign_incremental(self):
+ payload = self._create_payload_incremental()
+ payload.Sign(PayloadSigner())
+
+ output_file = common.MakeTempFile(suffix='.zip')
+ with zipfile.ZipFile(output_file, 'w') as output_zip:
+ payload.WriteToZip(output_zip)
+
+ import check_ota_package_signature
+ check_ota_package_signature.VerifyAbOtaPayload(
+ os.path.join(self.testdata_dir, 'testkey.x509.pem'),
+ output_file)
+
+ def test_Sign_withDataWipe(self):
+ common.OPTIONS.wipe_user_data = True
+ payload = self._create_payload_full()
+ payload.Sign(PayloadSigner())
+
+ with open(payload.payload_properties) as properties_fp:
+ self.assertIn("POWERWASH=1", properties_fp.read())
+
+ def test_Sign_badSigner(self):
+ """Tests that signing failure can be captured."""
+ payload = self._create_payload_full()
+ payload_signer = PayloadSigner()
+ payload_signer.signer_args.append('bad-option')
+ self.assertRaises(AssertionError, payload.Sign, payload_signer)
+
+ def test_WriteToZip(self):
+ payload = self._create_payload_full()
+ payload.Sign(PayloadSigner())
+
+ output_file = common.MakeTempFile(suffix='.zip')
+ with zipfile.ZipFile(output_file, 'w') as output_zip:
+ payload.WriteToZip(output_zip)
+
+ with zipfile.ZipFile(output_file) as verify_zip:
+ # First make sure we have the essential entries.
+ namelist = verify_zip.namelist()
+ self.assertIn(Payload.PAYLOAD_BIN, namelist)
+ self.assertIn(Payload.PAYLOAD_PROPERTIES_TXT, namelist)
+
+ # Then assert these entries are stored.
+ for entry_info in verify_zip.infolist():
+ if entry_info.filename not in (Payload.PAYLOAD_BIN,
+ Payload.PAYLOAD_PROPERTIES_TXT):
+ continue
+ self.assertEqual(zipfile.ZIP_STORED, entry_info.compress_type)
+
+ def test_WriteToZip_unsignedPayload(self):
+ """Unsigned payloads should not be allowed to be written to zip."""
+ payload = self._create_payload_full()
+
+ output_file = common.MakeTempFile(suffix='.zip')
+ with zipfile.ZipFile(output_file, 'w') as output_zip:
+ self.assertRaises(AssertionError, payload.WriteToZip, output_zip)
+
+ # Also test with incremental payload.
+ payload = self._create_payload_incremental()
+
+ output_file = common.MakeTempFile(suffix='.zip')
+ with zipfile.ZipFile(output_file, 'w') as output_zip:
+ self.assertRaises(AssertionError, payload.WriteToZip, output_zip)
diff --git a/tools/releasetools/test_utils.py b/tools/releasetools/test_utils.py
new file mode 100644
index 0000000..ec53731
--- /dev/null
+++ b/tools/releasetools/test_utils.py
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""
+Utils for running unittests.
+"""
+
+import os.path
+
+
+def get_testdata_dir():
+ """Returns the testdata dir, in relative to the script dir."""
+ # The script dir is the one we want, which could be different from pwd.
+ current_dir = os.path.dirname(os.path.realpath(__file__))
+ return os.path.join(current_dir, 'testdata')
diff --git a/tools/releasetools/testdata/testkey.pubkey.pem b/tools/releasetools/testdata/testkey.pubkey.pem
new file mode 100644
index 0000000..418ae60
--- /dev/null
+++ b/tools/releasetools/testdata/testkey.pubkey.pem
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvjvyO2LwWgmQNyq7z+xK
+04eg0t3AL4y2NhpAAOzVnFyCArFcFjLTGQDDvkbZP6N12O6+dwJoPLntnm9A+VnP
+IFFRHg0HUWSbHM+Qk8Jgv2/2AVkAUj5J1r9t4X+2WI0eRzJP15Zjn68pQKGmcyci
+ry0gbvmYvXL2ZUmTm56DmEfCUCRIY2IGJ/CcMnFeItVU0LxKsV5Mlt5BO0Vv/CV4
+EaiOLwyCnoZuUhYto7dHlO/47v/H9zhkJC54OA1dkD38EPgO5GnfhGFSNXQRmJDT
+XrFgd6O+QO4yUNX8lYP10MzimUpItZa05t68NADqwYl3T7nWzvuC9r4IqZDyPf21
+TQIDAQAB
+-----END PUBLIC KEY-----
diff --git a/tools/releasetools/validate_target_files.py b/tools/releasetools/validate_target_files.py
index 4b34820..1b3eb73 100755
--- a/tools/releasetools/validate_target_files.py
+++ b/tools/releasetools/validate_target_files.py
@@ -23,40 +23,23 @@
same check also applies to the vendor image if present.
"""
-import common
import logging
import os.path
import re
-import sparse_img
import sys
-
-def _GetImage(which, tmpdir):
- assert which in ('system', 'vendor')
-
- path = os.path.join(tmpdir, 'IMAGES', which + '.img')
- mappath = os.path.join(tmpdir, 'IMAGES', which + '.map')
-
- # Map file must exist (allowed to be empty).
- assert os.path.exists(path) and os.path.exists(mappath)
-
- clobbered_blocks = '0'
- return sparse_img.SparseImage(path, mappath, clobbered_blocks)
+import common
def _ReadFile(file_name, unpacked_name, round_up=False):
"""Constructs and returns a File object. Rounds up its size if needed."""
- def RoundUpTo4K(value):
- rounded_up = value + 4095
- return rounded_up - (rounded_up % 4096)
-
assert os.path.exists(unpacked_name)
with open(unpacked_name, 'r') as f:
file_data = f.read()
file_size = len(file_data)
if round_up:
- file_size_rounded_up = RoundUpTo4K(file_size)
+ file_size_rounded_up = common.RoundUpTo4K(file_size)
file_data += '\0' * (file_size_rounded_up - file_size)
return common.File(file_name, file_data)
@@ -64,13 +47,13 @@
def ValidateFileAgainstSha1(input_tmp, file_name, file_path, expected_sha1):
"""Check if the file has the expected SHA-1."""
- logging.info('Validating the SHA-1 of {}'.format(file_name))
+ logging.info('Validating the SHA-1 of %s', file_name)
unpacked_name = os.path.join(input_tmp, file_path)
assert os.path.exists(unpacked_name)
actual_sha1 = _ReadFile(file_name, unpacked_name, False).sha1
assert actual_sha1 == expected_sha1, \
'SHA-1 mismatches for {}. actual {}, expected {}'.format(
- file_name, actual_sha1, expected_sha1)
+ file_name, actual_sha1, expected_sha1)
def ValidateFileConsistency(input_zip, input_tmp):
@@ -78,33 +61,28 @@
def CheckAllFiles(which):
logging.info('Checking %s image.', which)
- image = _GetImage(which, input_tmp)
+ image = common.GetSparseImage(which, input_tmp, input_zip)
prefix = '/' + which
for entry in image.file_map:
+ # Skip entries like '__NONZERO-0'.
if not entry.startswith(prefix):
continue
# Read the blocks that the file resides. Note that it will contain the
# bytes past the file length, which is expected to be padded with '\0's.
ranges = image.file_map[entry]
+
+ incomplete = ranges.extra.get('incomplete', False)
+ if incomplete:
+ logging.warning('Skipping %s that has incomplete block list', entry)
+ continue
+
blocks_sha1 = image.RangeSha1(ranges)
# The filename under unpacked directory, such as SYSTEM/bin/sh.
unpacked_name = os.path.join(
input_tmp, which.upper(), entry[(len(prefix) + 1):])
unpacked_file = _ReadFile(entry, unpacked_name, True)
- file_size = unpacked_file.size
-
- # block.map may contain less blocks, because mke2fs may skip allocating
- # blocks if they contain all zeros. We can't reconstruct such a file from
- # its block list. (Bug: 65213616)
- if file_size > ranges.size() * 4096:
- logging.warning(
- 'Skipping %s that has less blocks: file size %d-byte,'
- ' ranges %s (%d-byte)', entry, file_size, ranges,
- ranges.size() * 4096)
- continue
-
file_sha1 = unpacked_file.sha1
assert blocks_sha1 == file_sha1, \
'file: %s, range: %s, blocks_sha1: %s, file_sha1: %s' % (
@@ -147,10 +125,10 @@
script_path = 'SYSTEM/bin/install-recovery.sh'
if not os.path.exists(os.path.join(input_tmp, script_path)):
- logging.info('{} does not exist in input_tmp'.format(script_path))
+ logging.info('%s does not exist in input_tmp', script_path)
return
- logging.info('Checking {}'.format(script_path))
+ logging.info('Checking %s', script_path)
with open(os.path.join(input_tmp, script_path), 'r') as script:
lines = script.read().strip().split('\n')
assert len(lines) >= 6
@@ -168,7 +146,7 @@
expected_recovery_sha1 = applypatch_argv[3].strip()
assert expected_recovery_check_sha1 == expected_recovery_sha1
ValidateFileAgainstSha1(input_tmp, 'recovery.img',
- 'SYSTEM/etc/recovery.img', expected_recovery_sha1)
+ 'SYSTEM/etc/recovery.img', expected_recovery_sha1)
else:
# We're patching boot.img to get recovery.img where bonus_args is optional
if applypatch_argv[1] == "-b":
@@ -182,16 +160,17 @@
boot_info = applypatch_argv[boot_info_index].strip().split(':')
assert len(boot_info) == 4
ValidateFileAgainstSha1(input_tmp, file_name='boot.img',
- file_path='IMAGES/boot.img', expected_sha1=boot_info[3])
+ file_path='IMAGES/boot.img',
+ expected_sha1=boot_info[3])
recovery_sha1_index = boot_info_index + 2
expected_recovery_sha1 = applypatch_argv[recovery_sha1_index]
assert expected_recovery_check_sha1 == expected_recovery_sha1
ValidateFileAgainstSha1(input_tmp, file_name='recovery.img',
- file_path='IMAGES/recovery.img',
- expected_sha1=expected_recovery_sha1)
+ file_path='IMAGES/recovery.img',
+ expected_sha1=expected_recovery_sha1)
- logging.info('Done checking {}'.format(script_path))
+ logging.info('Done checking %s', script_path)
def main(argv):