From 65afc07f9dd1220ff45e7bfd7ca09a1647928e33 Mon Sep 17 00:00:00 2001 From: Yifan Hong Date: Fri, 17 Apr 2020 10:08:10 -0700 Subject: [PATCH] Support forcefully generating non-AB packages. Force generate a non-A/B update package when requested. Bug: 154344887 Test: ota_from_target_files.py --force_non_ab ... Test: apply it as well Change-Id: I5e81eb161722e07ef50081b6a16685cbc9963ae2 (cherry picked from commit 7169f754cc4b806c54a049b838355483575c1785) Merged-In: I5e81eb161722e07ef50081b6a16685cbc9963ae2 --- core/Makefile | 20 ++++++++++------ core/board_config.mk | 24 ++++++++++++++++--- core/product.mk | 7 ++++++ target/product/virtual_ab_ota_plus_non_ab.mk | 21 ++++++++++++++++ tools/releasetools/blockimgdiff.py | 1 + tools/releasetools/common.py | 12 +++++++--- tools/releasetools/ota_from_target_files.py | 25 ++++++++++++++++---- 7 files changed, 93 insertions(+), 17 deletions(-) create mode 100644 target/product/virtual_ab_ota_plus_non_ab.mk diff --git a/core/Makefile b/core/Makefile index 7feec370a..1d9b99c32 100644 --- a/core/Makefile +++ b/core/Makefile @@ -3365,10 +3365,10 @@ endif # When building a standalone recovery image for non-A/B devices, recovery image must be self-signed # to be verified independently, and cannot be chained into vbmeta.img. See the link below for # details. -ifneq ($(AB_OTA_UPDATER),true) +ifeq ($(TARGET_OTA_ALLOW_NON_AB),true) ifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),) $(if $(BOARD_AVB_RECOVERY_KEY_PATH),,\ - $(error BOARD_AVB_RECOVERY_KEY_PATH must be defined for non-A/B devices. \ + $(error BOARD_AVB_RECOVERY_KEY_PATH must be defined for if non-A/B is supported. \ See https://android.googlesource.com/platform/external/avb/+/master/README.md#booting-into-recovery)) endif endif @@ -3459,7 +3459,7 @@ $(eval $(_signing_args) := \ # The recovery partition in non-A/B devices should be verified separately. Skip adding the chain # partition descriptor for recovery partition into vbmeta.img. -$(if $(or $(filter true,$(AB_OTA_UPDATER)),$(filter-out recovery,$(part))),\ +$(if $(or $(filter-out true,$(TARGET_OTA_ALLOW_NON_AB)),$(filter-out recovery,$(part))),\ $(eval INTERNAL_AVB_MAKE_VBMETA_IMAGE_ARGS += \ --chain_partition $(part):$($(_rollback_index_location)):$(AVB_CHAIN_KEY_DIR)/$(part).avbpubkey)) @@ -4254,6 +4254,9 @@ ifeq ($(AB_OTA_UPDATER),true) $(hide) echo "build_type=$(TARGET_BUILD_VARIANT)" >> $@ $(hide) echo "ab_update=true" >> $@ endif +ifeq ($(TARGET_OTA_ALLOW_NON_AB),true) + $(hide) echo "allow_non_ab=true" >> $@ +endif ifdef BOARD_PREBUILT_DTBOIMAGE $(hide) echo "has_dtbo=true" >> $@ ifeq ($(BOARD_AVB_ENABLE),true) @@ -4331,10 +4334,13 @@ $(BUILT_TARGET_FILES_PACKAGE): PRIVATE_TOOL_EXTENSION := $(tool_extension) ifeq ($(AB_OTA_UPDATER),true) updater_dep := system/update_engine/update_engine.conf -else -# Build OTA tools if not using the AB Updater. +endif + +# Build OTA tools if non-A/B is allowed +ifeq ($(TARGET_OTA_ALLOW_NON_AB),true) updater_dep := $(built_ota_tools) endif + $(BUILT_TARGET_FILES_PACKAGE): $(updater_dep) # If we are using recovery as boot, output recovery files to BOOT/. @@ -4610,7 +4616,7 @@ endif @# Extra contents of the OTA package $(hide) mkdir -p $(zip_root)/OTA $(hide) cp $(INSTALLED_ANDROID_INFO_TXT_TARGET) $(zip_root)/OTA/ -ifneq ($(AB_OTA_UPDATER),true) +ifeq ($(TARGET_OTA_ALLOW_NON_AB),true) ifneq ($(built_ota_tools),) $(hide) mkdir -p $(zip_root)/OTA/bin $(hide) cp $(PRIVATE_OTA_TOOLS) $(zip_root)/OTA/bin/ @@ -4647,7 +4653,7 @@ ifneq ($(PRODUCT_ODM_BASE_FS_PATH),) $(hide) cp $(PRODUCT_ODM_BASE_FS_PATH) \ $(zip_root)/META/$(notdir $(PRODUCT_ODM_BASE_FS_PATH)) endif -ifneq ($(AB_OTA_UPDATER),true) +ifeq ($(TARGET_OTA_ALLOW_NON_AB),true) ifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),) $(hide) PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH MKBOOTIMG=$(MKBOOTIMG) \ $(MAKE_RECOVERY_PATCH) $(zip_root) $(zip_root) diff --git a/core/board_config.mk b/core/board_config.mk index 2279c3f10..0ff28c5fd 100644 --- a/core/board_config.mk +++ b/core/board_config.mk @@ -550,13 +550,31 @@ endif .KATI_READONLY := BUILDING_ODM_IMAGE ########################################### -# Ensure that only TARGET_RECOVERY_UPDATER_LIBS *or* AB_OTA_UPDATER is set. +# Ensure consistency among TARGET_RECOVERY_UPDATER_LIBS, AB_OTA_UPDATER, and PRODUCT_OTA_FORCE_NON_AB_PACKAGE. TARGET_RECOVERY_UPDATER_LIBS ?= AB_OTA_UPDATER ?= .KATI_READONLY := TARGET_RECOVERY_UPDATER_LIBS AB_OTA_UPDATER -ifeq ($(AB_OTA_UPDATER),true) + +# Ensure that if PRODUCT_OTA_FORCE_NON_AB_PACKAGE == true, then AB_OTA_UPDATER must be true +ifeq ($(PRODUCT_OTA_FORCE_NON_AB_PACKAGE),true) + ifneq ($(AB_OTA_UPDATER),true) + $(error AB_OTA_UPDATER must be set to true when PRODUCT_OTA_FORCE_NON_AB_PACKAGE is true) + endif +endif + +# In some configurations, A/B and non-A/B may coexist. Check TARGET_OTA_ALLOW_NON_AB +# to see if non-A/B is supported. +TARGET_OTA_ALLOW_NON_AB := false +ifneq ($(AB_OTA_UPDATER),true) + TARGET_OTA_ALLOW_NON_AB := true +else ifeq ($(PRODUCT_OTA_FORCE_NON_AB_PACKAGE),true) + TARGET_OTA_ALLOW_NON_AB := true +endif +.KATI_READONLY := TARGET_OTA_ALLOW_NON_AB + +ifneq ($(TARGET_OTA_ALLOW_NON_AB),true) ifneq ($(strip $(TARGET_RECOVERY_UPDATER_LIBS)),) - $(error Do not use TARGET_RECOVERY_UPDATER_LIBS when using AB_OTA_UPDATER) + $(error Do not use TARGET_RECOVERY_UPDATER_LIBS when using TARGET_OTA_ALLOW_NON_AB) endif endif diff --git a/core/product.mk b/core/product.mk index ea9be72a8..0aa07ef5b 100644 --- a/core/product.mk +++ b/core/product.mk @@ -393,6 +393,13 @@ _product_single_value_vars += PRODUCT_VIRTUAL_AB_OTA # If set, device retrofits virtual A/B. _product_single_value_vars += PRODUCT_VIRTUAL_AB_OTA_RETROFIT +# If set, forcefully generate a non-A/B update package. +# Note: A device configuration should inherit from virtual_ab_ota_plus_non_ab.mk +# instead of setting this variable directly. +# Note: Use TARGET_OTA_ALLOW_NON_AB in the build system because +# TARGET_OTA_ALLOW_NON_AB takes the value of AB_OTA_UPDATER into account. +_product_single_value_vars += PRODUCT_OTA_FORCE_NON_AB_PACKAGE + # If set, Java module in product partition cannot use hidden APIs. _product_single_value_vars += PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE diff --git a/target/product/virtual_ab_ota_plus_non_ab.mk b/target/product/virtual_ab_ota_plus_non_ab.mk new file mode 100644 index 000000000..325d75e3e --- /dev/null +++ b/target/product/virtual_ab_ota_plus_non_ab.mk @@ -0,0 +1,21 @@ +# +# Copyright (C) 2020 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. +# + +$(call inherit-product, $(SRC_TARGET_DIR)/product/virtual_ab_ota.mk) + +PRODUCT_OTA_FORCE_NON_AB_PACKAGE := true + +PRODUCT_PROPERTY_OVERRIDES += ro.virtual_ab.allow_non_ab=true diff --git a/tools/releasetools/blockimgdiff.py b/tools/releasetools/blockimgdiff.py index 72f065d19..8b6a6900d 100644 --- a/tools/releasetools/blockimgdiff.py +++ b/tools/releasetools/blockimgdiff.py @@ -1558,6 +1558,7 @@ class BlockImageDiff(object): split_large_apks = [] cache_size = common.OPTIONS.cache_size split_threshold = 0.125 + assert cache_size is not None max_blocks_per_transfer = int(cache_size * split_threshold / self.tgt.blocksize) empty = RangeSet() diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py index 2b043344a..9f48f78b7 100644 --- a/tools/releasetools/common.py +++ b/tools/releasetools/common.py @@ -848,12 +848,13 @@ class PartitionBuildProps(object): def LoadRecoveryFSTab(read_helper, fstab_version, recovery_fstab_path, system_root_image=False): class Partition(object): - def __init__(self, mount_point, fs_type, device, length, context): + def __init__(self, mount_point, fs_type, device, length, context, slotselect): self.mount_point = mount_point self.fs_type = fs_type self.device = device self.length = length self.context = context + self.slotselect = slotselect try: data = read_helper(recovery_fstab_path) @@ -881,10 +882,13 @@ def LoadRecoveryFSTab(read_helper, fstab_version, recovery_fstab_path, # It's a good line, parse it. length = 0 + slotselect = False options = options.split(",") for i in options: if i.startswith("length="): length = int(i[7:]) + elif i == "slotselect": + slotselect = True else: # Ignore all unknown options in the unified fstab. continue @@ -898,7 +902,8 @@ def LoadRecoveryFSTab(read_helper, fstab_version, recovery_fstab_path, mount_point = pieces[1] d[mount_point] = Partition(mount_point=mount_point, fs_type=pieces[2], - device=pieces[0], length=length, context=context) + device=pieces[0], length=length, context=context, + slotselect=slotselect) # / is used for the system mount point when the root directory is included in # system. Other areas assume system is always at "/system" so point /system @@ -913,7 +918,8 @@ def _FindAndLoadRecoveryFstab(info_dict, input_file, read_helper): """Finds the path to recovery fstab and loads its contents.""" # recovery fstab is only meaningful when installing an update via recovery # (i.e. non-A/B OTA). Skip loading fstab if device used A/B OTA. - if info_dict.get('ab_update') == 'true': + if info_dict.get('ab_update') == 'true' and \ + info_dict.get("allow_non_ab") != "true": return None # We changed recovery.fstab path in Q, from ../RAMDISK/etc/recovery.fstab to diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py index ad001d13c..16d7485e0 100755 --- a/tools/releasetools/ota_from_target_files.py +++ b/tools/releasetools/ota_from_target_files.py @@ -78,6 +78,13 @@ Common options that apply to both of non-A/B and A/B OTAs Write a copy of the metadata to a separate file. Therefore, users can read the post build fingerprint without extracting the OTA package. + --force_non_ab + This flag can only be set on an A/B device that also supports non-A/B + updates. Implies --two_step. + If set, generate that non-A/B update package. + If not set, generates A/B package for A/B device and non-A/B package for + non-A/B device. + Non-A/B OTA specific options -b (--binary) @@ -251,6 +258,7 @@ OPTIONS.skip_compatibility_check = False OPTIONS.output_metadata_path = None OPTIONS.disable_fec_computation = False OPTIONS.boot_variable_values = None +OPTIONS.force_non_ab = False METADATA_NAME = 'META-INF/com/android/metadata' @@ -933,7 +941,7 @@ def GetPackageMetadata(target_info, source_info=None): 'ro.build.version.security_patch'), } - if target_info.is_ab: + if target_info.is_ab and not OPTIONS.force_non_ab: metadata['ota-type'] = 'AB' metadata['ota-required-cache'] = '0' else: @@ -2067,6 +2075,8 @@ def main(argv): OPTIONS.output_metadata_path = a elif o == "--disable_fec_computation": OPTIONS.disable_fec_computation = True + elif o == "--force_non_ab": + OPTIONS.force_non_ab = True else: return False return True @@ -2103,6 +2113,7 @@ def main(argv): "skip_compatibility_check", "output_metadata_path=", "disable_fec_computation", + "force_non_ab", ], extra_option_handler=option_handler) if len(args) != 2: @@ -2164,11 +2175,17 @@ def main(argv): OPTIONS.skip_postinstall = True ab_update = OPTIONS.info_dict.get("ab_update") == "true" + allow_non_ab = OPTIONS.info_dict.get("allow_non_ab") == "true" + if OPTIONS.force_non_ab: + assert allow_non_ab, "--force_non_ab only allowed on devices that supports non-A/B" + assert ab_update, "--force_non_ab only allowed on A/B devices" + + generate_ab = not OPTIONS.force_non_ab and ab_update # Use the default key to sign the package if not specified with package_key. # package_keys are needed on ab_updates, so always define them if an - # ab_update is getting created. - if not OPTIONS.no_signing or ab_update: + # A/B update is getting created. + if not OPTIONS.no_signing or generate_ab: if OPTIONS.package_key is None: OPTIONS.package_key = OPTIONS.info_dict.get( "default_system_dev_certificate", @@ -2176,7 +2193,7 @@ def main(argv): # Get signing keys OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key]) - if ab_update: + if generate_ab: GenerateAbOtaPackage( target_file=args[0], output_file=args[1],