# Copyright (C) 2022 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.
import common from typing import BinaryIO, List from update_metadata_pb2 import DeltaArchiveManifest, DynamicPartitionMetadata, DynamicPartitionGroup from ota_metadata_pb2 import OtaMetadata from update_payload import Payload
from payload_signer import PayloadSigner from ota_utils import PayloadGenerator, METADATA_PROTO_NAME, FinalizeMetadata from ota_signing_utils import AddSigningArgumentParse
def WriteDataBlob(payload: Payload, outfp: BinaryIO, read_size=1024*64): for i in range(0, payload.total_data_length, read_size):
blob = payload.ReadDataBlob(
i, min(i+read_size, payload.total_data_length)-i)
outfp.write(blob)
def ConcatBlobs(payloads: List[Payload], outfp: BinaryIO): for payload in payloads:
WriteDataBlob(payload, outfp)
def TotalDataLength(partitions): for partition in reversed(partitions): for op in reversed(partition.operations): if op.data_length > 0: return op.data_offset + op.data_length return0
def ExtendPartitionUpdates(partitions, new_partitions):
prefix_blob_length = TotalDataLength(partitions)
partitions.extend(new_partitions) for part in partitions[-len(new_partitions):]: for op in part.operations: if op.HasField("data_length") and op.data_length != 0:
op.data_offset += prefix_blob_length
class DuplicatePartitionError(ValueError): pass
def MergeDynamicPartitionGroups(groups: List[DynamicPartitionGroup], new_groups: List[DynamicPartitionGroup]):
new_groups = {new_group.name: new_group for new_group in new_groups} for group in groups: if group.name notin new_groups: continue
new_group = new_groups[group.name]
common_partitions = set(group.partition_names).intersection(
set(new_group.partition_names)) if len(common_partitions) != 0: raise DuplicatePartitionError(
f"Old group and new group should not have any intersections, {group.partition_names}, {new_group.partition_names}, common partitions: {common_partitions}")
group.partition_names.extend(new_group.partition_names)
group.size = max(new_group.size, group.size) del new_groups[group.name] for new_group in new_groups.values():
groups.append(new_group)
def CheckDuplicatePartitions(payloads: List[Payload]):
partition_to_ota = {} for payload in payloads: for group in payload.manifest.dynamic_partition_metadata.groups: for part in group.partition_names: if part in partition_to_ota: raise DuplicatePartitionError(
f"OTA {partition_to_ota[part].name} and {payload.name} have duplicating partition {part}")
partition_to_ota[part] = payload
def ApexInfo(file_paths): if len(file_paths) > 1:
logger.info("More than one target file specified, will ignore " "apex_info.pb (if any)") returnNone with zipfile.ZipFile(file_paths[0], "r", allowZip64=True) as zfp: if APEX_INFO_ENTRY in zfp.namelist():
apex_info_bytes = zfp.read(APEX_INFO_ENTRY) return apex_info_bytes returnNone
def main(argv):
parser = argparse.ArgumentParser(description='Merge multiple partial OTAs')
parser.add_argument('packages', type=str, nargs='+',
help='Paths to OTA packages to merge')
parser.add_argument('--output', type=str,
help='Paths to output merged ota', required=True)
parser.add_argument('--metadata_ota', type=str,
help='Output zip will use build metadata from this OTA package, if unspecified, use the last OTA package in merge list')
parser.add_argument('-v', action="store_true",
help="Enable verbose logging", dest="verbose")
AddSigningArgumentParse(parser)
parser.epilog = ('This tool can also be used to resign a regular OTA. For a single regular OTA, ' 'apex_info.pb will be written to output. When merging multiple OTAs, ' 'apex_info.pb will not be written.')
args = parser.parse_args(argv[1:])
file_paths = args.packages
common.OPTIONS.verbose = args.verbose if args.verbose:
logger.setLevel(logging.INFO)
logger.info(args) if args.java_path:
common.OPTIONS.java_path = args.java_path
if args.search_path:
common.OPTIONS.search_path = args.search_path
if args.signapk_path:
common.OPTIONS.signapk_path = args.signapk_path
if args.extra_signapk_args:
common.OPTIONS.extra_signapk_args = args.extra_signapk_args
if args.signapk_shared_library_path:
common.OPTIONS.signapk_shared_library_path = args.signapk_shared_library_path
# Get signing keys
key_passwords = common.GetKeyPasswords([args.package_key])
apex_info_bytes = ApexInfo(file_paths)
with tempfile.NamedTemporaryFile() as unsigned_payload:
WriteHeaderAndManifest(merged_manifest, unsigned_payload)
ConcatBlobs(payloads, unsigned_payload)
unsigned_payload.flush()
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.