1*9e94795aSAndroid Build Coastguard Worker# Copyright (C) 2020 The Android Open Source Project 2*9e94795aSAndroid Build Coastguard Worker# 3*9e94795aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 4*9e94795aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 5*9e94795aSAndroid Build Coastguard Worker# You may obtain a copy of the License at 6*9e94795aSAndroid Build Coastguard Worker# 7*9e94795aSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 8*9e94795aSAndroid Build Coastguard Worker# 9*9e94795aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 10*9e94795aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 11*9e94795aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*9e94795aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 13*9e94795aSAndroid Build Coastguard Worker# limitations under the License. 14*9e94795aSAndroid Build Coastguard Worker 15*9e94795aSAndroid Build Coastguard Workerimport copy 16*9e94795aSAndroid Build Coastguard Workerimport itertools 17*9e94795aSAndroid Build Coastguard Workerimport logging 18*9e94795aSAndroid Build Coastguard Workerimport os 19*9e94795aSAndroid Build Coastguard Workerimport shutil 20*9e94795aSAndroid Build Coastguard Workerimport struct 21*9e94795aSAndroid Build Coastguard Workerimport zipfile 22*9e94795aSAndroid Build Coastguard Worker 23*9e94795aSAndroid Build Coastguard Workerimport ota_metadata_pb2 24*9e94795aSAndroid Build Coastguard Workerimport common 25*9e94795aSAndroid Build Coastguard Workerimport fnmatch 26*9e94795aSAndroid Build Coastguard Workerfrom common import (ZipDelete, DoesInputFileContain, ReadBytesFromInputFile, OPTIONS, MakeTempFile, 27*9e94795aSAndroid Build Coastguard Worker ZipWriteStr, BuildInfo, LoadDictionaryFromFile, 28*9e94795aSAndroid Build Coastguard Worker SignFile, PARTITIONS_WITH_BUILD_PROP, PartitionBuildProps, 29*9e94795aSAndroid Build Coastguard Worker GetRamdiskFormat, ParseUpdateEngineConfig) 30*9e94795aSAndroid Build Coastguard Workerimport payload_signer 31*9e94795aSAndroid Build Coastguard Workerfrom payload_signer import PayloadSigner, AddSigningArgumentParse, GeneratePayloadProperties 32*9e94795aSAndroid Build Coastguard Worker 33*9e94795aSAndroid Build Coastguard Worker 34*9e94795aSAndroid Build Coastguard Workerlogger = logging.getLogger(__name__) 35*9e94795aSAndroid Build Coastguard Worker 36*9e94795aSAndroid Build Coastguard WorkerOPTIONS.no_signing = False 37*9e94795aSAndroid Build Coastguard WorkerOPTIONS.force_non_ab = False 38*9e94795aSAndroid Build Coastguard WorkerOPTIONS.wipe_user_data = False 39*9e94795aSAndroid Build Coastguard WorkerOPTIONS.downgrade = False 40*9e94795aSAndroid Build Coastguard WorkerOPTIONS.key_passwords = {} 41*9e94795aSAndroid Build Coastguard WorkerOPTIONS.incremental_source = None 42*9e94795aSAndroid Build Coastguard WorkerOPTIONS.retrofit_dynamic_partitions = False 43*9e94795aSAndroid Build Coastguard WorkerOPTIONS.output_metadata_path = None 44*9e94795aSAndroid Build Coastguard WorkerOPTIONS.boot_variable_file = None 45*9e94795aSAndroid Build Coastguard Worker 46*9e94795aSAndroid Build Coastguard WorkerMETADATA_NAME = 'META-INF/com/android/metadata' 47*9e94795aSAndroid Build Coastguard WorkerMETADATA_PROTO_NAME = 'META-INF/com/android/metadata.pb' 48*9e94795aSAndroid Build Coastguard WorkerUNZIP_PATTERN = ['IMAGES/*', 'META/*', 'OTA/*', 49*9e94795aSAndroid Build Coastguard Worker 'RADIO/*', '*/build.prop', '*/default.prop', '*/build.default', "*/etc/vintf/*"] 50*9e94795aSAndroid Build Coastguard WorkerSECURITY_PATCH_LEVEL_PROP_NAME = "ro.build.version.security_patch" 51*9e94795aSAndroid Build Coastguard WorkerTARGET_FILES_IMAGES_SUBDIR = ["IMAGES", "PREBUILT_IMAGES", "RADIO"] 52*9e94795aSAndroid Build Coastguard Worker 53*9e94795aSAndroid Build Coastguard Worker 54*9e94795aSAndroid Build Coastguard Worker# Key is the compression algorithm, value is minimum API level required to 55*9e94795aSAndroid Build Coastguard Worker# use this compression algorithm for VABC OTA on device. 56*9e94795aSAndroid Build Coastguard WorkerVABC_COMPRESSION_PARAM_SUPPORT = { 57*9e94795aSAndroid Build Coastguard Worker "gz": 31, 58*9e94795aSAndroid Build Coastguard Worker "brotli": 31, 59*9e94795aSAndroid Build Coastguard Worker "none": 31, 60*9e94795aSAndroid Build Coastguard Worker # lz4 support is added in Android U 61*9e94795aSAndroid Build Coastguard Worker "lz4": 34, 62*9e94795aSAndroid Build Coastguard Worker # zstd support is added in Android V 63*9e94795aSAndroid Build Coastguard Worker "zstd": 35, 64*9e94795aSAndroid Build Coastguard Worker} 65*9e94795aSAndroid Build Coastguard Worker 66*9e94795aSAndroid Build Coastguard Worker 67*9e94795aSAndroid Build Coastguard Workerdef FinalizeMetadata(metadata, input_file, output_file, needed_property_files=None, package_key=None, pw=None): 68*9e94795aSAndroid Build Coastguard Worker """Finalizes the metadata and signs an A/B OTA package. 69*9e94795aSAndroid Build Coastguard Worker 70*9e94795aSAndroid Build Coastguard Worker In order to stream an A/B OTA package, we need 'ota-streaming-property-files' 71*9e94795aSAndroid Build Coastguard Worker that contains the offsets and sizes for the ZIP entries. An example 72*9e94795aSAndroid Build Coastguard Worker property-files string is as follows. 73*9e94795aSAndroid Build Coastguard Worker 74*9e94795aSAndroid Build Coastguard Worker "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379" 75*9e94795aSAndroid Build Coastguard Worker 76*9e94795aSAndroid Build Coastguard Worker OTA server can pass down this string, in addition to the package URL, to the 77*9e94795aSAndroid Build Coastguard Worker system update client. System update client can then fetch individual ZIP 78*9e94795aSAndroid Build Coastguard Worker entries (ZIP_STORED) directly at the given offset of the URL. 79*9e94795aSAndroid Build Coastguard Worker 80*9e94795aSAndroid Build Coastguard Worker Args: 81*9e94795aSAndroid Build Coastguard Worker metadata: The metadata dict for the package. 82*9e94795aSAndroid Build Coastguard Worker input_file: The input ZIP filename that doesn't contain the package METADATA 83*9e94795aSAndroid Build Coastguard Worker entry yet. 84*9e94795aSAndroid Build Coastguard Worker output_file: The final output ZIP filename. 85*9e94795aSAndroid Build Coastguard Worker needed_property_files: The list of PropertyFiles' to be generated. Default is [AbOtaPropertyFiles(), StreamingPropertyFiles()] 86*9e94795aSAndroid Build Coastguard Worker package_key: The key used to sign this OTA package 87*9e94795aSAndroid Build Coastguard Worker pw: Password for the package_key 88*9e94795aSAndroid Build Coastguard Worker """ 89*9e94795aSAndroid Build Coastguard Worker no_signing = package_key is None 90*9e94795aSAndroid Build Coastguard Worker 91*9e94795aSAndroid Build Coastguard Worker if needed_property_files is None: 92*9e94795aSAndroid Build Coastguard Worker # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers 93*9e94795aSAndroid Build Coastguard Worker # all the info of the latter. However, system updaters and OTA servers need to 94*9e94795aSAndroid Build Coastguard Worker # take time to switch to the new flag. We keep both of the flags for 95*9e94795aSAndroid Build Coastguard Worker # P-timeframe, and will remove StreamingPropertyFiles in later release. 96*9e94795aSAndroid Build Coastguard Worker needed_property_files = ( 97*9e94795aSAndroid Build Coastguard Worker AbOtaPropertyFiles(), 98*9e94795aSAndroid Build Coastguard Worker StreamingPropertyFiles(), 99*9e94795aSAndroid Build Coastguard Worker ) 100*9e94795aSAndroid Build Coastguard Worker 101*9e94795aSAndroid Build Coastguard Worker def ComputeAllPropertyFiles(input_file, needed_property_files): 102*9e94795aSAndroid Build Coastguard Worker # Write the current metadata entry with placeholders. 103*9e94795aSAndroid Build Coastguard Worker with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip: 104*9e94795aSAndroid Build Coastguard Worker for property_files in needed_property_files: 105*9e94795aSAndroid Build Coastguard Worker metadata.property_files[property_files.name] = property_files.Compute( 106*9e94795aSAndroid Build Coastguard Worker input_zip) 107*9e94795aSAndroid Build Coastguard Worker 108*9e94795aSAndroid Build Coastguard Worker ZipDelete(input_file, [METADATA_NAME, METADATA_PROTO_NAME], True) 109*9e94795aSAndroid Build Coastguard Worker with zipfile.ZipFile(input_file, 'a', allowZip64=True) as output_zip: 110*9e94795aSAndroid Build Coastguard Worker WriteMetadata(metadata, output_zip) 111*9e94795aSAndroid Build Coastguard Worker 112*9e94795aSAndroid Build Coastguard Worker if no_signing: 113*9e94795aSAndroid Build Coastguard Worker return input_file 114*9e94795aSAndroid Build Coastguard Worker 115*9e94795aSAndroid Build Coastguard Worker prelim_signing = MakeTempFile(suffix='.zip') 116*9e94795aSAndroid Build Coastguard Worker SignOutput(input_file, prelim_signing, package_key, pw) 117*9e94795aSAndroid Build Coastguard Worker return prelim_signing 118*9e94795aSAndroid Build Coastguard Worker 119*9e94795aSAndroid Build Coastguard Worker def FinalizeAllPropertyFiles(prelim_signing, needed_property_files): 120*9e94795aSAndroid Build Coastguard Worker with zipfile.ZipFile(prelim_signing, 'r', allowZip64=True) as prelim_signing_zip: 121*9e94795aSAndroid Build Coastguard Worker for property_files in needed_property_files: 122*9e94795aSAndroid Build Coastguard Worker metadata.property_files[property_files.name] = property_files.Finalize( 123*9e94795aSAndroid Build Coastguard Worker prelim_signing_zip, 124*9e94795aSAndroid Build Coastguard Worker len(metadata.property_files[property_files.name])) 125*9e94795aSAndroid Build Coastguard Worker 126*9e94795aSAndroid Build Coastguard Worker # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP 127*9e94795aSAndroid Build Coastguard Worker # entries, as well as padding the entry headers. We do a preliminary signing 128*9e94795aSAndroid Build Coastguard Worker # (with an incomplete metadata entry) to allow that to happen. Then compute 129*9e94795aSAndroid Build Coastguard Worker # the ZIP entry offsets, write back the final metadata and do the final 130*9e94795aSAndroid Build Coastguard Worker # signing. 131*9e94795aSAndroid Build Coastguard Worker prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files) 132*9e94795aSAndroid Build Coastguard Worker try: 133*9e94795aSAndroid Build Coastguard Worker FinalizeAllPropertyFiles(prelim_signing, needed_property_files) 134*9e94795aSAndroid Build Coastguard Worker except PropertyFiles.InsufficientSpaceException: 135*9e94795aSAndroid Build Coastguard Worker # Even with the preliminary signing, the entry orders may change 136*9e94795aSAndroid Build Coastguard Worker # dramatically, which leads to insufficiently reserved space during the 137*9e94795aSAndroid Build Coastguard Worker # first call to ComputeAllPropertyFiles(). In that case, we redo all the 138*9e94795aSAndroid Build Coastguard Worker # preliminary signing works, based on the already ordered ZIP entries, to 139*9e94795aSAndroid Build Coastguard Worker # address the issue. 140*9e94795aSAndroid Build Coastguard Worker prelim_signing = ComputeAllPropertyFiles( 141*9e94795aSAndroid Build Coastguard Worker prelim_signing, needed_property_files) 142*9e94795aSAndroid Build Coastguard Worker FinalizeAllPropertyFiles(prelim_signing, needed_property_files) 143*9e94795aSAndroid Build Coastguard Worker 144*9e94795aSAndroid Build Coastguard Worker # Replace the METADATA entry. 145*9e94795aSAndroid Build Coastguard Worker ZipDelete(prelim_signing, [METADATA_NAME, METADATA_PROTO_NAME]) 146*9e94795aSAndroid Build Coastguard Worker with zipfile.ZipFile(prelim_signing, 'a', allowZip64=True) as output_zip: 147*9e94795aSAndroid Build Coastguard Worker WriteMetadata(metadata, output_zip) 148*9e94795aSAndroid Build Coastguard Worker 149*9e94795aSAndroid Build Coastguard Worker # Re-sign the package after updating the metadata entry. 150*9e94795aSAndroid Build Coastguard Worker if no_signing: 151*9e94795aSAndroid Build Coastguard Worker logger.info(f"Signing disabled for output file {output_file}") 152*9e94795aSAndroid Build Coastguard Worker shutil.copy(prelim_signing, output_file) 153*9e94795aSAndroid Build Coastguard Worker else: 154*9e94795aSAndroid Build Coastguard Worker logger.info( 155*9e94795aSAndroid Build Coastguard Worker f"Signing the output file {output_file} with key {package_key}") 156*9e94795aSAndroid Build Coastguard Worker SignOutput(prelim_signing, output_file, package_key, pw) 157*9e94795aSAndroid Build Coastguard Worker 158*9e94795aSAndroid Build Coastguard Worker # Reopen the final signed zip to double check the streaming metadata. 159*9e94795aSAndroid Build Coastguard Worker with zipfile.ZipFile(output_file, allowZip64=True) as output_zip: 160*9e94795aSAndroid Build Coastguard Worker for property_files in needed_property_files: 161*9e94795aSAndroid Build Coastguard Worker property_files.Verify( 162*9e94795aSAndroid Build Coastguard Worker output_zip, metadata.property_files[property_files.name].strip()) 163*9e94795aSAndroid Build Coastguard Worker 164*9e94795aSAndroid Build Coastguard Worker # If requested, dump the metadata to a separate file. 165*9e94795aSAndroid Build Coastguard Worker output_metadata_path = OPTIONS.output_metadata_path 166*9e94795aSAndroid Build Coastguard Worker if output_metadata_path: 167*9e94795aSAndroid Build Coastguard Worker WriteMetadata(metadata, output_metadata_path) 168*9e94795aSAndroid Build Coastguard Worker 169*9e94795aSAndroid Build Coastguard Worker 170*9e94795aSAndroid Build Coastguard Workerdef WriteMetadata(metadata_proto, output): 171*9e94795aSAndroid Build Coastguard Worker """Writes the metadata to the zip archive or a file. 172*9e94795aSAndroid Build Coastguard Worker 173*9e94795aSAndroid Build Coastguard Worker Args: 174*9e94795aSAndroid Build Coastguard Worker metadata_proto: The metadata protobuf for the package. 175*9e94795aSAndroid Build Coastguard Worker output: A ZipFile object or a string of the output file path. If a string 176*9e94795aSAndroid Build Coastguard Worker path is given, the metadata in the protobuf format will be written to 177*9e94795aSAndroid Build Coastguard Worker {output}.pb, e.g. ota_metadata.pb 178*9e94795aSAndroid Build Coastguard Worker """ 179*9e94795aSAndroid Build Coastguard Worker 180*9e94795aSAndroid Build Coastguard Worker metadata_dict = BuildLegacyOtaMetadata(metadata_proto) 181*9e94795aSAndroid Build Coastguard Worker legacy_metadata = "".join(["%s=%s\n" % kv for kv in 182*9e94795aSAndroid Build Coastguard Worker sorted(metadata_dict.items())]) 183*9e94795aSAndroid Build Coastguard Worker if isinstance(output, zipfile.ZipFile): 184*9e94795aSAndroid Build Coastguard Worker ZipWriteStr(output, METADATA_PROTO_NAME, metadata_proto.SerializeToString(), 185*9e94795aSAndroid Build Coastguard Worker compress_type=zipfile.ZIP_STORED) 186*9e94795aSAndroid Build Coastguard Worker ZipWriteStr(output, METADATA_NAME, legacy_metadata, 187*9e94795aSAndroid Build Coastguard Worker compress_type=zipfile.ZIP_STORED) 188*9e94795aSAndroid Build Coastguard Worker return 189*9e94795aSAndroid Build Coastguard Worker 190*9e94795aSAndroid Build Coastguard Worker with open('{}.pb'.format(output), 'wb') as f: 191*9e94795aSAndroid Build Coastguard Worker f.write(metadata_proto.SerializeToString()) 192*9e94795aSAndroid Build Coastguard Worker with open(output, 'w') as f: 193*9e94795aSAndroid Build Coastguard Worker f.write(legacy_metadata) 194*9e94795aSAndroid Build Coastguard Worker 195*9e94795aSAndroid Build Coastguard Worker 196*9e94795aSAndroid Build Coastguard Workerdef UpdateDeviceState(device_state, build_info, boot_variable_values, 197*9e94795aSAndroid Build Coastguard Worker is_post_build): 198*9e94795aSAndroid Build Coastguard Worker """Update the fields of the DeviceState proto with build info.""" 199*9e94795aSAndroid Build Coastguard Worker 200*9e94795aSAndroid Build Coastguard Worker def UpdatePartitionStates(partition_states): 201*9e94795aSAndroid Build Coastguard Worker """Update the per-partition state according to its build.prop""" 202*9e94795aSAndroid Build Coastguard Worker if not build_info.is_ab: 203*9e94795aSAndroid Build Coastguard Worker return 204*9e94795aSAndroid Build Coastguard Worker build_info_set = ComputeRuntimeBuildInfos(build_info, 205*9e94795aSAndroid Build Coastguard Worker boot_variable_values) 206*9e94795aSAndroid Build Coastguard Worker assert "ab_partitions" in build_info.info_dict,\ 207*9e94795aSAndroid Build Coastguard Worker "ab_partitions property required for ab update." 208*9e94795aSAndroid Build Coastguard Worker ab_partitions = set(build_info.info_dict.get("ab_partitions")) 209*9e94795aSAndroid Build Coastguard Worker 210*9e94795aSAndroid Build Coastguard Worker # delta_generator will error out on unused timestamps, 211*9e94795aSAndroid Build Coastguard Worker # so only generate timestamps for dynamic partitions 212*9e94795aSAndroid Build Coastguard Worker # used in OTA update. 213*9e94795aSAndroid Build Coastguard Worker for partition in sorted(set(PARTITIONS_WITH_BUILD_PROP) & ab_partitions): 214*9e94795aSAndroid Build Coastguard Worker partition_prop = build_info.info_dict.get( 215*9e94795aSAndroid Build Coastguard Worker '{}.build.prop'.format(partition)) 216*9e94795aSAndroid Build Coastguard Worker # Skip if the partition is missing, or it doesn't have a build.prop 217*9e94795aSAndroid Build Coastguard Worker if not partition_prop or not partition_prop.build_props: 218*9e94795aSAndroid Build Coastguard Worker continue 219*9e94795aSAndroid Build Coastguard Worker 220*9e94795aSAndroid Build Coastguard Worker partition_state = partition_states.add() 221*9e94795aSAndroid Build Coastguard Worker partition_state.partition_name = partition 222*9e94795aSAndroid Build Coastguard Worker # Update the partition's runtime device names and fingerprints 223*9e94795aSAndroid Build Coastguard Worker partition_devices = set() 224*9e94795aSAndroid Build Coastguard Worker partition_fingerprints = set() 225*9e94795aSAndroid Build Coastguard Worker for runtime_build_info in build_info_set: 226*9e94795aSAndroid Build Coastguard Worker partition_devices.add( 227*9e94795aSAndroid Build Coastguard Worker runtime_build_info.GetPartitionBuildProp('ro.product.device', 228*9e94795aSAndroid Build Coastguard Worker partition)) 229*9e94795aSAndroid Build Coastguard Worker partition_fingerprints.add( 230*9e94795aSAndroid Build Coastguard Worker runtime_build_info.GetPartitionFingerprint(partition)) 231*9e94795aSAndroid Build Coastguard Worker 232*9e94795aSAndroid Build Coastguard Worker partition_state.device.extend(sorted(partition_devices)) 233*9e94795aSAndroid Build Coastguard Worker partition_state.build.extend(sorted(partition_fingerprints)) 234*9e94795aSAndroid Build Coastguard Worker 235*9e94795aSAndroid Build Coastguard Worker # TODO(xunchang) set the boot image's version with kmi. Note the boot 236*9e94795aSAndroid Build Coastguard Worker # image doesn't have a file map. 237*9e94795aSAndroid Build Coastguard Worker partition_state.version = build_info.GetPartitionBuildProp( 238*9e94795aSAndroid Build Coastguard Worker 'ro.build.date.utc', partition) 239*9e94795aSAndroid Build Coastguard Worker 240*9e94795aSAndroid Build Coastguard Worker # TODO(xunchang), we can save a call to ComputeRuntimeBuildInfos. 241*9e94795aSAndroid Build Coastguard Worker build_devices, build_fingerprints = \ 242*9e94795aSAndroid Build Coastguard Worker CalculateRuntimeDevicesAndFingerprints(build_info, boot_variable_values) 243*9e94795aSAndroid Build Coastguard Worker device_state.device.extend(sorted(build_devices)) 244*9e94795aSAndroid Build Coastguard Worker device_state.build.extend(sorted(build_fingerprints)) 245*9e94795aSAndroid Build Coastguard Worker device_state.build_incremental = build_info.GetBuildProp( 246*9e94795aSAndroid Build Coastguard Worker 'ro.build.version.incremental') 247*9e94795aSAndroid Build Coastguard Worker 248*9e94795aSAndroid Build Coastguard Worker UpdatePartitionStates(device_state.partition_state) 249*9e94795aSAndroid Build Coastguard Worker 250*9e94795aSAndroid Build Coastguard Worker if is_post_build: 251*9e94795aSAndroid Build Coastguard Worker device_state.sdk_level = build_info.GetBuildProp( 252*9e94795aSAndroid Build Coastguard Worker 'ro.build.version.sdk') 253*9e94795aSAndroid Build Coastguard Worker device_state.security_patch_level = build_info.GetBuildProp( 254*9e94795aSAndroid Build Coastguard Worker 'ro.build.version.security_patch') 255*9e94795aSAndroid Build Coastguard Worker # Use the actual post-timestamp, even for a downgrade case. 256*9e94795aSAndroid Build Coastguard Worker device_state.timestamp = int(build_info.GetBuildProp('ro.build.date.utc')) 257*9e94795aSAndroid Build Coastguard Worker 258*9e94795aSAndroid Build Coastguard Worker 259*9e94795aSAndroid Build Coastguard Workerdef GetPackageMetadata(target_info, source_info=None): 260*9e94795aSAndroid Build Coastguard Worker """Generates and returns the metadata proto. 261*9e94795aSAndroid Build Coastguard Worker 262*9e94795aSAndroid Build Coastguard Worker It generates a ota_metadata protobuf that contains the info to be written 263*9e94795aSAndroid Build Coastguard Worker into an OTA package (META-INF/com/android/metadata.pb). It also handles the 264*9e94795aSAndroid Build Coastguard Worker detection of downgrade / data wipe based on the global options. 265*9e94795aSAndroid Build Coastguard Worker 266*9e94795aSAndroid Build Coastguard Worker Args: 267*9e94795aSAndroid Build Coastguard Worker target_info: The BuildInfo instance that holds the target build info. 268*9e94795aSAndroid Build Coastguard Worker source_info: The BuildInfo instance that holds the source build info, or 269*9e94795aSAndroid Build Coastguard Worker None if generating full OTA. 270*9e94795aSAndroid Build Coastguard Worker 271*9e94795aSAndroid Build Coastguard Worker Returns: 272*9e94795aSAndroid Build Coastguard Worker A protobuf to be written into package metadata entry. 273*9e94795aSAndroid Build Coastguard Worker """ 274*9e94795aSAndroid Build Coastguard Worker assert isinstance(target_info, BuildInfo) 275*9e94795aSAndroid Build Coastguard Worker assert source_info is None or isinstance(source_info, BuildInfo) 276*9e94795aSAndroid Build Coastguard Worker 277*9e94795aSAndroid Build Coastguard Worker boot_variable_values = {} 278*9e94795aSAndroid Build Coastguard Worker if OPTIONS.boot_variable_file: 279*9e94795aSAndroid Build Coastguard Worker d = LoadDictionaryFromFile(OPTIONS.boot_variable_file) 280*9e94795aSAndroid Build Coastguard Worker for key, values in d.items(): 281*9e94795aSAndroid Build Coastguard Worker boot_variable_values[key] = [val.strip() for val in values.split(',')] 282*9e94795aSAndroid Build Coastguard Worker 283*9e94795aSAndroid Build Coastguard Worker metadata_proto = ota_metadata_pb2.OtaMetadata() 284*9e94795aSAndroid Build Coastguard Worker # TODO(xunchang) some fields, e.g. post-device isn't necessary. We can 285*9e94795aSAndroid Build Coastguard Worker # consider skipping them if they aren't used by clients. 286*9e94795aSAndroid Build Coastguard Worker UpdateDeviceState(metadata_proto.postcondition, target_info, 287*9e94795aSAndroid Build Coastguard Worker boot_variable_values, True) 288*9e94795aSAndroid Build Coastguard Worker 289*9e94795aSAndroid Build Coastguard Worker if target_info.is_ab and not OPTIONS.force_non_ab: 290*9e94795aSAndroid Build Coastguard Worker metadata_proto.type = ota_metadata_pb2.OtaMetadata.AB 291*9e94795aSAndroid Build Coastguard Worker metadata_proto.required_cache = 0 292*9e94795aSAndroid Build Coastguard Worker else: 293*9e94795aSAndroid Build Coastguard Worker metadata_proto.type = ota_metadata_pb2.OtaMetadata.BLOCK 294*9e94795aSAndroid Build Coastguard Worker # cache requirement will be updated by the non-A/B codes. 295*9e94795aSAndroid Build Coastguard Worker 296*9e94795aSAndroid Build Coastguard Worker if OPTIONS.wipe_user_data: 297*9e94795aSAndroid Build Coastguard Worker metadata_proto.wipe = True 298*9e94795aSAndroid Build Coastguard Worker 299*9e94795aSAndroid Build Coastguard Worker if OPTIONS.retrofit_dynamic_partitions: 300*9e94795aSAndroid Build Coastguard Worker metadata_proto.retrofit_dynamic_partitions = True 301*9e94795aSAndroid Build Coastguard Worker 302*9e94795aSAndroid Build Coastguard Worker is_incremental = source_info is not None 303*9e94795aSAndroid Build Coastguard Worker if is_incremental: 304*9e94795aSAndroid Build Coastguard Worker UpdateDeviceState(metadata_proto.precondition, source_info, 305*9e94795aSAndroid Build Coastguard Worker boot_variable_values, False) 306*9e94795aSAndroid Build Coastguard Worker else: 307*9e94795aSAndroid Build Coastguard Worker metadata_proto.precondition.device.extend( 308*9e94795aSAndroid Build Coastguard Worker metadata_proto.postcondition.device) 309*9e94795aSAndroid Build Coastguard Worker 310*9e94795aSAndroid Build Coastguard Worker # Detect downgrades and set up downgrade flags accordingly. 311*9e94795aSAndroid Build Coastguard Worker if is_incremental: 312*9e94795aSAndroid Build Coastguard Worker HandleDowngradeMetadata(metadata_proto, target_info, source_info) 313*9e94795aSAndroid Build Coastguard Worker 314*9e94795aSAndroid Build Coastguard Worker return metadata_proto 315*9e94795aSAndroid Build Coastguard Worker 316*9e94795aSAndroid Build Coastguard Worker 317*9e94795aSAndroid Build Coastguard Workerdef BuildLegacyOtaMetadata(metadata_proto): 318*9e94795aSAndroid Build Coastguard Worker """Converts the metadata proto to a legacy metadata dict. 319*9e94795aSAndroid Build Coastguard Worker 320*9e94795aSAndroid Build Coastguard Worker This metadata dict is used to build the legacy metadata text file for 321*9e94795aSAndroid Build Coastguard Worker backward compatibility. We won't add new keys to the legacy metadata format. 322*9e94795aSAndroid Build Coastguard Worker If new information is needed, we should add it as a new field in OtaMetadata 323*9e94795aSAndroid Build Coastguard Worker proto definition. 324*9e94795aSAndroid Build Coastguard Worker """ 325*9e94795aSAndroid Build Coastguard Worker 326*9e94795aSAndroid Build Coastguard Worker separator = '|' 327*9e94795aSAndroid Build Coastguard Worker 328*9e94795aSAndroid Build Coastguard Worker metadata_dict = {} 329*9e94795aSAndroid Build Coastguard Worker if metadata_proto.type == ota_metadata_pb2.OtaMetadata.AB: 330*9e94795aSAndroid Build Coastguard Worker metadata_dict['ota-type'] = 'AB' 331*9e94795aSAndroid Build Coastguard Worker elif metadata_proto.type == ota_metadata_pb2.OtaMetadata.BLOCK: 332*9e94795aSAndroid Build Coastguard Worker metadata_dict['ota-type'] = 'BLOCK' 333*9e94795aSAndroid Build Coastguard Worker if metadata_proto.wipe: 334*9e94795aSAndroid Build Coastguard Worker metadata_dict['ota-wipe'] = 'yes' 335*9e94795aSAndroid Build Coastguard Worker if metadata_proto.retrofit_dynamic_partitions: 336*9e94795aSAndroid Build Coastguard Worker metadata_dict['ota-retrofit-dynamic-partitions'] = 'yes' 337*9e94795aSAndroid Build Coastguard Worker if metadata_proto.downgrade: 338*9e94795aSAndroid Build Coastguard Worker metadata_dict['ota-downgrade'] = 'yes' 339*9e94795aSAndroid Build Coastguard Worker 340*9e94795aSAndroid Build Coastguard Worker metadata_dict['ota-required-cache'] = str(metadata_proto.required_cache) 341*9e94795aSAndroid Build Coastguard Worker 342*9e94795aSAndroid Build Coastguard Worker post_build = metadata_proto.postcondition 343*9e94795aSAndroid Build Coastguard Worker metadata_dict['post-build'] = separator.join(post_build.build) 344*9e94795aSAndroid Build Coastguard Worker metadata_dict['post-build-incremental'] = post_build.build_incremental 345*9e94795aSAndroid Build Coastguard Worker metadata_dict['post-sdk-level'] = post_build.sdk_level 346*9e94795aSAndroid Build Coastguard Worker metadata_dict['post-security-patch-level'] = post_build.security_patch_level 347*9e94795aSAndroid Build Coastguard Worker metadata_dict['post-timestamp'] = str(post_build.timestamp) 348*9e94795aSAndroid Build Coastguard Worker 349*9e94795aSAndroid Build Coastguard Worker pre_build = metadata_proto.precondition 350*9e94795aSAndroid Build Coastguard Worker metadata_dict['pre-device'] = separator.join(pre_build.device) 351*9e94795aSAndroid Build Coastguard Worker # incremental updates 352*9e94795aSAndroid Build Coastguard Worker if len(pre_build.build) != 0: 353*9e94795aSAndroid Build Coastguard Worker metadata_dict['pre-build'] = separator.join(pre_build.build) 354*9e94795aSAndroid Build Coastguard Worker metadata_dict['pre-build-incremental'] = pre_build.build_incremental 355*9e94795aSAndroid Build Coastguard Worker 356*9e94795aSAndroid Build Coastguard Worker if metadata_proto.spl_downgrade: 357*9e94795aSAndroid Build Coastguard Worker metadata_dict['spl-downgrade'] = 'yes' 358*9e94795aSAndroid Build Coastguard Worker metadata_dict.update(metadata_proto.property_files) 359*9e94795aSAndroid Build Coastguard Worker 360*9e94795aSAndroid Build Coastguard Worker return metadata_dict 361*9e94795aSAndroid Build Coastguard Worker 362*9e94795aSAndroid Build Coastguard Worker 363*9e94795aSAndroid Build Coastguard Workerdef HandleDowngradeMetadata(metadata_proto, target_info, source_info): 364*9e94795aSAndroid Build Coastguard Worker # Only incremental OTAs are allowed to reach here. 365*9e94795aSAndroid Build Coastguard Worker assert OPTIONS.incremental_source is not None 366*9e94795aSAndroid Build Coastguard Worker 367*9e94795aSAndroid Build Coastguard Worker # used for logging upon errors 368*9e94795aSAndroid Build Coastguard Worker log_downgrades = [] 369*9e94795aSAndroid Build Coastguard Worker log_upgrades = [] 370*9e94795aSAndroid Build Coastguard Worker 371*9e94795aSAndroid Build Coastguard Worker post_timestamp = target_info.GetBuildProp("ro.build.date.utc") 372*9e94795aSAndroid Build Coastguard Worker pre_timestamp = source_info.GetBuildProp("ro.build.date.utc") 373*9e94795aSAndroid Build Coastguard Worker if int(post_timestamp) < int(pre_timestamp): 374*9e94795aSAndroid Build Coastguard Worker logger.info(f"ro.build.date.utc pre timestamp: {pre_timestamp}, " 375*9e94795aSAndroid Build Coastguard Worker f"post timestamp: {post_timestamp}. Downgrade detected.") 376*9e94795aSAndroid Build Coastguard Worker log_downgrades.append(f"ro.build.date.utc pre: {pre_timestamp} post: {post_timestamp}") 377*9e94795aSAndroid Build Coastguard Worker else: 378*9e94795aSAndroid Build Coastguard Worker logger.info(f"ro.build.date.utc pre timestamp: {pre_timestamp}, " 379*9e94795aSAndroid Build Coastguard Worker f"post timestamp: {post_timestamp}.") 380*9e94795aSAndroid Build Coastguard Worker log_upgrades.append(f"ro.build.date.utc pre: {pre_timestamp} post: {post_timestamp}") 381*9e94795aSAndroid Build Coastguard Worker 382*9e94795aSAndroid Build Coastguard Worker # When merging system and vendor target files, it is not enough 383*9e94795aSAndroid Build Coastguard Worker # to check ro.build.date.utc, the timestamp for each partition must 384*9e94795aSAndroid Build Coastguard Worker # be checked. 385*9e94795aSAndroid Build Coastguard Worker if source_info.is_ab: 386*9e94795aSAndroid Build Coastguard Worker ab_partitions = set(source_info.get("ab_partitions")) 387*9e94795aSAndroid Build Coastguard Worker for partition in sorted(set(PARTITIONS_WITH_BUILD_PROP) & ab_partitions): 388*9e94795aSAndroid Build Coastguard Worker 389*9e94795aSAndroid Build Coastguard Worker partition_prop = source_info.get('{}.build.prop'.format(partition)) 390*9e94795aSAndroid Build Coastguard Worker # Skip if the partition is missing, or it doesn't have a build.prop 391*9e94795aSAndroid Build Coastguard Worker if not partition_prop or not partition_prop.build_props: 392*9e94795aSAndroid Build Coastguard Worker continue 393*9e94795aSAndroid Build Coastguard Worker partition_prop = target_info.get('{}.build.prop'.format(partition)) 394*9e94795aSAndroid Build Coastguard Worker # Skip if the partition is missing, or it doesn't have a build.prop 395*9e94795aSAndroid Build Coastguard Worker if not partition_prop or not partition_prop.build_props: 396*9e94795aSAndroid Build Coastguard Worker continue 397*9e94795aSAndroid Build Coastguard Worker 398*9e94795aSAndroid Build Coastguard Worker post_timestamp = target_info.GetPartitionBuildProp( 399*9e94795aSAndroid Build Coastguard Worker 'ro.build.date.utc', partition) 400*9e94795aSAndroid Build Coastguard Worker pre_timestamp = source_info.GetPartitionBuildProp( 401*9e94795aSAndroid Build Coastguard Worker 'ro.build.date.utc', partition) 402*9e94795aSAndroid Build Coastguard Worker if int(post_timestamp) < int(pre_timestamp): 403*9e94795aSAndroid Build Coastguard Worker logger.info(f"Partition {partition} pre timestamp: {pre_timestamp}, " 404*9e94795aSAndroid Build Coastguard Worker f"post time: {post_timestamp}. Downgrade detected.") 405*9e94795aSAndroid Build Coastguard Worker log_downgrades.append(f"{partition} pre: {pre_timestamp} post: {post_timestamp}") 406*9e94795aSAndroid Build Coastguard Worker else: 407*9e94795aSAndroid Build Coastguard Worker logger.info(f"Partition {partition} pre timestamp: {pre_timestamp}, " 408*9e94795aSAndroid Build Coastguard Worker f"post timestamp: {post_timestamp}.") 409*9e94795aSAndroid Build Coastguard Worker log_upgrades.append(f"{partition} pre: {pre_timestamp} post: {post_timestamp}") 410*9e94795aSAndroid Build Coastguard Worker 411*9e94795aSAndroid Build Coastguard Worker if OPTIONS.spl_downgrade: 412*9e94795aSAndroid Build Coastguard Worker metadata_proto.spl_downgrade = True 413*9e94795aSAndroid Build Coastguard Worker 414*9e94795aSAndroid Build Coastguard Worker if OPTIONS.downgrade: 415*9e94795aSAndroid Build Coastguard Worker if len(log_downgrades) == 0: 416*9e94795aSAndroid Build Coastguard Worker raise RuntimeError( 417*9e94795aSAndroid Build Coastguard Worker "--downgrade or --override_timestamp specified but no downgrade " 418*9e94795aSAndroid Build Coastguard Worker "detected. Current values for ro.build.date.utc: " + ', '.join(log_upgrades)) 419*9e94795aSAndroid Build Coastguard Worker metadata_proto.downgrade = True 420*9e94795aSAndroid Build Coastguard Worker else: 421*9e94795aSAndroid Build Coastguard Worker if len(log_downgrades) != 0: 422*9e94795aSAndroid Build Coastguard Worker raise RuntimeError( 423*9e94795aSAndroid Build Coastguard Worker "Downgrade detected based on timestamp check in ro.build.date.utc. " 424*9e94795aSAndroid Build Coastguard Worker "Need to specify --override_timestamp OR --downgrade to allow " 425*9e94795aSAndroid Build Coastguard Worker "building the incremental. Downgrades detected for: " 426*9e94795aSAndroid Build Coastguard Worker + ', '.join(log_downgrades)) 427*9e94795aSAndroid Build Coastguard Worker 428*9e94795aSAndroid Build Coastguard Workerdef ComputeRuntimeBuildInfos(default_build_info, boot_variable_values): 429*9e94795aSAndroid Build Coastguard Worker """Returns a set of build info objects that may exist during runtime.""" 430*9e94795aSAndroid Build Coastguard Worker 431*9e94795aSAndroid Build Coastguard Worker build_info_set = {default_build_info} 432*9e94795aSAndroid Build Coastguard Worker if not boot_variable_values: 433*9e94795aSAndroid Build Coastguard Worker return build_info_set 434*9e94795aSAndroid Build Coastguard Worker 435*9e94795aSAndroid Build Coastguard Worker # Calculate all possible combinations of the values for the boot variables. 436*9e94795aSAndroid Build Coastguard Worker keys = boot_variable_values.keys() 437*9e94795aSAndroid Build Coastguard Worker value_list = boot_variable_values.values() 438*9e94795aSAndroid Build Coastguard Worker combinations = [dict(zip(keys, values)) 439*9e94795aSAndroid Build Coastguard Worker for values in itertools.product(*value_list)] 440*9e94795aSAndroid Build Coastguard Worker for placeholder_values in combinations: 441*9e94795aSAndroid Build Coastguard Worker # Reload the info_dict as some build properties may change their values 442*9e94795aSAndroid Build Coastguard Worker # based on the value of ro.boot* properties. 443*9e94795aSAndroid Build Coastguard Worker info_dict = copy.deepcopy(default_build_info.info_dict) 444*9e94795aSAndroid Build Coastguard Worker for partition in PARTITIONS_WITH_BUILD_PROP: 445*9e94795aSAndroid Build Coastguard Worker partition_prop_key = "{}.build.prop".format(partition) 446*9e94795aSAndroid Build Coastguard Worker input_file = info_dict[partition_prop_key].input_file 447*9e94795aSAndroid Build Coastguard Worker ramdisk = GetRamdiskFormat(info_dict) 448*9e94795aSAndroid Build Coastguard Worker if isinstance(input_file, zipfile.ZipFile): 449*9e94795aSAndroid Build Coastguard Worker with zipfile.ZipFile(input_file.filename, allowZip64=True) as input_zip: 450*9e94795aSAndroid Build Coastguard Worker info_dict[partition_prop_key] = \ 451*9e94795aSAndroid Build Coastguard Worker PartitionBuildProps.FromInputFile(input_zip, partition, 452*9e94795aSAndroid Build Coastguard Worker placeholder_values, 453*9e94795aSAndroid Build Coastguard Worker ramdisk) 454*9e94795aSAndroid Build Coastguard Worker else: 455*9e94795aSAndroid Build Coastguard Worker info_dict[partition_prop_key] = \ 456*9e94795aSAndroid Build Coastguard Worker PartitionBuildProps.FromInputFile(input_file, partition, 457*9e94795aSAndroid Build Coastguard Worker placeholder_values, 458*9e94795aSAndroid Build Coastguard Worker ramdisk) 459*9e94795aSAndroid Build Coastguard Worker info_dict["build.prop"] = info_dict["system.build.prop"] 460*9e94795aSAndroid Build Coastguard Worker build_info_set.add(BuildInfo(info_dict, default_build_info.oem_dicts)) 461*9e94795aSAndroid Build Coastguard Worker 462*9e94795aSAndroid Build Coastguard Worker return build_info_set 463*9e94795aSAndroid Build Coastguard Worker 464*9e94795aSAndroid Build Coastguard Worker 465*9e94795aSAndroid Build Coastguard Workerdef CalculateRuntimeDevicesAndFingerprints(default_build_info, 466*9e94795aSAndroid Build Coastguard Worker boot_variable_values): 467*9e94795aSAndroid Build Coastguard Worker """Returns a tuple of sets for runtime devices and fingerprints""" 468*9e94795aSAndroid Build Coastguard Worker 469*9e94795aSAndroid Build Coastguard Worker device_names = set() 470*9e94795aSAndroid Build Coastguard Worker fingerprints = set() 471*9e94795aSAndroid Build Coastguard Worker build_info_set = ComputeRuntimeBuildInfos(default_build_info, 472*9e94795aSAndroid Build Coastguard Worker boot_variable_values) 473*9e94795aSAndroid Build Coastguard Worker for runtime_build_info in build_info_set: 474*9e94795aSAndroid Build Coastguard Worker device_names.add(runtime_build_info.device) 475*9e94795aSAndroid Build Coastguard Worker fingerprints.add(runtime_build_info.fingerprint) 476*9e94795aSAndroid Build Coastguard Worker return device_names, fingerprints 477*9e94795aSAndroid Build Coastguard Worker 478*9e94795aSAndroid Build Coastguard Worker 479*9e94795aSAndroid Build Coastguard Workerdef GetZipEntryOffset(zfp, entry_info): 480*9e94795aSAndroid Build Coastguard Worker """Get offset to a beginning of a particular zip entry 481*9e94795aSAndroid Build Coastguard Worker Args: 482*9e94795aSAndroid Build Coastguard Worker fp: zipfile.ZipFile 483*9e94795aSAndroid Build Coastguard Worker entry_info: zipfile.ZipInfo 484*9e94795aSAndroid Build Coastguard Worker 485*9e94795aSAndroid Build Coastguard Worker Returns: 486*9e94795aSAndroid Build Coastguard Worker (offset, size) tuple 487*9e94795aSAndroid Build Coastguard Worker """ 488*9e94795aSAndroid Build Coastguard Worker # Don't use len(entry_info.extra). Because that returns size of extra 489*9e94795aSAndroid Build Coastguard Worker # fields in central directory. We need to look at local file directory, 490*9e94795aSAndroid Build Coastguard Worker # as these two might have different sizes. 491*9e94795aSAndroid Build Coastguard Worker 492*9e94795aSAndroid Build Coastguard Worker # We cannot work with zipfile.ZipFile instances, we need a |fp| for the underlying file. 493*9e94795aSAndroid Build Coastguard Worker zfp = zfp.fp 494*9e94795aSAndroid Build Coastguard Worker zfp.seek(entry_info.header_offset) 495*9e94795aSAndroid Build Coastguard Worker data = zfp.read(zipfile.sizeFileHeader) 496*9e94795aSAndroid Build Coastguard Worker fheader = struct.unpack(zipfile.structFileHeader, data) 497*9e94795aSAndroid Build Coastguard Worker # Last two fields of local file header are filename length and 498*9e94795aSAndroid Build Coastguard Worker # extra length 499*9e94795aSAndroid Build Coastguard Worker filename_len = fheader[-2] 500*9e94795aSAndroid Build Coastguard Worker extra_len = fheader[-1] 501*9e94795aSAndroid Build Coastguard Worker offset = entry_info.header_offset 502*9e94795aSAndroid Build Coastguard Worker offset += zipfile.sizeFileHeader 503*9e94795aSAndroid Build Coastguard Worker offset += filename_len + extra_len 504*9e94795aSAndroid Build Coastguard Worker size = entry_info.file_size 505*9e94795aSAndroid Build Coastguard Worker return (offset, size) 506*9e94795aSAndroid Build Coastguard Worker 507*9e94795aSAndroid Build Coastguard Worker 508*9e94795aSAndroid Build Coastguard Workerclass PropertyFiles(object): 509*9e94795aSAndroid Build Coastguard Worker """A class that computes the property-files string for an OTA package. 510*9e94795aSAndroid Build Coastguard Worker 511*9e94795aSAndroid Build Coastguard Worker A property-files string is a comma-separated string that contains the 512*9e94795aSAndroid Build Coastguard Worker offset/size info for an OTA package. The entries, which must be ZIP_STORED, 513*9e94795aSAndroid Build Coastguard Worker can be fetched directly with the package URL along with the offset/size info. 514*9e94795aSAndroid Build Coastguard Worker These strings can be used for streaming A/B OTAs, or allowing an updater to 515*9e94795aSAndroid Build Coastguard Worker download package metadata entry directly, without paying the cost of 516*9e94795aSAndroid Build Coastguard Worker downloading entire package. 517*9e94795aSAndroid Build Coastguard Worker 518*9e94795aSAndroid Build Coastguard Worker Computing the final property-files string requires two passes. Because doing 519*9e94795aSAndroid Build Coastguard Worker the whole package signing (with signapk.jar) will possibly reorder the ZIP 520*9e94795aSAndroid Build Coastguard Worker entries, which may in turn invalidate earlier computed ZIP entry offset/size 521*9e94795aSAndroid Build Coastguard Worker values. 522*9e94795aSAndroid Build Coastguard Worker 523*9e94795aSAndroid Build Coastguard Worker This class provides functions to be called for each pass. The general flow is 524*9e94795aSAndroid Build Coastguard Worker as follows. 525*9e94795aSAndroid Build Coastguard Worker 526*9e94795aSAndroid Build Coastguard Worker property_files = PropertyFiles() 527*9e94795aSAndroid Build Coastguard Worker # The first pass, which writes placeholders before doing initial signing. 528*9e94795aSAndroid Build Coastguard Worker property_files.Compute() 529*9e94795aSAndroid Build Coastguard Worker SignOutput() 530*9e94795aSAndroid Build Coastguard Worker 531*9e94795aSAndroid Build Coastguard Worker # The second pass, by replacing the placeholders with actual data. 532*9e94795aSAndroid Build Coastguard Worker property_files.Finalize() 533*9e94795aSAndroid Build Coastguard Worker SignOutput() 534*9e94795aSAndroid Build Coastguard Worker 535*9e94795aSAndroid Build Coastguard Worker And the caller can additionally verify the final result. 536*9e94795aSAndroid Build Coastguard Worker 537*9e94795aSAndroid Build Coastguard Worker property_files.Verify() 538*9e94795aSAndroid Build Coastguard Worker """ 539*9e94795aSAndroid Build Coastguard Worker 540*9e94795aSAndroid Build Coastguard Worker def __init__(self): 541*9e94795aSAndroid Build Coastguard Worker self.name = None 542*9e94795aSAndroid Build Coastguard Worker self.required = () 543*9e94795aSAndroid Build Coastguard Worker self.optional = () 544*9e94795aSAndroid Build Coastguard Worker 545*9e94795aSAndroid Build Coastguard Worker def Compute(self, input_zip): 546*9e94795aSAndroid Build Coastguard Worker """Computes and returns a property-files string with placeholders. 547*9e94795aSAndroid Build Coastguard Worker 548*9e94795aSAndroid Build Coastguard Worker We reserve extra space for the offset and size of the metadata entry itself, 549*9e94795aSAndroid Build Coastguard Worker although we don't know the final values until the package gets signed. 550*9e94795aSAndroid Build Coastguard Worker 551*9e94795aSAndroid Build Coastguard Worker Args: 552*9e94795aSAndroid Build Coastguard Worker input_zip: The input ZIP file. 553*9e94795aSAndroid Build Coastguard Worker 554*9e94795aSAndroid Build Coastguard Worker Returns: 555*9e94795aSAndroid Build Coastguard Worker A string with placeholders for the metadata offset/size info, e.g. 556*9e94795aSAndroid Build Coastguard Worker "payload.bin:679:343,payload_properties.txt:378:45,metadata: ". 557*9e94795aSAndroid Build Coastguard Worker """ 558*9e94795aSAndroid Build Coastguard Worker return self.GetPropertyFilesString(input_zip, reserve_space=True) 559*9e94795aSAndroid Build Coastguard Worker 560*9e94795aSAndroid Build Coastguard Worker class InsufficientSpaceException(Exception): 561*9e94795aSAndroid Build Coastguard Worker pass 562*9e94795aSAndroid Build Coastguard Worker 563*9e94795aSAndroid Build Coastguard Worker def Finalize(self, input_zip, reserved_length): 564*9e94795aSAndroid Build Coastguard Worker """Finalizes a property-files string with actual METADATA offset/size info. 565*9e94795aSAndroid Build Coastguard Worker 566*9e94795aSAndroid Build Coastguard Worker The input ZIP file has been signed, with the ZIP entries in the desired 567*9e94795aSAndroid Build Coastguard Worker place (signapk.jar will possibly reorder the ZIP entries). Now we compute 568*9e94795aSAndroid Build Coastguard Worker the ZIP entry offsets and construct the property-files string with actual 569*9e94795aSAndroid Build Coastguard Worker data. Note that during this process, we must pad the property-files string 570*9e94795aSAndroid Build Coastguard Worker to the reserved length, so that the METADATA entry size remains the same. 571*9e94795aSAndroid Build Coastguard Worker Otherwise the entries' offsets and sizes may change again. 572*9e94795aSAndroid Build Coastguard Worker 573*9e94795aSAndroid Build Coastguard Worker Args: 574*9e94795aSAndroid Build Coastguard Worker input_zip: The input ZIP file. 575*9e94795aSAndroid Build Coastguard Worker reserved_length: The reserved length of the property-files string during 576*9e94795aSAndroid Build Coastguard Worker the call to Compute(). The final string must be no more than this 577*9e94795aSAndroid Build Coastguard Worker size. 578*9e94795aSAndroid Build Coastguard Worker 579*9e94795aSAndroid Build Coastguard Worker Returns: 580*9e94795aSAndroid Build Coastguard Worker A property-files string including the metadata offset/size info, e.g. 581*9e94795aSAndroid Build Coastguard Worker "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379 ". 582*9e94795aSAndroid Build Coastguard Worker 583*9e94795aSAndroid Build Coastguard Worker Raises: 584*9e94795aSAndroid Build Coastguard Worker InsufficientSpaceException: If the reserved length is insufficient to hold 585*9e94795aSAndroid Build Coastguard Worker the final string. 586*9e94795aSAndroid Build Coastguard Worker """ 587*9e94795aSAndroid Build Coastguard Worker result = self.GetPropertyFilesString(input_zip, reserve_space=False) 588*9e94795aSAndroid Build Coastguard Worker if len(result) > reserved_length: 589*9e94795aSAndroid Build Coastguard Worker raise self.InsufficientSpaceException( 590*9e94795aSAndroid Build Coastguard Worker 'Insufficient reserved space: reserved={}, actual={}'.format( 591*9e94795aSAndroid Build Coastguard Worker reserved_length, len(result))) 592*9e94795aSAndroid Build Coastguard Worker 593*9e94795aSAndroid Build Coastguard Worker result += ' ' * (reserved_length - len(result)) 594*9e94795aSAndroid Build Coastguard Worker return result 595*9e94795aSAndroid Build Coastguard Worker 596*9e94795aSAndroid Build Coastguard Worker def Verify(self, input_zip, expected): 597*9e94795aSAndroid Build Coastguard Worker """Verifies the input ZIP file contains the expected property-files string. 598*9e94795aSAndroid Build Coastguard Worker 599*9e94795aSAndroid Build Coastguard Worker Args: 600*9e94795aSAndroid Build Coastguard Worker input_zip: The input ZIP file. 601*9e94795aSAndroid Build Coastguard Worker expected: The property-files string that's computed from Finalize(). 602*9e94795aSAndroid Build Coastguard Worker 603*9e94795aSAndroid Build Coastguard Worker Raises: 604*9e94795aSAndroid Build Coastguard Worker AssertionError: On finding a mismatch. 605*9e94795aSAndroid Build Coastguard Worker """ 606*9e94795aSAndroid Build Coastguard Worker actual = self.GetPropertyFilesString(input_zip) 607*9e94795aSAndroid Build Coastguard Worker assert actual == expected, \ 608*9e94795aSAndroid Build Coastguard Worker "Mismatching streaming metadata: {} vs {}.".format(actual, expected) 609*9e94795aSAndroid Build Coastguard Worker 610*9e94795aSAndroid Build Coastguard Worker def GetPropertyFilesString(self, zip_file, reserve_space=False): 611*9e94795aSAndroid Build Coastguard Worker """ 612*9e94795aSAndroid Build Coastguard Worker Constructs the property-files string per request. 613*9e94795aSAndroid Build Coastguard Worker 614*9e94795aSAndroid Build Coastguard Worker Args: 615*9e94795aSAndroid Build Coastguard Worker zip_file: The input ZIP file. 616*9e94795aSAndroid Build Coastguard Worker reserved_length: The reserved length of the property-files string. 617*9e94795aSAndroid Build Coastguard Worker 618*9e94795aSAndroid Build Coastguard Worker Returns: 619*9e94795aSAndroid Build Coastguard Worker A property-files string including the metadata offset/size info, e.g. 620*9e94795aSAndroid Build Coastguard Worker "payload.bin:679:343,payload_properties.txt:378:45,metadata: ". 621*9e94795aSAndroid Build Coastguard Worker """ 622*9e94795aSAndroid Build Coastguard Worker 623*9e94795aSAndroid Build Coastguard Worker def ComputeEntryOffsetSize(name): 624*9e94795aSAndroid Build Coastguard Worker """Computes the zip entry offset and size.""" 625*9e94795aSAndroid Build Coastguard Worker info = zip_file.getinfo(name) 626*9e94795aSAndroid Build Coastguard Worker (offset, size) = GetZipEntryOffset(zip_file, info) 627*9e94795aSAndroid Build Coastguard Worker return '%s:%d:%d' % (os.path.basename(name), offset, size) 628*9e94795aSAndroid Build Coastguard Worker 629*9e94795aSAndroid Build Coastguard Worker tokens = [] 630*9e94795aSAndroid Build Coastguard Worker tokens.extend(self._GetPrecomputed(zip_file)) 631*9e94795aSAndroid Build Coastguard Worker for entry in self.required: 632*9e94795aSAndroid Build Coastguard Worker tokens.append(ComputeEntryOffsetSize(entry)) 633*9e94795aSAndroid Build Coastguard Worker for entry in self.optional: 634*9e94795aSAndroid Build Coastguard Worker if entry in zip_file.namelist(): 635*9e94795aSAndroid Build Coastguard Worker tokens.append(ComputeEntryOffsetSize(entry)) 636*9e94795aSAndroid Build Coastguard Worker 637*9e94795aSAndroid Build Coastguard Worker # 'META-INF/com/android/metadata' is required. We don't know its actual 638*9e94795aSAndroid Build Coastguard Worker # offset and length (as well as the values for other entries). So we reserve 639*9e94795aSAndroid Build Coastguard Worker # 15-byte as a placeholder ('offset:length'), which is sufficient to cover 640*9e94795aSAndroid Build Coastguard Worker # the space for metadata entry. Because 'offset' allows a max of 10-digit 641*9e94795aSAndroid Build Coastguard Worker # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the 642*9e94795aSAndroid Build Coastguard Worker # reserved space serves the metadata entry only. 643*9e94795aSAndroid Build Coastguard Worker if reserve_space: 644*9e94795aSAndroid Build Coastguard Worker tokens.append('metadata:' + ' ' * 15) 645*9e94795aSAndroid Build Coastguard Worker tokens.append('metadata.pb:' + ' ' * 15) 646*9e94795aSAndroid Build Coastguard Worker else: 647*9e94795aSAndroid Build Coastguard Worker tokens.append(ComputeEntryOffsetSize(METADATA_NAME)) 648*9e94795aSAndroid Build Coastguard Worker if METADATA_PROTO_NAME in zip_file.namelist(): 649*9e94795aSAndroid Build Coastguard Worker tokens.append(ComputeEntryOffsetSize(METADATA_PROTO_NAME)) 650*9e94795aSAndroid Build Coastguard Worker 651*9e94795aSAndroid Build Coastguard Worker return ','.join(tokens) 652*9e94795aSAndroid Build Coastguard Worker 653*9e94795aSAndroid Build Coastguard Worker def _GetPrecomputed(self, input_zip): 654*9e94795aSAndroid Build Coastguard Worker """Computes the additional tokens to be included into the property-files. 655*9e94795aSAndroid Build Coastguard Worker 656*9e94795aSAndroid Build Coastguard Worker This applies to tokens without actual ZIP entries, such as 657*9e94795aSAndroid Build Coastguard Worker payload_metadata.bin. We want to expose the offset/size to updaters, so 658*9e94795aSAndroid Build Coastguard Worker that they can download the payload metadata directly with the info. 659*9e94795aSAndroid Build Coastguard Worker 660*9e94795aSAndroid Build Coastguard Worker Args: 661*9e94795aSAndroid Build Coastguard Worker input_zip: The input zip file. 662*9e94795aSAndroid Build Coastguard Worker 663*9e94795aSAndroid Build Coastguard Worker Returns: 664*9e94795aSAndroid Build Coastguard Worker A list of strings (tokens) to be added to the property-files string. 665*9e94795aSAndroid Build Coastguard Worker """ 666*9e94795aSAndroid Build Coastguard Worker # pylint: disable=no-self-use 667*9e94795aSAndroid Build Coastguard Worker # pylint: disable=unused-argument 668*9e94795aSAndroid Build Coastguard Worker return [] 669*9e94795aSAndroid Build Coastguard Worker 670*9e94795aSAndroid Build Coastguard Worker 671*9e94795aSAndroid Build Coastguard Workerdef SignOutput(temp_zip_name, output_zip_name, package_key=None, pw=None): 672*9e94795aSAndroid Build Coastguard Worker if package_key is None: 673*9e94795aSAndroid Build Coastguard Worker package_key = OPTIONS.package_key 674*9e94795aSAndroid Build Coastguard Worker if pw is None and OPTIONS.key_passwords: 675*9e94795aSAndroid Build Coastguard Worker pw = OPTIONS.key_passwords[package_key] 676*9e94795aSAndroid Build Coastguard Worker 677*9e94795aSAndroid Build Coastguard Worker SignFile(temp_zip_name, output_zip_name, package_key, pw, 678*9e94795aSAndroid Build Coastguard Worker whole_file=True) 679*9e94795aSAndroid Build Coastguard Worker 680*9e94795aSAndroid Build Coastguard Worker 681*9e94795aSAndroid Build Coastguard Workerdef ConstructOtaApexInfo(target_zip, source_file=None): 682*9e94795aSAndroid Build Coastguard Worker """If applicable, add the source version to the apex info.""" 683*9e94795aSAndroid Build Coastguard Worker 684*9e94795aSAndroid Build Coastguard Worker def _ReadApexInfo(input_zip): 685*9e94795aSAndroid Build Coastguard Worker if not DoesInputFileContain(input_zip, "META/apex_info.pb"): 686*9e94795aSAndroid Build Coastguard Worker logger.warning("target_file doesn't contain apex_info.pb %s", input_zip) 687*9e94795aSAndroid Build Coastguard Worker return None 688*9e94795aSAndroid Build Coastguard Worker return ReadBytesFromInputFile(input_zip, "META/apex_info.pb") 689*9e94795aSAndroid Build Coastguard Worker 690*9e94795aSAndroid Build Coastguard Worker target_apex_string = _ReadApexInfo(target_zip) 691*9e94795aSAndroid Build Coastguard Worker # Return early if the target apex info doesn't exist or is empty. 692*9e94795aSAndroid Build Coastguard Worker if not target_apex_string: 693*9e94795aSAndroid Build Coastguard Worker return target_apex_string 694*9e94795aSAndroid Build Coastguard Worker 695*9e94795aSAndroid Build Coastguard Worker # If the source apex info isn't available, just return the target info 696*9e94795aSAndroid Build Coastguard Worker if not source_file: 697*9e94795aSAndroid Build Coastguard Worker return target_apex_string 698*9e94795aSAndroid Build Coastguard Worker 699*9e94795aSAndroid Build Coastguard Worker source_apex_string = _ReadApexInfo(source_file) 700*9e94795aSAndroid Build Coastguard Worker if not source_apex_string: 701*9e94795aSAndroid Build Coastguard Worker return target_apex_string 702*9e94795aSAndroid Build Coastguard Worker 703*9e94795aSAndroid Build Coastguard Worker source_apex_proto = ota_metadata_pb2.ApexMetadata() 704*9e94795aSAndroid Build Coastguard Worker source_apex_proto.ParseFromString(source_apex_string) 705*9e94795aSAndroid Build Coastguard Worker source_apex_versions = {apex.package_name: apex.version for apex in 706*9e94795aSAndroid Build Coastguard Worker source_apex_proto.apex_info} 707*9e94795aSAndroid Build Coastguard Worker 708*9e94795aSAndroid Build Coastguard Worker # If the apex package is available in the source build, initialize the source 709*9e94795aSAndroid Build Coastguard Worker # apex version. 710*9e94795aSAndroid Build Coastguard Worker target_apex_proto = ota_metadata_pb2.ApexMetadata() 711*9e94795aSAndroid Build Coastguard Worker target_apex_proto.ParseFromString(target_apex_string) 712*9e94795aSAndroid Build Coastguard Worker for target_apex in target_apex_proto.apex_info: 713*9e94795aSAndroid Build Coastguard Worker name = target_apex.package_name 714*9e94795aSAndroid Build Coastguard Worker if name in source_apex_versions: 715*9e94795aSAndroid Build Coastguard Worker target_apex.source_version = source_apex_versions[name] 716*9e94795aSAndroid Build Coastguard Worker 717*9e94795aSAndroid Build Coastguard Worker return target_apex_proto.SerializeToString() 718*9e94795aSAndroid Build Coastguard Worker 719*9e94795aSAndroid Build Coastguard Worker 720*9e94795aSAndroid Build Coastguard Workerdef IsLz4diffCompatible(source_file: str, target_file: str): 721*9e94795aSAndroid Build Coastguard Worker """Check whether lz4diff versions in two builds are compatible 722*9e94795aSAndroid Build Coastguard Worker 723*9e94795aSAndroid Build Coastguard Worker Args: 724*9e94795aSAndroid Build Coastguard Worker source_file: Path to source build's target_file.zip 725*9e94795aSAndroid Build Coastguard Worker target_file: Path to target build's target_file.zip 726*9e94795aSAndroid Build Coastguard Worker 727*9e94795aSAndroid Build Coastguard Worker Returns: 728*9e94795aSAndroid Build Coastguard Worker bool true if and only if lz4diff versions are compatible 729*9e94795aSAndroid Build Coastguard Worker """ 730*9e94795aSAndroid Build Coastguard Worker if source_file is None or target_file is None: 731*9e94795aSAndroid Build Coastguard Worker return False 732*9e94795aSAndroid Build Coastguard Worker # Right now we enable lz4diff as long as source build has liblz4.so. 733*9e94795aSAndroid Build Coastguard Worker # In the future we might introduce version system to lz4diff as well. 734*9e94795aSAndroid Build Coastguard Worker if zipfile.is_zipfile(source_file): 735*9e94795aSAndroid Build Coastguard Worker with zipfile.ZipFile(source_file, "r") as zfp: 736*9e94795aSAndroid Build Coastguard Worker return "META/liblz4.so" in zfp.namelist() 737*9e94795aSAndroid Build Coastguard Worker else: 738*9e94795aSAndroid Build Coastguard Worker assert os.path.isdir(source_file) 739*9e94795aSAndroid Build Coastguard Worker return os.path.exists(os.path.join(source_file, "META", "liblz4.so")) 740*9e94795aSAndroid Build Coastguard Worker 741*9e94795aSAndroid Build Coastguard Worker 742*9e94795aSAndroid Build Coastguard Workerdef IsZucchiniCompatible(source_file: str, target_file: str): 743*9e94795aSAndroid Build Coastguard Worker """Check whether zucchini versions in two builds are compatible 744*9e94795aSAndroid Build Coastguard Worker 745*9e94795aSAndroid Build Coastguard Worker Args: 746*9e94795aSAndroid Build Coastguard Worker source_file: Path to source build's target_file.zip 747*9e94795aSAndroid Build Coastguard Worker target_file: Path to target build's target_file.zip 748*9e94795aSAndroid Build Coastguard Worker 749*9e94795aSAndroid Build Coastguard Worker Returns: 750*9e94795aSAndroid Build Coastguard Worker bool true if and only if zucchini versions are compatible 751*9e94795aSAndroid Build Coastguard Worker """ 752*9e94795aSAndroid Build Coastguard Worker if source_file is None or target_file is None: 753*9e94795aSAndroid Build Coastguard Worker return False 754*9e94795aSAndroid Build Coastguard Worker assert os.path.exists(source_file) 755*9e94795aSAndroid Build Coastguard Worker assert os.path.exists(target_file) 756*9e94795aSAndroid Build Coastguard Worker 757*9e94795aSAndroid Build Coastguard Worker assert zipfile.is_zipfile(source_file) or os.path.isdir(source_file) 758*9e94795aSAndroid Build Coastguard Worker assert zipfile.is_zipfile(target_file) or os.path.isdir(target_file) 759*9e94795aSAndroid Build Coastguard Worker _ZUCCHINI_CONFIG_ENTRY_NAME = "META/zucchini_config.txt" 760*9e94795aSAndroid Build Coastguard Worker 761*9e94795aSAndroid Build Coastguard Worker def ReadEntry(path, entry): 762*9e94795aSAndroid Build Coastguard Worker # Read an entry inside a .zip file or extracted dir of .zip file 763*9e94795aSAndroid Build Coastguard Worker if zipfile.is_zipfile(path): 764*9e94795aSAndroid Build Coastguard Worker with zipfile.ZipFile(path, "r", allowZip64=True) as zfp: 765*9e94795aSAndroid Build Coastguard Worker if entry in zfp.namelist(): 766*9e94795aSAndroid Build Coastguard Worker return zfp.read(entry).decode() 767*9e94795aSAndroid Build Coastguard Worker else: 768*9e94795aSAndroid Build Coastguard Worker entry_path = os.path.join(path, entry) 769*9e94795aSAndroid Build Coastguard Worker if os.path.exists(entry_path): 770*9e94795aSAndroid Build Coastguard Worker with open(entry_path, "r") as fp: 771*9e94795aSAndroid Build Coastguard Worker return fp.read() 772*9e94795aSAndroid Build Coastguard Worker return False 773*9e94795aSAndroid Build Coastguard Worker sourceEntry = ReadEntry(source_file, _ZUCCHINI_CONFIG_ENTRY_NAME) 774*9e94795aSAndroid Build Coastguard Worker targetEntry = ReadEntry(target_file, _ZUCCHINI_CONFIG_ENTRY_NAME) 775*9e94795aSAndroid Build Coastguard Worker return sourceEntry and targetEntry and sourceEntry == targetEntry 776*9e94795aSAndroid Build Coastguard Worker 777*9e94795aSAndroid Build Coastguard Worker 778*9e94795aSAndroid Build Coastguard Workerdef ExtractTargetFiles(path: str): 779*9e94795aSAndroid Build Coastguard Worker if os.path.isdir(path): 780*9e94795aSAndroid Build Coastguard Worker logger.info("target files %s is already extracted", path) 781*9e94795aSAndroid Build Coastguard Worker return path 782*9e94795aSAndroid Build Coastguard Worker extracted_dir = common.MakeTempDir("target_files") 783*9e94795aSAndroid Build Coastguard Worker logger.info(f"Extracting target files {path} to {extracted_dir}") 784*9e94795aSAndroid Build Coastguard Worker common.UnzipToDir(path, extracted_dir, UNZIP_PATTERN + [""]) 785*9e94795aSAndroid Build Coastguard Worker for subdir in TARGET_FILES_IMAGES_SUBDIR: 786*9e94795aSAndroid Build Coastguard Worker image_dir = os.path.join(extracted_dir, subdir) 787*9e94795aSAndroid Build Coastguard Worker if not os.path.exists(image_dir): 788*9e94795aSAndroid Build Coastguard Worker continue 789*9e94795aSAndroid Build Coastguard Worker for filename in os.listdir(image_dir): 790*9e94795aSAndroid Build Coastguard Worker if not filename.endswith(".img"): 791*9e94795aSAndroid Build Coastguard Worker continue 792*9e94795aSAndroid Build Coastguard Worker common.UnsparseImage(os.path.join(image_dir, filename)) 793*9e94795aSAndroid Build Coastguard Worker 794*9e94795aSAndroid Build Coastguard Worker return extracted_dir 795*9e94795aSAndroid Build Coastguard Worker 796*9e94795aSAndroid Build Coastguard Worker 797*9e94795aSAndroid Build Coastguard Workerdef LocatePartitionPath(target_files_dir: str, partition: str, allow_empty): 798*9e94795aSAndroid Build Coastguard Worker for subdir in TARGET_FILES_IMAGES_SUBDIR: 799*9e94795aSAndroid Build Coastguard Worker path = os.path.join(target_files_dir, subdir, partition + ".img") 800*9e94795aSAndroid Build Coastguard Worker if os.path.exists(path): 801*9e94795aSAndroid Build Coastguard Worker return path 802*9e94795aSAndroid Build Coastguard Worker if allow_empty: 803*9e94795aSAndroid Build Coastguard Worker return "" 804*9e94795aSAndroid Build Coastguard Worker raise common.ExternalError( 805*9e94795aSAndroid Build Coastguard Worker "Partition {} not found in target files {}".format(partition, target_files_dir)) 806*9e94795aSAndroid Build Coastguard Worker 807*9e94795aSAndroid Build Coastguard Worker 808*9e94795aSAndroid Build Coastguard Workerdef GetPartitionImages(target_files_dir: str, ab_partitions, allow_empty=True): 809*9e94795aSAndroid Build Coastguard Worker assert os.path.isdir(target_files_dir) 810*9e94795aSAndroid Build Coastguard Worker return ":".join([LocatePartitionPath(target_files_dir, partition, allow_empty) for partition in ab_partitions]) 811*9e94795aSAndroid Build Coastguard Worker 812*9e94795aSAndroid Build Coastguard Worker 813*9e94795aSAndroid Build Coastguard Workerdef LocatePartitionMap(target_files_dir: str, partition: str): 814*9e94795aSAndroid Build Coastguard Worker for subdir in TARGET_FILES_IMAGES_SUBDIR: 815*9e94795aSAndroid Build Coastguard Worker path = os.path.join(target_files_dir, subdir, partition + ".map") 816*9e94795aSAndroid Build Coastguard Worker if os.path.exists(path): 817*9e94795aSAndroid Build Coastguard Worker return path 818*9e94795aSAndroid Build Coastguard Worker return "" 819*9e94795aSAndroid Build Coastguard Worker 820*9e94795aSAndroid Build Coastguard Worker 821*9e94795aSAndroid Build Coastguard Workerdef GetPartitionMaps(target_files_dir: str, ab_partitions): 822*9e94795aSAndroid Build Coastguard Worker assert os.path.isdir(target_files_dir) 823*9e94795aSAndroid Build Coastguard Worker return ":".join([LocatePartitionMap(target_files_dir, partition) for partition in ab_partitions]) 824*9e94795aSAndroid Build Coastguard Worker 825*9e94795aSAndroid Build Coastguard Worker 826*9e94795aSAndroid Build Coastguard Workerclass PayloadGenerator(object): 827*9e94795aSAndroid Build Coastguard Worker """Manages the creation and the signing of an A/B OTA Payload.""" 828*9e94795aSAndroid Build Coastguard Worker 829*9e94795aSAndroid Build Coastguard Worker PAYLOAD_BIN = payload_signer.PAYLOAD_BIN 830*9e94795aSAndroid Build Coastguard Worker PAYLOAD_PROPERTIES_TXT = payload_signer.PAYLOAD_PROPERTIES_TXT 831*9e94795aSAndroid Build Coastguard Worker SECONDARY_PAYLOAD_BIN = 'secondary/payload.bin' 832*9e94795aSAndroid Build Coastguard Worker SECONDARY_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt' 833*9e94795aSAndroid Build Coastguard Worker 834*9e94795aSAndroid Build Coastguard Worker def __init__(self, secondary=False, wipe_user_data=False, minor_version=None, is_partial_update=False, spl_downgrade=False): 835*9e94795aSAndroid Build Coastguard Worker """Initializes a Payload instance. 836*9e94795aSAndroid Build Coastguard Worker 837*9e94795aSAndroid Build Coastguard Worker Args: 838*9e94795aSAndroid Build Coastguard Worker secondary: Whether it's generating a secondary payload (default: False). 839*9e94795aSAndroid Build Coastguard Worker """ 840*9e94795aSAndroid Build Coastguard Worker self.payload_file = None 841*9e94795aSAndroid Build Coastguard Worker self.payload_properties = None 842*9e94795aSAndroid Build Coastguard Worker self.secondary = secondary 843*9e94795aSAndroid Build Coastguard Worker self.wipe_user_data = wipe_user_data 844*9e94795aSAndroid Build Coastguard Worker self.minor_version = minor_version 845*9e94795aSAndroid Build Coastguard Worker self.is_partial_update = is_partial_update 846*9e94795aSAndroid Build Coastguard Worker self.spl_downgrade = spl_downgrade 847*9e94795aSAndroid Build Coastguard Worker 848*9e94795aSAndroid Build Coastguard Worker def _Run(self, cmd, **kwargs): # pylint: disable=no-self-use 849*9e94795aSAndroid Build Coastguard Worker # Don't pipe (buffer) the output if verbose is set. Let 850*9e94795aSAndroid Build Coastguard Worker # brillo_update_payload write to stdout/stderr directly, so its progress can 851*9e94795aSAndroid Build Coastguard Worker # be monitored. 852*9e94795aSAndroid Build Coastguard Worker if OPTIONS.verbose: 853*9e94795aSAndroid Build Coastguard Worker common.RunAndCheckOutput(cmd, stdout=None, stderr=None, **kwargs) 854*9e94795aSAndroid Build Coastguard Worker else: 855*9e94795aSAndroid Build Coastguard Worker common.RunAndCheckOutput(cmd, **kwargs) 856*9e94795aSAndroid Build Coastguard Worker 857*9e94795aSAndroid Build Coastguard Worker def Generate(self, target_file, source_file=None, additional_args=None, **kwargs): 858*9e94795aSAndroid Build Coastguard Worker """Generates a payload from the given target-files zip(s). 859*9e94795aSAndroid Build Coastguard Worker 860*9e94795aSAndroid Build Coastguard Worker Args: 861*9e94795aSAndroid Build Coastguard Worker target_file: The filename of the target build target-files zip. 862*9e94795aSAndroid Build Coastguard Worker source_file: The filename of the source build target-files zip; or None if 863*9e94795aSAndroid Build Coastguard Worker generating a full OTA. 864*9e94795aSAndroid Build Coastguard Worker additional_args: A list of additional args that should be passed to 865*9e94795aSAndroid Build Coastguard Worker delta_generator binary; or None. 866*9e94795aSAndroid Build Coastguard Worker kwargs: Any additional args to pass to subprocess.Popen 867*9e94795aSAndroid Build Coastguard Worker """ 868*9e94795aSAndroid Build Coastguard Worker if additional_args is None: 869*9e94795aSAndroid Build Coastguard Worker additional_args = [] 870*9e94795aSAndroid Build Coastguard Worker 871*9e94795aSAndroid Build Coastguard Worker payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin") 872*9e94795aSAndroid Build Coastguard Worker target_dir = ExtractTargetFiles(target_file) 873*9e94795aSAndroid Build Coastguard Worker cmd = ["delta_generator", 874*9e94795aSAndroid Build Coastguard Worker "--out_file", payload_file] 875*9e94795aSAndroid Build Coastguard Worker with open(os.path.join(target_dir, "META", "ab_partitions.txt"), "r") as fp: 876*9e94795aSAndroid Build Coastguard Worker ab_partitions = fp.read().strip().splitlines() 877*9e94795aSAndroid Build Coastguard Worker cmd.extend(["--partition_names", ":".join(ab_partitions)]) 878*9e94795aSAndroid Build Coastguard Worker cmd.extend( 879*9e94795aSAndroid Build Coastguard Worker ["--new_partitions", GetPartitionImages(target_dir, ab_partitions, False)]) 880*9e94795aSAndroid Build Coastguard Worker cmd.extend( 881*9e94795aSAndroid Build Coastguard Worker ["--new_mapfiles", GetPartitionMaps(target_dir, ab_partitions)]) 882*9e94795aSAndroid Build Coastguard Worker if source_file is not None: 883*9e94795aSAndroid Build Coastguard Worker source_dir = ExtractTargetFiles(source_file) 884*9e94795aSAndroid Build Coastguard Worker cmd.extend( 885*9e94795aSAndroid Build Coastguard Worker ["--old_partitions", GetPartitionImages(source_dir, ab_partitions, True)]) 886*9e94795aSAndroid Build Coastguard Worker cmd.extend( 887*9e94795aSAndroid Build Coastguard Worker ["--old_mapfiles", GetPartitionMaps(source_dir, ab_partitions)]) 888*9e94795aSAndroid Build Coastguard Worker 889*9e94795aSAndroid Build Coastguard Worker if OPTIONS.disable_fec_computation: 890*9e94795aSAndroid Build Coastguard Worker cmd.extend(["--disable_fec_computation=true"]) 891*9e94795aSAndroid Build Coastguard Worker if OPTIONS.disable_verity_computation: 892*9e94795aSAndroid Build Coastguard Worker cmd.extend(["--disable_verity_computation=true"]) 893*9e94795aSAndroid Build Coastguard Worker postinstall_config = os.path.join( 894*9e94795aSAndroid Build Coastguard Worker target_dir, "META", "postinstall_config.txt") 895*9e94795aSAndroid Build Coastguard Worker 896*9e94795aSAndroid Build Coastguard Worker if os.path.exists(postinstall_config): 897*9e94795aSAndroid Build Coastguard Worker cmd.extend(["--new_postinstall_config_file", postinstall_config]) 898*9e94795aSAndroid Build Coastguard Worker dynamic_partition_info = os.path.join( 899*9e94795aSAndroid Build Coastguard Worker target_dir, "META", "dynamic_partitions_info.txt") 900*9e94795aSAndroid Build Coastguard Worker 901*9e94795aSAndroid Build Coastguard Worker if os.path.exists(dynamic_partition_info): 902*9e94795aSAndroid Build Coastguard Worker cmd.extend(["--dynamic_partition_info_file", dynamic_partition_info]) 903*9e94795aSAndroid Build Coastguard Worker 904*9e94795aSAndroid Build Coastguard Worker apex_info = os.path.join( 905*9e94795aSAndroid Build Coastguard Worker target_dir, "META", "apex_info.pb") 906*9e94795aSAndroid Build Coastguard Worker if os.path.exists(apex_info): 907*9e94795aSAndroid Build Coastguard Worker cmd.extend(["--apex_info_file", apex_info]) 908*9e94795aSAndroid Build Coastguard Worker 909*9e94795aSAndroid Build Coastguard Worker major_version, minor_version = ParseUpdateEngineConfig( 910*9e94795aSAndroid Build Coastguard Worker os.path.join(target_dir, "META", "update_engine_config.txt")) 911*9e94795aSAndroid Build Coastguard Worker if source_file: 912*9e94795aSAndroid Build Coastguard Worker major_version, minor_version = ParseUpdateEngineConfig( 913*9e94795aSAndroid Build Coastguard Worker os.path.join(source_dir, "META", "update_engine_config.txt")) 914*9e94795aSAndroid Build Coastguard Worker if self.minor_version: 915*9e94795aSAndroid Build Coastguard Worker minor_version = self.minor_version 916*9e94795aSAndroid Build Coastguard Worker cmd.extend(["--major_version", str(major_version)]) 917*9e94795aSAndroid Build Coastguard Worker if source_file is not None or self.is_partial_update: 918*9e94795aSAndroid Build Coastguard Worker cmd.extend(["--minor_version", str(minor_version)]) 919*9e94795aSAndroid Build Coastguard Worker if self.is_partial_update: 920*9e94795aSAndroid Build Coastguard Worker cmd.extend(["--is_partial_update=true"]) 921*9e94795aSAndroid Build Coastguard Worker cmd.extend(additional_args) 922*9e94795aSAndroid Build Coastguard Worker self._Run(cmd, **kwargs) 923*9e94795aSAndroid Build Coastguard Worker 924*9e94795aSAndroid Build Coastguard Worker self.payload_file = payload_file 925*9e94795aSAndroid Build Coastguard Worker self.payload_properties = None 926*9e94795aSAndroid Build Coastguard Worker 927*9e94795aSAndroid Build Coastguard Worker def Sign(self, payload_signer): 928*9e94795aSAndroid Build Coastguard Worker """Generates and signs the hashes of the payload and metadata. 929*9e94795aSAndroid Build Coastguard Worker 930*9e94795aSAndroid Build Coastguard Worker Args: 931*9e94795aSAndroid Build Coastguard Worker payload_signer: A PayloadSigner() instance that serves the signing work. 932*9e94795aSAndroid Build Coastguard Worker 933*9e94795aSAndroid Build Coastguard Worker Raises: 934*9e94795aSAndroid Build Coastguard Worker AssertionError: On any failure when calling brillo_update_payload script. 935*9e94795aSAndroid Build Coastguard Worker """ 936*9e94795aSAndroid Build Coastguard Worker assert isinstance(payload_signer, PayloadSigner) 937*9e94795aSAndroid Build Coastguard Worker 938*9e94795aSAndroid Build Coastguard Worker signed_payload_file = payload_signer.SignPayload(self.payload_file) 939*9e94795aSAndroid Build Coastguard Worker 940*9e94795aSAndroid Build Coastguard Worker self.payload_file = signed_payload_file 941*9e94795aSAndroid Build Coastguard Worker 942*9e94795aSAndroid Build Coastguard Worker def WriteToZip(self, output_zip): 943*9e94795aSAndroid Build Coastguard Worker """Writes the payload to the given zip. 944*9e94795aSAndroid Build Coastguard Worker 945*9e94795aSAndroid Build Coastguard Worker Args: 946*9e94795aSAndroid Build Coastguard Worker output_zip: The output ZipFile instance. 947*9e94795aSAndroid Build Coastguard Worker """ 948*9e94795aSAndroid Build Coastguard Worker assert self.payload_file is not None 949*9e94795aSAndroid Build Coastguard Worker # 4. Dump the signed payload properties. 950*9e94795aSAndroid Build Coastguard Worker properties_file = GeneratePayloadProperties(self.payload_file) 951*9e94795aSAndroid Build Coastguard Worker 952*9e94795aSAndroid Build Coastguard Worker 953*9e94795aSAndroid Build Coastguard Worker with open(properties_file, "a") as f: 954*9e94795aSAndroid Build Coastguard Worker if self.wipe_user_data: 955*9e94795aSAndroid Build Coastguard Worker f.write("POWERWASH=1\n") 956*9e94795aSAndroid Build Coastguard Worker if self.secondary: 957*9e94795aSAndroid Build Coastguard Worker f.write("SWITCH_SLOT_ON_REBOOT=0\n") 958*9e94795aSAndroid Build Coastguard Worker if self.spl_downgrade: 959*9e94795aSAndroid Build Coastguard Worker f.write("SPL_DOWNGRADE=1\n") 960*9e94795aSAndroid Build Coastguard Worker 961*9e94795aSAndroid Build Coastguard Worker 962*9e94795aSAndroid Build Coastguard Worker self.payload_properties = properties_file 963*9e94795aSAndroid Build Coastguard Worker 964*9e94795aSAndroid Build Coastguard Worker if self.secondary: 965*9e94795aSAndroid Build Coastguard Worker payload_arcname = PayloadGenerator.SECONDARY_PAYLOAD_BIN 966*9e94795aSAndroid Build Coastguard Worker payload_properties_arcname = PayloadGenerator.SECONDARY_PAYLOAD_PROPERTIES_TXT 967*9e94795aSAndroid Build Coastguard Worker else: 968*9e94795aSAndroid Build Coastguard Worker payload_arcname = PayloadGenerator.PAYLOAD_BIN 969*9e94795aSAndroid Build Coastguard Worker payload_properties_arcname = PayloadGenerator.PAYLOAD_PROPERTIES_TXT 970*9e94795aSAndroid Build Coastguard Worker 971*9e94795aSAndroid Build Coastguard Worker # Add the signed payload file and properties into the zip. In order to 972*9e94795aSAndroid Build Coastguard Worker # support streaming, we pack them as ZIP_STORED. So these entries can be 973*9e94795aSAndroid Build Coastguard Worker # read directly with the offset and length pairs. 974*9e94795aSAndroid Build Coastguard Worker common.ZipWrite(output_zip, self.payload_file, arcname=payload_arcname, 975*9e94795aSAndroid Build Coastguard Worker compress_type=zipfile.ZIP_STORED) 976*9e94795aSAndroid Build Coastguard Worker common.ZipWrite(output_zip, self.payload_properties, 977*9e94795aSAndroid Build Coastguard Worker arcname=payload_properties_arcname, 978*9e94795aSAndroid Build Coastguard Worker compress_type=zipfile.ZIP_STORED) 979*9e94795aSAndroid Build Coastguard Worker 980*9e94795aSAndroid Build Coastguard Worker 981*9e94795aSAndroid Build Coastguard Workerclass StreamingPropertyFiles(PropertyFiles): 982*9e94795aSAndroid Build Coastguard Worker """A subclass for computing the property-files for streaming A/B OTAs.""" 983*9e94795aSAndroid Build Coastguard Worker 984*9e94795aSAndroid Build Coastguard Worker def __init__(self): 985*9e94795aSAndroid Build Coastguard Worker super(StreamingPropertyFiles, self).__init__() 986*9e94795aSAndroid Build Coastguard Worker self.name = 'ota-streaming-property-files' 987*9e94795aSAndroid Build Coastguard Worker self.required = ( 988*9e94795aSAndroid Build Coastguard Worker # payload.bin and payload_properties.txt must exist. 989*9e94795aSAndroid Build Coastguard Worker 'payload.bin', 990*9e94795aSAndroid Build Coastguard Worker 'payload_properties.txt', 991*9e94795aSAndroid Build Coastguard Worker ) 992*9e94795aSAndroid Build Coastguard Worker self.optional = ( 993*9e94795aSAndroid Build Coastguard Worker # apex_info.pb isn't directly used in the update flow 994*9e94795aSAndroid Build Coastguard Worker 'apex_info.pb', 995*9e94795aSAndroid Build Coastguard Worker # care_map is available only if dm-verity is enabled. 996*9e94795aSAndroid Build Coastguard Worker 'care_map.pb', 997*9e94795aSAndroid Build Coastguard Worker 'care_map.txt', 998*9e94795aSAndroid Build Coastguard Worker # compatibility.zip is available only if target supports Treble. 999*9e94795aSAndroid Build Coastguard Worker 'compatibility.zip', 1000*9e94795aSAndroid Build Coastguard Worker ) 1001*9e94795aSAndroid Build Coastguard Worker 1002*9e94795aSAndroid Build Coastguard Worker 1003*9e94795aSAndroid Build Coastguard Workerclass AbOtaPropertyFiles(StreamingPropertyFiles): 1004*9e94795aSAndroid Build Coastguard Worker """The property-files for A/B OTA that includes payload_metadata.bin info. 1005*9e94795aSAndroid Build Coastguard Worker 1006*9e94795aSAndroid Build Coastguard Worker Since P, we expose one more token (aka property-file), in addition to the ones 1007*9e94795aSAndroid Build Coastguard Worker for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'. 1008*9e94795aSAndroid Build Coastguard Worker 'payload_metadata.bin' is the header part of a payload ('payload.bin'), which 1009*9e94795aSAndroid Build Coastguard Worker doesn't exist as a separate ZIP entry, but can be used to verify if the 1010*9e94795aSAndroid Build Coastguard Worker payload can be applied on the given device. 1011*9e94795aSAndroid Build Coastguard Worker 1012*9e94795aSAndroid Build Coastguard Worker For backward compatibility, we keep both of the 'ota-streaming-property-files' 1013*9e94795aSAndroid Build Coastguard Worker and the newly added 'ota-property-files' in P. The new token will only be 1014*9e94795aSAndroid Build Coastguard Worker available in 'ota-property-files'. 1015*9e94795aSAndroid Build Coastguard Worker """ 1016*9e94795aSAndroid Build Coastguard Worker 1017*9e94795aSAndroid Build Coastguard Worker def __init__(self): 1018*9e94795aSAndroid Build Coastguard Worker super(AbOtaPropertyFiles, self).__init__() 1019*9e94795aSAndroid Build Coastguard Worker self.name = 'ota-property-files' 1020*9e94795aSAndroid Build Coastguard Worker 1021*9e94795aSAndroid Build Coastguard Worker def _GetPrecomputed(self, input_zip): 1022*9e94795aSAndroid Build Coastguard Worker offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip) 1023*9e94795aSAndroid Build Coastguard Worker return ['payload_metadata.bin:{}:{}'.format(offset, size)] 1024*9e94795aSAndroid Build Coastguard Worker 1025*9e94795aSAndroid Build Coastguard Worker @staticmethod 1026*9e94795aSAndroid Build Coastguard Worker def _GetPayloadMetadataOffsetAndSize(input_zip): 1027*9e94795aSAndroid Build Coastguard Worker """Computes the offset and size of the payload metadata for a given package. 1028*9e94795aSAndroid Build Coastguard Worker 1029*9e94795aSAndroid Build Coastguard Worker (From system/update_engine/update_metadata.proto) 1030*9e94795aSAndroid Build Coastguard Worker A delta update file contains all the deltas needed to update a system from 1031*9e94795aSAndroid Build Coastguard Worker one specific version to another specific version. The update format is 1032*9e94795aSAndroid Build Coastguard Worker represented by this struct pseudocode: 1033*9e94795aSAndroid Build Coastguard Worker 1034*9e94795aSAndroid Build Coastguard Worker struct delta_update_file { 1035*9e94795aSAndroid Build Coastguard Worker char magic[4] = "CrAU"; 1036*9e94795aSAndroid Build Coastguard Worker uint64 file_format_version; 1037*9e94795aSAndroid Build Coastguard Worker uint64 manifest_size; // Size of protobuf DeltaArchiveManifest 1038*9e94795aSAndroid Build Coastguard Worker 1039*9e94795aSAndroid Build Coastguard Worker // Only present if format_version > 1: 1040*9e94795aSAndroid Build Coastguard Worker uint32 metadata_signature_size; 1041*9e94795aSAndroid Build Coastguard Worker 1042*9e94795aSAndroid Build Coastguard Worker // The Bzip2 compressed DeltaArchiveManifest 1043*9e94795aSAndroid Build Coastguard Worker char manifest[metadata_signature_size]; 1044*9e94795aSAndroid Build Coastguard Worker 1045*9e94795aSAndroid Build Coastguard Worker // The signature of the metadata (from the beginning of the payload up to 1046*9e94795aSAndroid Build Coastguard Worker // this location, not including the signature itself). This is a 1047*9e94795aSAndroid Build Coastguard Worker // serialized Signatures message. 1048*9e94795aSAndroid Build Coastguard Worker char medatada_signature_message[metadata_signature_size]; 1049*9e94795aSAndroid Build Coastguard Worker 1050*9e94795aSAndroid Build Coastguard Worker // Data blobs for files, no specific format. The specific offset 1051*9e94795aSAndroid Build Coastguard Worker // and length of each data blob is recorded in the DeltaArchiveManifest. 1052*9e94795aSAndroid Build Coastguard Worker struct { 1053*9e94795aSAndroid Build Coastguard Worker char data[]; 1054*9e94795aSAndroid Build Coastguard Worker } blobs[]; 1055*9e94795aSAndroid Build Coastguard Worker 1056*9e94795aSAndroid Build Coastguard Worker // These two are not signed: 1057*9e94795aSAndroid Build Coastguard Worker uint64 payload_signatures_message_size; 1058*9e94795aSAndroid Build Coastguard Worker char payload_signatures_message[]; 1059*9e94795aSAndroid Build Coastguard Worker }; 1060*9e94795aSAndroid Build Coastguard Worker 1061*9e94795aSAndroid Build Coastguard Worker 'payload-metadata.bin' contains all the bytes from the beginning of the 1062*9e94795aSAndroid Build Coastguard Worker payload, till the end of 'medatada_signature_message'. 1063*9e94795aSAndroid Build Coastguard Worker """ 1064*9e94795aSAndroid Build Coastguard Worker payload_info = input_zip.getinfo('payload.bin') 1065*9e94795aSAndroid Build Coastguard Worker (payload_offset, payload_size) = GetZipEntryOffset(input_zip, payload_info) 1066*9e94795aSAndroid Build Coastguard Worker 1067*9e94795aSAndroid Build Coastguard Worker # Read the underlying raw zipfile at specified offset 1068*9e94795aSAndroid Build Coastguard Worker payload_fp = input_zip.fp 1069*9e94795aSAndroid Build Coastguard Worker payload_fp.seek(payload_offset) 1070*9e94795aSAndroid Build Coastguard Worker header_bin = payload_fp.read(24) 1071*9e94795aSAndroid Build Coastguard Worker 1072*9e94795aSAndroid Build Coastguard Worker # network byte order (big-endian) 1073*9e94795aSAndroid Build Coastguard Worker header = struct.unpack("!IQQL", header_bin) 1074*9e94795aSAndroid Build Coastguard Worker 1075*9e94795aSAndroid Build Coastguard Worker # 'CrAU' 1076*9e94795aSAndroid Build Coastguard Worker magic = header[0] 1077*9e94795aSAndroid Build Coastguard Worker assert magic == 0x43724155, "Invalid magic: {:x}, computed offset {}" \ 1078*9e94795aSAndroid Build Coastguard Worker .format(magic, payload_offset) 1079*9e94795aSAndroid Build Coastguard Worker 1080*9e94795aSAndroid Build Coastguard Worker manifest_size = header[2] 1081*9e94795aSAndroid Build Coastguard Worker metadata_signature_size = header[3] 1082*9e94795aSAndroid Build Coastguard Worker metadata_total = 24 + manifest_size + metadata_signature_size 1083*9e94795aSAndroid Build Coastguard Worker assert metadata_total <= payload_size 1084*9e94795aSAndroid Build Coastguard Worker 1085*9e94795aSAndroid Build Coastguard Worker return (payload_offset, metadata_total) 1086*9e94795aSAndroid Build Coastguard Worker 1087*9e94795aSAndroid Build Coastguard Worker 1088*9e94795aSAndroid Build Coastguard Workerdef Fnmatch(filename, pattersn): 1089*9e94795aSAndroid Build Coastguard Worker return any([fnmatch.fnmatch(filename, pat) for pat in pattersn]) 1090*9e94795aSAndroid Build Coastguard Worker 1091*9e94795aSAndroid Build Coastguard Worker 1092*9e94795aSAndroid Build Coastguard Workerdef CopyTargetFilesDir(input_dir): 1093*9e94795aSAndroid Build Coastguard Worker output_dir = common.MakeTempDir("target_files") 1094*9e94795aSAndroid Build Coastguard Worker 1095*9e94795aSAndroid Build Coastguard Worker def SymlinkIfNotSparse(src, dst): 1096*9e94795aSAndroid Build Coastguard Worker if common.IsSparseImage(src): 1097*9e94795aSAndroid Build Coastguard Worker return common.UnsparseImage(src, dst) 1098*9e94795aSAndroid Build Coastguard Worker else: 1099*9e94795aSAndroid Build Coastguard Worker return os.symlink(os.path.realpath(src), dst) 1100*9e94795aSAndroid Build Coastguard Worker 1101*9e94795aSAndroid Build Coastguard Worker for subdir in TARGET_FILES_IMAGES_SUBDIR: 1102*9e94795aSAndroid Build Coastguard Worker if not os.path.exists(os.path.join(input_dir, subdir)): 1103*9e94795aSAndroid Build Coastguard Worker continue 1104*9e94795aSAndroid Build Coastguard Worker shutil.copytree(os.path.join(input_dir, subdir), os.path.join( 1105*9e94795aSAndroid Build Coastguard Worker output_dir, subdir), dirs_exist_ok=True, copy_function=SymlinkIfNotSparse) 1106*9e94795aSAndroid Build Coastguard Worker shutil.copytree(os.path.join(input_dir, "META"), os.path.join( 1107*9e94795aSAndroid Build Coastguard Worker output_dir, "META"), dirs_exist_ok=True) 1108*9e94795aSAndroid Build Coastguard Worker 1109*9e94795aSAndroid Build Coastguard Worker for (dirpath, _, filenames) in os.walk(input_dir): 1110*9e94795aSAndroid Build Coastguard Worker for filename in filenames: 1111*9e94795aSAndroid Build Coastguard Worker path = os.path.join(dirpath, filename) 1112*9e94795aSAndroid Build Coastguard Worker relative_path = path.removeprefix(input_dir).removeprefix("/") 1113*9e94795aSAndroid Build Coastguard Worker if not Fnmatch(relative_path, UNZIP_PATTERN): 1114*9e94795aSAndroid Build Coastguard Worker continue 1115*9e94795aSAndroid Build Coastguard Worker target_path = os.path.join( 1116*9e94795aSAndroid Build Coastguard Worker output_dir, relative_path) 1117*9e94795aSAndroid Build Coastguard Worker if os.path.exists(target_path): 1118*9e94795aSAndroid Build Coastguard Worker continue 1119*9e94795aSAndroid Build Coastguard Worker os.makedirs(os.path.dirname(target_path), exist_ok=True) 1120*9e94795aSAndroid Build Coastguard Worker shutil.copy(path, target_path) 1121*9e94795aSAndroid Build Coastguard Worker return output_dir 1122