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:
Treehugger Robot
2020-05-20 00:30:31 +00:00
committed by Gerrit Code Review
8 changed files with 251 additions and 59 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View 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

View File

@@ -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()

View File

@@ -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}

View File

@@ -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)

View File

@@ -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],