Merge changes from topic "nonab_pkg"
* changes: Use add_slot_suffix function in edify script Support forcefully generating non-AB packages.
This commit is contained in:
@@ -3365,10 +3365,10 @@ endif
|
|||||||
# When building a standalone recovery image for non-A/B devices, recovery image must be self-signed
|
# 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
|
# to be verified independently, and cannot be chained into vbmeta.img. See the link below for
|
||||||
# details.
|
# details.
|
||||||
ifneq ($(AB_OTA_UPDATER),true)
|
ifeq ($(TARGET_OTA_ALLOW_NON_AB),true)
|
||||||
ifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),)
|
ifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),)
|
||||||
$(if $(BOARD_AVB_RECOVERY_KEY_PATH),,\
|
$(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))
|
See https://android.googlesource.com/platform/external/avb/+/master/README.md#booting-into-recovery))
|
||||||
endif
|
endif
|
||||||
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
|
# The recovery partition in non-A/B devices should be verified separately. Skip adding the chain
|
||||||
# partition descriptor for recovery partition into vbmeta.img.
|
# 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 += \
|
$(eval INTERNAL_AVB_MAKE_VBMETA_IMAGE_ARGS += \
|
||||||
--chain_partition $(part):$($(_rollback_index_location)):$(AVB_CHAIN_KEY_DIR)/$(part).avbpubkey))
|
--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 "build_type=$(TARGET_BUILD_VARIANT)" >> $@
|
||||||
$(hide) echo "ab_update=true" >> $@
|
$(hide) echo "ab_update=true" >> $@
|
||||||
endif
|
endif
|
||||||
|
ifeq ($(TARGET_OTA_ALLOW_NON_AB),true)
|
||||||
|
$(hide) echo "allow_non_ab=true" >> $@
|
||||||
|
endif
|
||||||
ifdef BOARD_PREBUILT_DTBOIMAGE
|
ifdef BOARD_PREBUILT_DTBOIMAGE
|
||||||
$(hide) echo "has_dtbo=true" >> $@
|
$(hide) echo "has_dtbo=true" >> $@
|
||||||
ifeq ($(BOARD_AVB_ENABLE),true)
|
ifeq ($(BOARD_AVB_ENABLE),true)
|
||||||
@@ -4331,10 +4334,13 @@ $(BUILT_TARGET_FILES_PACKAGE): PRIVATE_TOOL_EXTENSION := $(tool_extension)
|
|||||||
|
|
||||||
ifeq ($(AB_OTA_UPDATER),true)
|
ifeq ($(AB_OTA_UPDATER),true)
|
||||||
updater_dep := system/update_engine/update_engine.conf
|
updater_dep := system/update_engine/update_engine.conf
|
||||||
else
|
endif
|
||||||
# Build OTA tools if not using the AB Updater.
|
|
||||||
|
# Build OTA tools if non-A/B is allowed
|
||||||
|
ifeq ($(TARGET_OTA_ALLOW_NON_AB),true)
|
||||||
updater_dep := $(built_ota_tools)
|
updater_dep := $(built_ota_tools)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
$(BUILT_TARGET_FILES_PACKAGE): $(updater_dep)
|
$(BUILT_TARGET_FILES_PACKAGE): $(updater_dep)
|
||||||
|
|
||||||
# If we are using recovery as boot, output recovery files to BOOT/.
|
# If we are using recovery as boot, output recovery files to BOOT/.
|
||||||
@@ -4610,7 +4616,7 @@ endif
|
|||||||
@# Extra contents of the OTA package
|
@# Extra contents of the OTA package
|
||||||
$(hide) mkdir -p $(zip_root)/OTA
|
$(hide) mkdir -p $(zip_root)/OTA
|
||||||
$(hide) cp $(INSTALLED_ANDROID_INFO_TXT_TARGET) $(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),)
|
ifneq ($(built_ota_tools),)
|
||||||
$(hide) mkdir -p $(zip_root)/OTA/bin
|
$(hide) mkdir -p $(zip_root)/OTA/bin
|
||||||
$(hide) cp $(PRIVATE_OTA_TOOLS) $(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) \
|
$(hide) cp $(PRODUCT_ODM_BASE_FS_PATH) \
|
||||||
$(zip_root)/META/$(notdir $(PRODUCT_ODM_BASE_FS_PATH))
|
$(zip_root)/META/$(notdir $(PRODUCT_ODM_BASE_FS_PATH))
|
||||||
endif
|
endif
|
||||||
ifneq ($(AB_OTA_UPDATER),true)
|
ifeq ($(TARGET_OTA_ALLOW_NON_AB),true)
|
||||||
ifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),)
|
ifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),)
|
||||||
$(hide) PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH MKBOOTIMG=$(MKBOOTIMG) \
|
$(hide) PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH MKBOOTIMG=$(MKBOOTIMG) \
|
||||||
$(MAKE_RECOVERY_PATCH) $(zip_root) $(zip_root)
|
$(MAKE_RECOVERY_PATCH) $(zip_root) $(zip_root)
|
||||||
|
@@ -550,13 +550,31 @@ endif
|
|||||||
.KATI_READONLY := BUILDING_ODM_IMAGE
|
.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 ?=
|
TARGET_RECOVERY_UPDATER_LIBS ?=
|
||||||
AB_OTA_UPDATER ?=
|
AB_OTA_UPDATER ?=
|
||||||
.KATI_READONLY := 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)),)
|
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
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@@ -393,6 +393,13 @@ _product_single_value_vars += PRODUCT_VIRTUAL_AB_OTA
|
|||||||
# If set, device retrofits virtual A/B.
|
# If set, device retrofits virtual A/B.
|
||||||
_product_single_value_vars += PRODUCT_VIRTUAL_AB_OTA_RETROFIT
|
_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.
|
# If set, Java module in product partition cannot use hidden APIs.
|
||||||
_product_single_value_vars += PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE
|
_product_single_value_vars += PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE
|
||||||
|
|
||||||
|
21
target/product/virtual_ab_ota_plus_non_ab.mk
Normal file
21
target/product/virtual_ab_ota_plus_non_ab.mk
Normal file
@@ -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
|
@@ -1558,6 +1558,7 @@ class BlockImageDiff(object):
|
|||||||
split_large_apks = []
|
split_large_apks = []
|
||||||
cache_size = common.OPTIONS.cache_size
|
cache_size = common.OPTIONS.cache_size
|
||||||
split_threshold = 0.125
|
split_threshold = 0.125
|
||||||
|
assert cache_size is not None
|
||||||
max_blocks_per_transfer = int(cache_size * split_threshold /
|
max_blocks_per_transfer = int(cache_size * split_threshold /
|
||||||
self.tgt.blocksize)
|
self.tgt.blocksize)
|
||||||
empty = RangeSet()
|
empty = RangeSet()
|
||||||
|
@@ -848,12 +848,13 @@ class PartitionBuildProps(object):
|
|||||||
def LoadRecoveryFSTab(read_helper, fstab_version, recovery_fstab_path,
|
def LoadRecoveryFSTab(read_helper, fstab_version, recovery_fstab_path,
|
||||||
system_root_image=False):
|
system_root_image=False):
|
||||||
class Partition(object):
|
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.mount_point = mount_point
|
||||||
self.fs_type = fs_type
|
self.fs_type = fs_type
|
||||||
self.device = device
|
self.device = device
|
||||||
self.length = length
|
self.length = length
|
||||||
self.context = context
|
self.context = context
|
||||||
|
self.slotselect = slotselect
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = read_helper(recovery_fstab_path)
|
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.
|
# It's a good line, parse it.
|
||||||
length = 0
|
length = 0
|
||||||
|
slotselect = False
|
||||||
options = options.split(",")
|
options = options.split(",")
|
||||||
for i in options:
|
for i in options:
|
||||||
if i.startswith("length="):
|
if i.startswith("length="):
|
||||||
length = int(i[7:])
|
length = int(i[7:])
|
||||||
|
elif i == "slotselect":
|
||||||
|
slotselect = True
|
||||||
else:
|
else:
|
||||||
# Ignore all unknown options in the unified fstab.
|
# Ignore all unknown options in the unified fstab.
|
||||||
continue
|
continue
|
||||||
@@ -898,7 +902,8 @@ def LoadRecoveryFSTab(read_helper, fstab_version, recovery_fstab_path,
|
|||||||
|
|
||||||
mount_point = pieces[1]
|
mount_point = pieces[1]
|
||||||
d[mount_point] = Partition(mount_point=mount_point, fs_type=pieces[2],
|
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
|
# / 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
|
# 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."""
|
"""Finds the path to recovery fstab and loads its contents."""
|
||||||
# recovery fstab is only meaningful when installing an update via recovery
|
# 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.
|
# (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
|
return None
|
||||||
|
|
||||||
# We changed recovery.fstab path in Q, from ../RAMDISK/etc/recovery.fstab to
|
# We changed recovery.fstab path in Q, from ../RAMDISK/etc/recovery.fstab to
|
||||||
@@ -2680,11 +2686,12 @@ class BlockDifference(object):
|
|||||||
self.device = 'map_partition("%s")' % partition
|
self.device = 'map_partition("%s")' % partition
|
||||||
else:
|
else:
|
||||||
if OPTIONS.source_info_dict is None:
|
if OPTIONS.source_info_dict is None:
|
||||||
_, device_path = GetTypeAndDevice("/" + partition, OPTIONS.info_dict)
|
_, device_expr = GetTypeAndDeviceExpr("/" + partition,
|
||||||
|
OPTIONS.info_dict)
|
||||||
else:
|
else:
|
||||||
_, device_path = GetTypeAndDevice("/" + partition,
|
_, device_expr = GetTypeAndDeviceExpr("/" + partition,
|
||||||
OPTIONS.source_info_dict)
|
OPTIONS.source_info_dict)
|
||||||
self.device = '"%s"' % device_path
|
self.device = device_expr
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def required_cache(self):
|
def required_cache(self):
|
||||||
@@ -2916,16 +2923,51 @@ PARTITION_TYPES = {
|
|||||||
"squashfs": "EMMC"
|
"squashfs": "EMMC"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def GetTypeAndDevice(mount_point, info, check_no_slot=True):
|
||||||
def GetTypeAndDevice(mount_point, info):
|
"""
|
||||||
|
Use GetTypeAndDeviceExpr whenever possible. This function is kept for
|
||||||
|
backwards compatibility. It aborts if the fstab entry has slotselect option
|
||||||
|
(unless check_no_slot is explicitly set to False).
|
||||||
|
"""
|
||||||
fstab = info["fstab"]
|
fstab = info["fstab"]
|
||||||
if fstab:
|
if fstab:
|
||||||
|
if check_no_slot:
|
||||||
|
assert not fstab[mount_point].slotselect, \
|
||||||
|
"Use GetTypeAndDeviceExpr instead"
|
||||||
return (PARTITION_TYPES[fstab[mount_point].fs_type],
|
return (PARTITION_TYPES[fstab[mount_point].fs_type],
|
||||||
fstab[mount_point].device)
|
fstab[mount_point].device)
|
||||||
else:
|
else:
|
||||||
raise KeyError
|
raise KeyError
|
||||||
|
|
||||||
|
|
||||||
|
def GetTypeAndDeviceExpr(mount_point, info):
|
||||||
|
"""
|
||||||
|
Return the filesystem of the partition, and an edify expression that evaluates
|
||||||
|
to the device at runtime.
|
||||||
|
"""
|
||||||
|
fstab = info["fstab"]
|
||||||
|
if fstab:
|
||||||
|
p = fstab[mount_point]
|
||||||
|
device_expr = '"%s"' % fstab[mount_point].device
|
||||||
|
if p.slotselect:
|
||||||
|
device_expr = 'add_slot_suffix(%s)' % device_expr
|
||||||
|
return (PARTITION_TYPES[fstab[mount_point].fs_type], device_expr)
|
||||||
|
else:
|
||||||
|
raise KeyError
|
||||||
|
|
||||||
|
|
||||||
|
def GetEntryForDevice(fstab, device):
|
||||||
|
"""
|
||||||
|
Returns:
|
||||||
|
The first entry in fstab whose device is the given value.
|
||||||
|
"""
|
||||||
|
if not fstab:
|
||||||
|
return None
|
||||||
|
for mount_point in fstab:
|
||||||
|
if fstab[mount_point].device == device:
|
||||||
|
return fstab[mount_point]
|
||||||
|
return None
|
||||||
|
|
||||||
def ParseCertificate(data):
|
def ParseCertificate(data):
|
||||||
"""Parses and converts a PEM-encoded certificate into DER-encoded.
|
"""Parses and converts a PEM-encoded certificate into DER-encoded.
|
||||||
|
|
||||||
@@ -3050,8 +3092,10 @@ def MakeRecoveryPatch(input_dir, output_sink, recovery_img, boot_img,
|
|||||||
try:
|
try:
|
||||||
# The following GetTypeAndDevice()s need to use the path in the target
|
# The following GetTypeAndDevice()s need to use the path in the target
|
||||||
# info_dict instead of source_info_dict.
|
# info_dict instead of source_info_dict.
|
||||||
boot_type, boot_device = GetTypeAndDevice("/boot", info_dict)
|
boot_type, boot_device = GetTypeAndDevice("/boot", info_dict,
|
||||||
recovery_type, recovery_device = GetTypeAndDevice("/recovery", info_dict)
|
check_no_slot=False)
|
||||||
|
recovery_type, recovery_device = GetTypeAndDevice("/recovery", info_dict,
|
||||||
|
check_no_slot=False)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -3093,8 +3137,8 @@ fi
|
|||||||
'recovery_size': recovery_img.size,
|
'recovery_size': recovery_img.size,
|
||||||
'recovery_sha1': recovery_img.sha1,
|
'recovery_sha1': recovery_img.sha1,
|
||||||
'boot_type': boot_type,
|
'boot_type': boot_type,
|
||||||
'boot_device': boot_device,
|
'boot_device': boot_device + '$(getprop ro.boot.slot_suffix)',
|
||||||
'recovery_type': recovery_type,
|
'recovery_type': recovery_type + '$(getprop ro.boot.slot_suffix)',
|
||||||
'recovery_device': recovery_device,
|
'recovery_device': recovery_device,
|
||||||
'bonus_args': bonus_args}
|
'bonus_args': bonus_args}
|
||||||
|
|
||||||
|
@@ -183,11 +183,30 @@ class EdifyGenerator(object):
|
|||||||
It checks the checksums of the given partitions. If none of them matches the
|
It checks the checksums of the given partitions. If none of them matches the
|
||||||
expected checksum, updater will additionally look for a backup on /cache.
|
expected checksum, updater will additionally look for a backup on /cache.
|
||||||
"""
|
"""
|
||||||
|
self._CheckSecondTokenNotSlotSuffixed(target, "PatchPartitionExprCheck")
|
||||||
|
self._CheckSecondTokenNotSlotSuffixed(source, "PatchPartitionExprCheck")
|
||||||
|
self.PatchPartitionExprCheck('"%s"' % target, '"%s"' % source)
|
||||||
|
|
||||||
|
def PatchPartitionExprCheck(self, target_expr, source_expr):
|
||||||
|
"""Checks whether updater can patch the given partitions.
|
||||||
|
|
||||||
|
It checks the checksums of the given partitions. If none of them matches the
|
||||||
|
expected checksum, updater will additionally look for a backup on /cache.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
target_expr: an Edify expression that serves as the target arg to
|
||||||
|
patch_partition. Must be evaluated to a string in the form of
|
||||||
|
foo:bar:baz:quux
|
||||||
|
source_expr: an Edify expression that serves as the source arg to
|
||||||
|
patch_partition. Must be evaluated to a string in the form of
|
||||||
|
foo:bar:baz:quux
|
||||||
|
"""
|
||||||
self.script.append(self.WordWrap((
|
self.script.append(self.WordWrap((
|
||||||
'patch_partition_check("{target}",\0"{source}") ||\n abort('
|
'patch_partition_check({target},\0{source}) ||\n abort('
|
||||||
'"E{code}: \\"{target}\\" or \\"{source}\\" has unexpected '
|
'concat("E{code}: \\"",{target},"\\" or \\"",{source},"\\" has '
|
||||||
'contents.");').format(
|
'unexpected contents."));').format(
|
||||||
target=target, source=source,
|
target=target_expr,
|
||||||
|
source=source_expr,
|
||||||
code=common.ErrorCode.BAD_PATCH_FILE)))
|
code=common.ErrorCode.BAD_PATCH_FILE)))
|
||||||
|
|
||||||
def CacheFreeSpaceCheck(self, amount):
|
def CacheFreeSpaceCheck(self, amount):
|
||||||
@@ -218,8 +237,9 @@ class EdifyGenerator(object):
|
|||||||
mount_flags = mount_dict.get(p.fs_type, "")
|
mount_flags = mount_dict.get(p.fs_type, "")
|
||||||
if p.context is not None:
|
if p.context is not None:
|
||||||
mount_flags = p.context + ("," + mount_flags if mount_flags else "")
|
mount_flags = p.context + ("," + mount_flags if mount_flags else "")
|
||||||
self.script.append('mount("%s", "%s", "%s", "%s", "%s");' % (
|
self.script.append('mount("%s", "%s", %s, "%s", "%s");' % (
|
||||||
p.fs_type, common.PARTITION_TYPES[p.fs_type], p.device,
|
p.fs_type, common.PARTITION_TYPES[p.fs_type],
|
||||||
|
self._GetSlotSuffixDeviceForEntry(p),
|
||||||
p.mount_point, mount_flags))
|
p.mount_point, mount_flags))
|
||||||
self.mounts.add(p.mount_point)
|
self.mounts.add(p.mount_point)
|
||||||
|
|
||||||
@@ -242,8 +262,9 @@ class EdifyGenerator(object):
|
|||||||
raise ValueError("Partition %s cannot be tuned\n" % (partition,))
|
raise ValueError("Partition %s cannot be tuned\n" % (partition,))
|
||||||
self.script.append(
|
self.script.append(
|
||||||
'tune2fs(' + "".join(['"%s", ' % (i,) for i in options]) +
|
'tune2fs(' + "".join(['"%s", ' % (i,) for i in options]) +
|
||||||
'"%s") || abort("E%d: Failed to tune partition %s");' % (
|
'%s) || abort("E%d: Failed to tune partition %s");' % (
|
||||||
p.device, common.ErrorCode.TUNE_PARTITION_FAILURE, partition))
|
self._GetSlotSuffixDeviceForEntry(p),
|
||||||
|
common.ErrorCode.TUNE_PARTITION_FAILURE, partition))
|
||||||
|
|
||||||
def FormatPartition(self, partition):
|
def FormatPartition(self, partition):
|
||||||
"""Format the given partition, specified by its mount point (eg,
|
"""Format the given partition, specified by its mount point (eg,
|
||||||
@@ -252,18 +273,19 @@ class EdifyGenerator(object):
|
|||||||
fstab = self.fstab
|
fstab = self.fstab
|
||||||
if fstab:
|
if fstab:
|
||||||
p = fstab[partition]
|
p = fstab[partition]
|
||||||
self.script.append('format("%s", "%s", "%s", "%s", "%s");' %
|
self.script.append('format("%s", "%s", %s, "%s", "%s");' %
|
||||||
(p.fs_type, common.PARTITION_TYPES[p.fs_type],
|
(p.fs_type, common.PARTITION_TYPES[p.fs_type],
|
||||||
p.device, p.length, p.mount_point))
|
self._GetSlotSuffixDeviceForEntry(p),
|
||||||
|
p.length, p.mount_point))
|
||||||
|
|
||||||
def WipeBlockDevice(self, partition):
|
def WipeBlockDevice(self, partition):
|
||||||
if partition not in ("/system", "/vendor"):
|
if partition not in ("/system", "/vendor"):
|
||||||
raise ValueError(("WipeBlockDevice doesn't work on %s\n") % (partition,))
|
raise ValueError(("WipeBlockDevice doesn't work on %s\n") % (partition,))
|
||||||
fstab = self.fstab
|
fstab = self.fstab
|
||||||
size = self.info.get(partition.lstrip("/") + "_size", None)
|
size = self.info.get(partition.lstrip("/") + "_size", None)
|
||||||
device = fstab[partition].device
|
device = self._GetSlotSuffixDeviceForEntry(fstab[partition])
|
||||||
|
|
||||||
self.script.append('wipe_block_device("%s", %s);' % (device, size))
|
self.script.append('wipe_block_device(%s, %s);' % (device, size))
|
||||||
|
|
||||||
def ApplyPatch(self, srcfile, tgtfile, tgtsize, tgtsha1, *patchpairs):
|
def ApplyPatch(self, srcfile, tgtfile, tgtsize, tgtsha1, *patchpairs):
|
||||||
"""Apply binary patches (in *patchpairs) to the given srcfile to
|
"""Apply binary patches (in *patchpairs) to the given srcfile to
|
||||||
@@ -296,14 +318,69 @@ class EdifyGenerator(object):
|
|||||||
self.PatchPartition(target, source, patch)
|
self.PatchPartition(target, source, patch)
|
||||||
|
|
||||||
def PatchPartition(self, target, source, patch):
|
def PatchPartition(self, target, source, patch):
|
||||||
"""Applies the patch to the source partition and writes it to target."""
|
"""
|
||||||
|
Applies the patch to the source partition and writes it to target.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
target: the target arg to patch_partition. Must be in the form of
|
||||||
|
foo:bar:baz:quux
|
||||||
|
source: the source arg to patch_partition. Must be in the form of
|
||||||
|
foo:bar:baz:quux
|
||||||
|
patch: the patch arg to patch_partition. Must be an unquoted string.
|
||||||
|
"""
|
||||||
|
self._CheckSecondTokenNotSlotSuffixed(target, "PatchPartitionExpr")
|
||||||
|
self._CheckSecondTokenNotSlotSuffixed(source, "PatchPartitionExpr")
|
||||||
|
self.PatchPartitionExpr('"%s"' % target, '"%s"' % source, '"%s"' % patch)
|
||||||
|
|
||||||
|
def PatchPartitionExpr(self, target_expr, source_expr, patch_expr):
|
||||||
|
"""
|
||||||
|
Applies the patch to the source partition and writes it to target.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
target_expr: an Edify expression that serves as the target arg to
|
||||||
|
patch_partition. Must be evaluated to a string in the form of
|
||||||
|
foo:bar:baz:quux
|
||||||
|
source_expr: an Edify expression that serves as the source arg to
|
||||||
|
patch_partition. Must be evaluated to a string in the form of
|
||||||
|
foo:bar:baz:quux
|
||||||
|
patch_expr: an Edify expression that serves as the patch arg to
|
||||||
|
patch_partition. Must be evaluated to a string.
|
||||||
|
"""
|
||||||
self.script.append(self.WordWrap((
|
self.script.append(self.WordWrap((
|
||||||
'patch_partition("{target}",\0"{source}",\0'
|
'patch_partition({target},\0{source},\0'
|
||||||
'package_extract_file("{patch}")) ||\n'
|
'package_extract_file({patch})) ||\n'
|
||||||
' abort("E{code}: Failed to apply patch to {source}");').format(
|
' abort(concat('
|
||||||
target=target, source=source, patch=patch,
|
' "E{code}: Failed to apply patch to ",{source}));').format(
|
||||||
|
target=target_expr,
|
||||||
|
source=source_expr,
|
||||||
|
patch=patch_expr,
|
||||||
code=common.ErrorCode.APPLY_PATCH_FAILURE)))
|
code=common.ErrorCode.APPLY_PATCH_FAILURE)))
|
||||||
|
|
||||||
|
def _GetSlotSuffixDeviceForEntry(self, entry=None):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
entry: the fstab entry of device "foo"
|
||||||
|
Returns:
|
||||||
|
An edify expression. Caller must not quote result.
|
||||||
|
If foo is slot suffixed, it returns
|
||||||
|
'add_slot_suffix("foo")'
|
||||||
|
Otherwise it returns
|
||||||
|
'"foo"' (quoted)
|
||||||
|
"""
|
||||||
|
assert entry is not None
|
||||||
|
if entry.slotselect:
|
||||||
|
return 'add_slot_suffix("%s")' % entry.device
|
||||||
|
return '"%s"' % entry.device
|
||||||
|
|
||||||
|
def _CheckSecondTokenNotSlotSuffixed(self, s, fn):
|
||||||
|
lst = s.split(':')
|
||||||
|
assert(len(s) == 4), "{} does not contain 4 tokens".format(s)
|
||||||
|
if self.fstab:
|
||||||
|
entry = common.GetEntryForDevice(s[1])
|
||||||
|
if entry is not None:
|
||||||
|
assert not entry.slotselect, \
|
||||||
|
"Use %s because %s is slot suffixed" % (fn, s[1])
|
||||||
|
|
||||||
def WriteRawImage(self, mount_point, fn, mapfn=None):
|
def WriteRawImage(self, mount_point, fn, mapfn=None):
|
||||||
"""Write the given package file into the partition for the given
|
"""Write the given package file into the partition for the given
|
||||||
mount point."""
|
mount point."""
|
||||||
@@ -312,15 +389,16 @@ class EdifyGenerator(object):
|
|||||||
if fstab:
|
if fstab:
|
||||||
p = fstab[mount_point]
|
p = fstab[mount_point]
|
||||||
partition_type = common.PARTITION_TYPES[p.fs_type]
|
partition_type = common.PARTITION_TYPES[p.fs_type]
|
||||||
args = {'device': p.device, 'fn': fn}
|
device = self._GetSlotSuffixDeviceForEntry(p)
|
||||||
|
args = {'device': device, 'fn': fn}
|
||||||
if partition_type == "EMMC":
|
if partition_type == "EMMC":
|
||||||
if mapfn:
|
if mapfn:
|
||||||
args["map"] = mapfn
|
args["map"] = mapfn
|
||||||
self.script.append(
|
self.script.append(
|
||||||
'package_extract_file("%(fn)s", "%(device)s", "%(map)s");' % args)
|
'package_extract_file("%(fn)s", %(device)s, "%(map)s");' % args)
|
||||||
else:
|
else:
|
||||||
self.script.append(
|
self.script.append(
|
||||||
'package_extract_file("%(fn)s", "%(device)s");' % args)
|
'package_extract_file("%(fn)s", %(device)s);' % args)
|
||||||
else:
|
else:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"don't know how to write \"%s\" partitions" % p.fs_type)
|
"don't know how to write \"%s\" partitions" % p.fs_type)
|
||||||
|
@@ -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
|
Write a copy of the metadata to a separate file. Therefore, users can
|
||||||
read the post build fingerprint without extracting the OTA package.
|
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
|
Non-A/B OTA specific options
|
||||||
|
|
||||||
-b (--binary) <file>
|
-b (--binary) <file>
|
||||||
@@ -251,6 +258,7 @@ OPTIONS.skip_compatibility_check = False
|
|||||||
OPTIONS.output_metadata_path = None
|
OPTIONS.output_metadata_path = None
|
||||||
OPTIONS.disable_fec_computation = False
|
OPTIONS.disable_fec_computation = False
|
||||||
OPTIONS.boot_variable_values = None
|
OPTIONS.boot_variable_values = None
|
||||||
|
OPTIONS.force_non_ab = False
|
||||||
|
|
||||||
|
|
||||||
METADATA_NAME = 'META-INF/com/android/metadata'
|
METADATA_NAME = 'META-INF/com/android/metadata'
|
||||||
@@ -933,7 +941,7 @@ def GetPackageMetadata(target_info, source_info=None):
|
|||||||
'ro.build.version.security_patch'),
|
'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-type'] = 'AB'
|
||||||
metadata['ota-required-cache'] = '0'
|
metadata['ota-required-cache'] = '0'
|
||||||
else:
|
else:
|
||||||
@@ -1455,7 +1463,8 @@ else if get_stage("%(bcb_dev)s") != "3/3" then
|
|||||||
required_cache_sizes = [diff.required_cache for diff in
|
required_cache_sizes = [diff.required_cache for diff in
|
||||||
block_diff_dict.values()]
|
block_diff_dict.values()]
|
||||||
if updating_boot:
|
if updating_boot:
|
||||||
boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
|
boot_type, boot_device_expr = common.GetTypeAndDeviceExpr("/boot",
|
||||||
|
source_info)
|
||||||
d = common.Difference(target_boot, source_boot)
|
d = common.Difference(target_boot, source_boot)
|
||||||
_, _, d = d.ComputePatch()
|
_, _, d = d.ComputePatch()
|
||||||
if d is None:
|
if d is None:
|
||||||
@@ -1470,11 +1479,11 @@ else if get_stage("%(bcb_dev)s") != "3/3" then
|
|||||||
|
|
||||||
common.ZipWriteStr(output_zip, "boot.img.p", d)
|
common.ZipWriteStr(output_zip, "boot.img.p", d)
|
||||||
|
|
||||||
script.PatchPartitionCheck(
|
target_expr = 'concat("{}:",{},":{}:{}")'.format(
|
||||||
"{}:{}:{}:{}".format(
|
boot_type, boot_device_expr, target_boot.size, target_boot.sha1)
|
||||||
boot_type, boot_device, target_boot.size, target_boot.sha1),
|
source_expr = 'concat("{}:",{},":{}:{}")'.format(
|
||||||
"{}:{}:{}:{}".format(
|
boot_type, boot_device_expr, source_boot.size, source_boot.sha1)
|
||||||
boot_type, boot_device, source_boot.size, source_boot.sha1))
|
script.PatchPartitionExprCheck(target_expr, source_expr)
|
||||||
|
|
||||||
required_cache_sizes.append(target_boot.size)
|
required_cache_sizes.append(target_boot.size)
|
||||||
|
|
||||||
@@ -1542,12 +1551,11 @@ else
|
|||||||
logger.info("boot image changed; including patch.")
|
logger.info("boot image changed; including patch.")
|
||||||
script.Print("Patching boot image...")
|
script.Print("Patching boot image...")
|
||||||
script.ShowProgress(0.1, 10)
|
script.ShowProgress(0.1, 10)
|
||||||
script.PatchPartition(
|
target_expr = 'concat("{}:",{},":{}:{}")'.format(
|
||||||
'{}:{}:{}:{}'.format(
|
boot_type, boot_device_expr, target_boot.size, target_boot.sha1)
|
||||||
boot_type, boot_device, target_boot.size, target_boot.sha1),
|
source_expr = 'concat("{}:",{},":{}:{}")'.format(
|
||||||
'{}:{}:{}:{}'.format(
|
boot_type, boot_device_expr, source_boot.size, source_boot.sha1)
|
||||||
boot_type, boot_device, source_boot.size, source_boot.sha1),
|
script.PatchPartitionExpr(target_expr, source_expr, '"boot.img.p"')
|
||||||
'boot.img.p')
|
|
||||||
else:
|
else:
|
||||||
logger.info("boot image unchanged; skipping.")
|
logger.info("boot image unchanged; skipping.")
|
||||||
|
|
||||||
@@ -2067,6 +2075,8 @@ def main(argv):
|
|||||||
OPTIONS.output_metadata_path = a
|
OPTIONS.output_metadata_path = a
|
||||||
elif o == "--disable_fec_computation":
|
elif o == "--disable_fec_computation":
|
||||||
OPTIONS.disable_fec_computation = True
|
OPTIONS.disable_fec_computation = True
|
||||||
|
elif o == "--force_non_ab":
|
||||||
|
OPTIONS.force_non_ab = True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
@@ -2103,6 +2113,7 @@ def main(argv):
|
|||||||
"skip_compatibility_check",
|
"skip_compatibility_check",
|
||||||
"output_metadata_path=",
|
"output_metadata_path=",
|
||||||
"disable_fec_computation",
|
"disable_fec_computation",
|
||||||
|
"force_non_ab",
|
||||||
], extra_option_handler=option_handler)
|
], extra_option_handler=option_handler)
|
||||||
|
|
||||||
if len(args) != 2:
|
if len(args) != 2:
|
||||||
@@ -2164,11 +2175,17 @@ def main(argv):
|
|||||||
OPTIONS.skip_postinstall = True
|
OPTIONS.skip_postinstall = True
|
||||||
|
|
||||||
ab_update = OPTIONS.info_dict.get("ab_update") == "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.
|
# 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
|
# package_keys are needed on ab_updates, so always define them if an
|
||||||
# ab_update is getting created.
|
# A/B update is getting created.
|
||||||
if not OPTIONS.no_signing or ab_update:
|
if not OPTIONS.no_signing or generate_ab:
|
||||||
if OPTIONS.package_key is None:
|
if OPTIONS.package_key is None:
|
||||||
OPTIONS.package_key = OPTIONS.info_dict.get(
|
OPTIONS.package_key = OPTIONS.info_dict.get(
|
||||||
"default_system_dev_certificate",
|
"default_system_dev_certificate",
|
||||||
@@ -2176,7 +2193,7 @@ def main(argv):
|
|||||||
# Get signing keys
|
# Get signing keys
|
||||||
OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
|
OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
|
||||||
|
|
||||||
if ab_update:
|
if generate_ab:
|
||||||
GenerateAbOtaPackage(
|
GenerateAbOtaPackage(
|
||||||
target_file=args[0],
|
target_file=args[0],
|
||||||
output_file=args[1],
|
output_file=args[1],
|
||||||
|
Reference in New Issue
Block a user