1#!/usr/bin/env python 2# 3# Copyright (C) 2008 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17""" 18Signs all the APK files in a target-files zipfile, producing a new 19target-files zip. 20 21Usage: sign_target_files_apks [flags] input_target_files output_target_files 22 23 -e (--extra_apks) <name,name,...=key> 24 Add extra APK/APEX name/key pairs as though they appeared in apkcerts.txt 25 or apexkeys.txt (so mappings specified by -k and -d are applied). Keys 26 specified in -e override any value for that app contained in the 27 apkcerts.txt file, or the container key for an APEX. Option may be 28 repeated to give multiple extra packages. 29 30 --extra_apex_payload_key <name,name,...=key> 31 Add a mapping for APEX package name to payload signing key, which will 32 override the default payload signing key in apexkeys.txt. Note that the 33 container key should be overridden via the `--extra_apks` flag above. 34 Option may be repeated for multiple APEXes. 35 36 --skip_apks_with_path_prefix <prefix> 37 Skip signing an APK if it has the matching prefix in its path. The prefix 38 should be matching the entry name, which has partition names in upper 39 case, e.g. "VENDOR/app/", or "SYSTEM_OTHER/preloads/". Option may be 40 repeated to give multiple prefixes. 41 42 -k (--key_mapping) <src_key=dest_key> 43 Add a mapping from the key name as specified in apkcerts.txt (the 44 src_key) to the real key you wish to sign the package with 45 (dest_key). Option may be repeated to give multiple key 46 mappings. 47 48 -d (--default_key_mappings) <dir> 49 Set up the following key mappings: 50 51 $devkey/devkey ==> $dir/releasekey 52 $devkey/testkey ==> $dir/releasekey 53 $devkey/media ==> $dir/media 54 $devkey/shared ==> $dir/shared 55 $devkey/platform ==> $dir/platform 56 57 where $devkey is the directory part of the value of 58 default_system_dev_certificate from the input target-files's 59 META/misc_info.txt. (Defaulting to "build/make/target/product/security" 60 if the value is not present in misc_info. 61 62 -d and -k options are added to the set of mappings in the order 63 in which they appear on the command line. 64 65 -o (--replace_ota_keys) 66 Replace the certificate (public key) used by OTA package verification 67 with the ones specified in the input target_files zip (in the 68 META/otakeys.txt file). Key remapping (-k and -d) is performed on the 69 keys. For A/B devices, the payload verification key will be replaced 70 as well. If there're multiple OTA keys, only the first one will be used 71 for payload verification. 72 73 -t (--tag_changes) <+tag>,<-tag>,... 74 Comma-separated list of changes to make to the set of tags (in 75 the last component of the build fingerprint). Prefix each with 76 '+' or '-' to indicate whether that tag should be added or 77 removed. Changes are processed in the order they appear. 78 Default value is "-test-keys,-dev-keys,+release-keys". 79 80 --replace_verity_private_key <key> 81 Replace the private key used for verity signing. It expects a filename 82 WITHOUT the extension (e.g. verity_key). 83 84 --replace_verity_public_key <key> 85 Replace the certificate (public key) used for verity verification. The 86 key file replaces the one at BOOT/RAMDISK/verity_key. It expects the key 87 filename WITH the extension (e.g. verity_key.pub). 88 89 --replace_verity_keyid <path_to_X509_PEM_cert_file> 90 Replace the veritykeyid in BOOT/cmdline of input_target_file_zip 91 with keyid of the cert pointed by <path_to_X509_PEM_cert_file>. 92 93 --remove_avb_public_keys <key1>,<key2>,... 94 Remove AVB public keys from the first-stage ramdisk. The key file to 95 remove is located at either of the following dirs: 96 - BOOT/RAMDISK/avb/ or 97 - BOOT/RAMDISK/first_stage_ramdisk/avb/ 98 The second dir will be used for lookup if BOARD_USES_RECOVERY_AS_BOOT is 99 set to true. 100 101 --avb_{boot,init_boot,recovery,system,system_other,vendor,dtbo,vbmeta, 102 vbmeta_system,vbmeta_vendor}_algorithm <algorithm> 103 --avb_{boot,init_boot,recovery,system,system_other,vendor,dtbo,vbmeta, 104 vbmeta_system,vbmeta_vendor}_key <key> 105 Use the specified algorithm (e.g. SHA256_RSA4096) and the key to AVB-sign 106 the specified image. Otherwise it uses the existing values in info dict. 107 108 --avb_{apex,init_boot,boot,recovery,system,system_other,vendor,dtbo,vbmeta, 109 vbmeta_system,vbmeta_vendor}_extra_args <args> 110 Specify any additional args that are needed to AVB-sign the image 111 (e.g. "--signing_helper /path/to/helper"). The args will be appended to 112 the existing ones in info dict. 113 114 --avb_extra_custom_image_key <partition=key> 115 --avb_extra_custom_image_algorithm <partition=algorithm> 116 Use the specified algorithm (e.g. SHA256_RSA4096) and the key to AVB-sign 117 the specified custom images mounted on the partition. Otherwise it uses 118 the existing values in info dict. 119 120 --avb_extra_custom_image_extra_args <partition=extra_args> 121 Specify any additional args that are needed to AVB-sign the custom images 122 mounted on the partition (e.g. "--signing_helper /path/to/helper"). The 123 args will be appended to the existing ones in info dict. 124 125 --gki_signing_algorithm <algorithm> 126 --gki_signing_key <key> 127 --gki_signing_extra_args <args> 128 DEPRECATED Does nothing. 129 130 --android_jar_path <path> 131 Path to the android.jar to repack the apex file. 132 133 --allow_gsi_debug_sepolicy 134 Allow the existence of the file 'userdebug_plat_sepolicy.cil' under 135 (/system/system_ext|/system_ext)/etc/selinux. 136 If not set, error out when the file exists. 137 138 --override_apk_keys <path> 139 Replace all APK keys with this private key 140 141 --override_apex_keys <path> 142 Replace all APEX keys with this private key 143 144 -k (--package_key) <key> 145 Key to use to sign the package (default is the value of 146 default_system_dev_certificate from the input target-files's 147 META/misc_info.txt, or "build/make/target/product/security/testkey" if 148 that value is not specified). 149 150 For incremental OTAs, the default value is based on the source 151 target-file, not the target build. 152 153 --payload_signer <signer> 154 Specify the signer when signing the payload and metadata for A/B OTAs. 155 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign 156 with the package private key. If the private key cannot be accessed 157 directly, a payload signer that knows how to do that should be specified. 158 The signer will be supplied with "-inkey <path_to_key>", 159 "-in <input_file>" and "-out <output_file>" parameters. 160 161 --payload_signer_args <args> 162 Specify the arguments needed for payload signer. 163 164 --payload_signer_maximum_signature_size <signature_size> 165 The maximum signature size (in bytes) that would be generated by the given 166 payload signer. Only meaningful when custom payload signer is specified 167 via '--payload_signer'. 168 If the signer uses a RSA key, this should be the number of bytes to 169 represent the modulus. If it uses an EC key, this is the size of a 170 DER-encoded ECDSA signature. 171""" 172 173from __future__ import print_function 174 175import base64 176import copy 177import errno 178import gzip 179import io 180import itertools 181import logging 182import os 183import re 184import shutil 185import stat 186import sys 187import tempfile 188import zipfile 189from xml.etree import ElementTree 190 191import add_img_to_target_files 192import apex_utils 193import common 194import payload_signer 195from payload_signer import SignOtaPackage, PAYLOAD_BIN 196 197 198if sys.hexversion < 0x02070000: 199 print("Python 2.7 or newer is required.", file=sys.stderr) 200 sys.exit(1) 201 202 203logger = logging.getLogger(__name__) 204 205OPTIONS = common.OPTIONS 206 207OPTIONS.extra_apks = {} 208OPTIONS.extra_apex_payload_keys = {} 209OPTIONS.skip_apks_with_path_prefix = set() 210OPTIONS.key_map = {} 211OPTIONS.rebuild_recovery = False 212OPTIONS.replace_ota_keys = False 213OPTIONS.remove_avb_public_keys = None 214OPTIONS.tag_changes = ("-test-keys", "-dev-keys", "+release-keys") 215OPTIONS.avb_keys = {} 216OPTIONS.avb_algorithms = {} 217OPTIONS.avb_extra_args = {} 218OPTIONS.android_jar_path = None 219OPTIONS.vendor_partitions = set() 220OPTIONS.vendor_otatools = None 221OPTIONS.allow_gsi_debug_sepolicy = False 222OPTIONS.override_apk_keys = None 223OPTIONS.override_apex_keys = None 224 225 226AVB_FOOTER_ARGS_BY_PARTITION = { 227 'boot': 'avb_boot_add_hash_footer_args', 228 'init_boot': 'avb_init_boot_add_hash_footer_args', 229 'dtbo': 'avb_dtbo_add_hash_footer_args', 230 'product': 'avb_product_add_hashtree_footer_args', 231 'recovery': 'avb_recovery_add_hash_footer_args', 232 'system': 'avb_system_add_hashtree_footer_args', 233 'system_dlkm': "avb_system_dlkm_add_hashtree_footer_args", 234 'system_ext': 'avb_system_ext_add_hashtree_footer_args', 235 'system_other': 'avb_system_other_add_hashtree_footer_args', 236 'odm': 'avb_odm_add_hashtree_footer_args', 237 'odm_dlkm': 'avb_odm_dlkm_add_hashtree_footer_args', 238 'pvmfw': 'avb_pvmfw_add_hash_footer_args', 239 'vendor': 'avb_vendor_add_hashtree_footer_args', 240 'vendor_boot': 'avb_vendor_boot_add_hash_footer_args', 241 'vendor_kernel_boot': 'avb_vendor_kernel_boot_add_hash_footer_args', 242 'vendor_dlkm': "avb_vendor_dlkm_add_hashtree_footer_args", 243 'vbmeta': 'avb_vbmeta_args', 244 'vbmeta_system': 'avb_vbmeta_system_args', 245 'vbmeta_vendor': 'avb_vbmeta_vendor_args', 246} 247 248 249# Check that AVB_FOOTER_ARGS_BY_PARTITION is in sync with AVB_PARTITIONS. 250for partition in common.AVB_PARTITIONS: 251 if partition not in AVB_FOOTER_ARGS_BY_PARTITION: 252 raise RuntimeError("Missing {} in AVB_FOOTER_ARGS".format(partition)) 253 254# Partitions that can be regenerated after signing using a separate 255# vendor otatools package. 256ALLOWED_VENDOR_PARTITIONS = set(["vendor", "odm"]) 257 258 259def IsApexFile(filename): 260 return filename.endswith(".apex") or filename.endswith(".capex") 261 262 263def IsOtaPackage(fp): 264 with zipfile.ZipFile(fp) as zfp: 265 if not PAYLOAD_BIN in zfp.namelist(): 266 return False 267 with zfp.open(PAYLOAD_BIN, "r") as payload: 268 magic = payload.read(4) 269 return magic == b"CrAU" 270 271 272def IsEntryOtaPackage(input_zip, filename): 273 with input_zip.open(filename, "r") as fp: 274 external_attr = input_zip.getinfo(filename).external_attr 275 if stat.S_ISLNK(external_attr >> 16): 276 return IsEntryOtaPackage(input_zip, 277 os.path.join(os.path.dirname(filename), fp.read().decode())) 278 return IsOtaPackage(fp) 279 280 281def GetApexFilename(filename): 282 name = os.path.basename(filename) 283 # Replace the suffix for compressed apex 284 if name.endswith(".capex"): 285 return name.replace(".capex", ".apex") 286 return name 287 288 289def GetApkCerts(certmap): 290 if OPTIONS.override_apk_keys is not None: 291 for apk in certmap.keys(): 292 certmap[apk] = OPTIONS.override_apk_keys 293 294 # apply the key remapping to the contents of the file 295 for apk, cert in certmap.items(): 296 certmap[apk] = OPTIONS.key_map.get(cert, cert) 297 298 # apply all the -e options, overriding anything in the file 299 for apk, cert in OPTIONS.extra_apks.items(): 300 if not cert: 301 cert = "PRESIGNED" 302 certmap[apk] = OPTIONS.key_map.get(cert, cert) 303 304 return certmap 305 306 307def GetApexKeys(keys_info, key_map): 308 """Gets APEX payload and container signing keys by applying the mapping rules. 309 310 Presigned payload / container keys will be set accordingly. 311 312 Args: 313 keys_info: A dict that maps from APEX filenames to a tuple of (payload_key, 314 container_key, sign_tool). 315 key_map: A dict that overrides the keys, specified via command-line input. 316 317 Returns: 318 A dict that contains the updated APEX key mapping, which should be used for 319 the current signing. 320 321 Raises: 322 AssertionError: On invalid container / payload key overrides. 323 """ 324 if OPTIONS.override_apex_keys is not None: 325 for apex in keys_info.keys(): 326 keys_info[apex] = (OPTIONS.override_apex_keys, keys_info[apex][1], keys_info[apex][2]) 327 328 if OPTIONS.override_apk_keys is not None: 329 key = key_map.get(OPTIONS.override_apk_keys, OPTIONS.override_apk_keys) 330 for apex in keys_info.keys(): 331 keys_info[apex] = (keys_info[apex][0], key, keys_info[apex][2]) 332 333 # Apply all the --extra_apex_payload_key options to override the payload 334 # signing keys in the given keys_info. 335 for apex, key in OPTIONS.extra_apex_payload_keys.items(): 336 if not key: 337 key = 'PRESIGNED' 338 if apex not in keys_info: 339 logger.warning('Failed to find %s in target_files; Ignored', apex) 340 continue 341 keys_info[apex] = (key, keys_info[apex][1], keys_info[apex][2]) 342 343 # Apply the key remapping to container keys. 344 for apex, (payload_key, container_key, sign_tool) in keys_info.items(): 345 keys_info[apex] = (payload_key, key_map.get(container_key, container_key), sign_tool) 346 347 # Apply all the --extra_apks options to override the container keys. 348 for apex, key in OPTIONS.extra_apks.items(): 349 # Skip non-APEX containers. 350 if apex not in keys_info: 351 continue 352 if not key: 353 key = 'PRESIGNED' 354 keys_info[apex] = (keys_info[apex][0], key_map.get(key, key), keys_info[apex][2]) 355 356 # A PRESIGNED container entails a PRESIGNED payload. Apply this to all the 357 # APEX key pairs. However, a PRESIGNED container with non-PRESIGNED payload 358 # (overridden via commandline) indicates a config error, which should not be 359 # allowed. 360 for apex, (payload_key, container_key, sign_tool) in keys_info.items(): 361 if container_key != 'PRESIGNED': 362 continue 363 if apex in OPTIONS.extra_apex_payload_keys: 364 payload_override = OPTIONS.extra_apex_payload_keys[apex] 365 assert payload_override == '', \ 366 ("Invalid APEX key overrides: {} has PRESIGNED container but " 367 "non-PRESIGNED payload key {}").format(apex, payload_override) 368 if payload_key != 'PRESIGNED': 369 print( 370 "Setting {} payload as PRESIGNED due to PRESIGNED container".format( 371 apex)) 372 keys_info[apex] = ('PRESIGNED', 'PRESIGNED', None) 373 374 return keys_info 375 376 377def GetApkFileInfo(filename, compressed_extension, skipped_prefixes): 378 """Returns the APK info based on the given filename. 379 380 Checks if the given filename (with path) looks like an APK file, by taking the 381 compressed extension into consideration. If it appears to be an APK file, 382 further checks if the APK file should be skipped when signing, based on the 383 given path prefixes. 384 385 Args: 386 filename: Path to the file. 387 compressed_extension: The extension string of compressed APKs (e.g. ".gz"), 388 or None if there's no compressed APKs. 389 skipped_prefixes: A set/list/tuple of the path prefixes to be skipped. 390 391 Returns: 392 (is_apk, is_compressed, should_be_skipped): is_apk indicates whether the 393 given filename is an APK file. is_compressed indicates whether the APK file 394 is compressed (only meaningful when is_apk is True). should_be_skipped 395 indicates whether the filename matches any of the given prefixes to be 396 skipped. 397 398 Raises: 399 AssertionError: On invalid compressed_extension or skipped_prefixes inputs. 400 """ 401 assert compressed_extension is None or compressed_extension.startswith('.'), \ 402 "Invalid compressed_extension arg: '{}'".format(compressed_extension) 403 404 # skipped_prefixes should be one of set/list/tuple types. Other types such as 405 # str shouldn't be accepted. 406 assert isinstance(skipped_prefixes, (set, list, tuple)), \ 407 "Invalid skipped_prefixes input type: {}".format(type(skipped_prefixes)) 408 409 compressed_apk_extension = ( 410 ".apk" + compressed_extension if compressed_extension else None) 411 is_apk = (filename.endswith(".apk") or 412 (compressed_apk_extension and 413 filename.endswith(compressed_apk_extension))) 414 if not is_apk: 415 return (False, False, False) 416 417 is_compressed = (compressed_apk_extension and 418 filename.endswith(compressed_apk_extension)) 419 should_be_skipped = filename.startswith(tuple(skipped_prefixes)) 420 return (True, is_compressed, should_be_skipped) 421 422 423def CheckApkAndApexKeysAvailable(input_tf_zip, known_keys, 424 compressed_extension, apex_keys): 425 """Checks that all the APKs and APEXes have keys specified. 426 427 Args: 428 input_tf_zip: An open target_files zip file. 429 known_keys: A set of APKs and APEXes that have known signing keys. 430 compressed_extension: The extension string of compressed APKs, such as 431 '.gz', or None if there's no compressed APKs. 432 apex_keys: A dict that contains the key mapping from APEX name to 433 (payload_key, container_key, sign_tool). 434 435 Raises: 436 AssertionError: On finding unknown APKs and APEXes. 437 """ 438 unknown_files = [] 439 for info in input_tf_zip.infolist(): 440 # Handle APEXes on all partitions 441 if IsApexFile(info.filename): 442 name = GetApexFilename(info.filename) 443 if name not in known_keys: 444 unknown_files.append(name) 445 continue 446 447 # And APKs. 448 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo( 449 info.filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix) 450 if not is_apk or should_be_skipped: 451 continue 452 453 name = os.path.basename(info.filename) 454 if is_compressed: 455 name = name[:-len(compressed_extension)] 456 if name not in known_keys: 457 unknown_files.append(name) 458 459 assert not unknown_files, \ 460 ("No key specified for:\n {}\n" 461 "Use '-e <apkname>=' to specify a key (which may be an empty string to " 462 "not sign this apk).".format("\n ".join(unknown_files))) 463 464 # For all the APEXes, double check that we won't have an APEX that has only 465 # one of the payload / container keys set. Note that non-PRESIGNED container 466 # with PRESIGNED payload could be allowed but currently unsupported. It would 467 # require changing SignApex implementation. 468 if not apex_keys: 469 return 470 471 invalid_apexes = [] 472 for info in input_tf_zip.infolist(): 473 if not IsApexFile(info.filename): 474 continue 475 476 name = GetApexFilename(info.filename) 477 478 (payload_key, container_key, _) = apex_keys[name] 479 if ((payload_key in common.SPECIAL_CERT_STRINGS and 480 container_key not in common.SPECIAL_CERT_STRINGS) or 481 (payload_key not in common.SPECIAL_CERT_STRINGS and 482 container_key in common.SPECIAL_CERT_STRINGS)): 483 invalid_apexes.append( 484 "{}: payload_key {}, container_key {}".format( 485 name, payload_key, container_key)) 486 487 assert not invalid_apexes, \ 488 "Invalid APEX keys specified:\n {}\n".format( 489 "\n ".join(invalid_apexes)) 490 491 492def SignApk(data, keyname, pw, platform_api_level, codename_to_api_level_map, 493 is_compressed, apk_name): 494 unsigned = tempfile.NamedTemporaryFile(suffix='_' + apk_name) 495 unsigned.write(data) 496 unsigned.flush() 497 498 if is_compressed: 499 uncompressed = tempfile.NamedTemporaryFile() 500 with gzip.open(unsigned.name, "rb") as in_file, \ 501 open(uncompressed.name, "wb") as out_file: 502 shutil.copyfileobj(in_file, out_file) 503 504 # Finally, close the "unsigned" file (which is gzip compressed), and then 505 # replace it with the uncompressed version. 506 # 507 # TODO(narayan): All this nastiness can be avoided if python 3.2 is in use, 508 # we could just gzip / gunzip in-memory buffers instead. 509 unsigned.close() 510 unsigned = uncompressed 511 512 signed = tempfile.NamedTemporaryFile(suffix='_' + apk_name) 513 514 # For pre-N builds, don't upgrade to SHA-256 JAR signatures based on the APK's 515 # minSdkVersion to avoid increasing incremental OTA update sizes. If an APK 516 # didn't change, we don't want its signature to change due to the switch 517 # from SHA-1 to SHA-256. 518 # By default, APK signer chooses SHA-256 signatures if the APK's minSdkVersion 519 # is 18 or higher. For pre-N builds we disable this mechanism by pretending 520 # that the APK's minSdkVersion is 1. 521 # For N+ builds, we let APK signer rely on the APK's minSdkVersion to 522 # determine whether to use SHA-256. 523 min_api_level = None 524 if platform_api_level > 23: 525 # Let APK signer choose whether to use SHA-1 or SHA-256, based on the APK's 526 # minSdkVersion attribute 527 min_api_level = None 528 else: 529 # Force APK signer to use SHA-1 530 min_api_level = 1 531 532 common.SignFile(unsigned.name, signed.name, keyname, pw, 533 min_api_level=min_api_level, 534 codename_to_api_level_map=codename_to_api_level_map) 535 536 data = None 537 if is_compressed: 538 # Recompress the file after it has been signed. 539 compressed = tempfile.NamedTemporaryFile() 540 with open(signed.name, "rb") as in_file, \ 541 gzip.open(compressed.name, "wb") as out_file: 542 shutil.copyfileobj(in_file, out_file) 543 544 data = compressed.read() 545 compressed.close() 546 else: 547 data = signed.read() 548 549 unsigned.close() 550 signed.close() 551 552 return data 553 554 555 556def IsBuildPropFile(filename): 557 return filename in ( 558 "SYSTEM/etc/prop.default", 559 "BOOT/RAMDISK/prop.default", 560 "RECOVERY/RAMDISK/prop.default", 561 562 "VENDOR_BOOT/RAMDISK/default.prop", 563 "VENDOR_BOOT/RAMDISK/prop.default", 564 565 # ROOT/default.prop is a legacy path, but may still exist for upgrading 566 # devices that don't support `property_overrides_split_enabled`. 567 "ROOT/default.prop", 568 569 # RECOVERY/RAMDISK/default.prop is a legacy path, but will always exist 570 # as a symlink in the current code. So it's a no-op here. Keeping the 571 # path here for clarity. 572 # Some build props might be stored under path 573 # VENDOR_BOOT/RAMDISK_FRAGMENTS/recovery/RAMDISK/default.prop, and 574 # default.prop can be a symbolic link to prop.default, so overwrite all 575 # files that ends with build.prop, default.prop or prop.default 576 "RECOVERY/RAMDISK/default.prop") or \ 577 filename.endswith("build.prop") or \ 578 filename.endswith("/default.prop") or \ 579 filename.endswith("/prop.default") 580 581 582def ProcessTargetFiles(input_tf_zip: zipfile.ZipFile, output_tf_zip, misc_info, 583 apk_keys, apex_keys, key_passwords, 584 platform_api_level, codename_to_api_level_map, 585 compressed_extension): 586 # maxsize measures the maximum filename length, including the ones to be 587 # skipped. 588 try: 589 maxsize = max( 590 [len(os.path.basename(i.filename)) for i in input_tf_zip.infolist() 591 if GetApkFileInfo(i.filename, compressed_extension, [])[0]]) 592 except ValueError: 593 # Sets this to zero for targets without APK files. 594 maxsize = 0 595 596 for info in input_tf_zip.infolist(): 597 filename = info.filename 598 if filename.startswith("IMAGES/"): 599 continue 600 601 # Skip OTA-specific images (e.g. split super images), which will be 602 # re-generated during signing. 603 if filename.startswith("OTA/") and filename.endswith(".img"): 604 continue 605 606 data = input_tf_zip.read(filename) 607 out_info = copy.copy(info) 608 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo( 609 filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix) 610 611 if is_apk and should_be_skipped: 612 # Copy skipped APKs verbatim. 613 print( 614 "NOT signing: %s\n" 615 " (skipped due to matching prefix)" % (filename,)) 616 common.ZipWriteStr(output_tf_zip, out_info, data) 617 618 # Sign APKs. 619 elif is_apk: 620 name = os.path.basename(filename) 621 if is_compressed: 622 name = name[:-len(compressed_extension)] 623 624 key = apk_keys[name] 625 if key not in common.SPECIAL_CERT_STRINGS: 626 print(" signing: %-*s (%s)" % (maxsize, name, key)) 627 signed_data = SignApk(data, key, key_passwords[key], platform_api_level, 628 codename_to_api_level_map, is_compressed, name) 629 common.ZipWriteStr(output_tf_zip, out_info, signed_data) 630 else: 631 # an APK we're not supposed to sign. 632 print( 633 "NOT signing: %s\n" 634 " (skipped due to special cert string)" % (name,)) 635 common.ZipWriteStr(output_tf_zip, out_info, data) 636 637 # Sign bundled APEX files on all partitions 638 elif IsApexFile(filename): 639 name = GetApexFilename(filename) 640 641 payload_key, container_key, sign_tool = apex_keys[name] 642 643 # We've asserted not having a case with only one of them PRESIGNED. 644 if (payload_key not in common.SPECIAL_CERT_STRINGS and 645 container_key not in common.SPECIAL_CERT_STRINGS): 646 print(" signing: %-*s container (%s)" % ( 647 maxsize, name, container_key)) 648 print(" : %-*s payload (%s)" % ( 649 maxsize, name, payload_key)) 650 651 signed_apex = apex_utils.SignApex( 652 misc_info['avb_avbtool'], 653 data, 654 payload_key, 655 container_key, 656 key_passwords, 657 apk_keys, 658 codename_to_api_level_map, 659 no_hashtree=None, # Let apex_util determine if hash tree is needed 660 signing_args=OPTIONS.avb_extra_args.get('apex'), 661 sign_tool=sign_tool) 662 common.ZipWrite(output_tf_zip, signed_apex, filename) 663 664 else: 665 print( 666 "NOT signing: %s\n" 667 " (skipped due to special cert string)" % (name,)) 668 common.ZipWriteStr(output_tf_zip, out_info, data) 669 670 elif filename.endswith(".zip") and IsEntryOtaPackage(input_tf_zip, filename): 671 logger.info("Re-signing OTA package {}".format(filename)) 672 with tempfile.NamedTemporaryFile() as input_ota, tempfile.NamedTemporaryFile() as output_ota: 673 with input_tf_zip.open(filename, "r") as in_fp: 674 shutil.copyfileobj(in_fp, input_ota) 675 input_ota.flush() 676 SignOtaPackage(input_ota.name, output_ota.name) 677 common.ZipWrite(output_tf_zip, output_ota.name, filename, 678 compress_type=zipfile.ZIP_STORED) 679 # System properties. 680 elif IsBuildPropFile(filename): 681 print("Rewriting %s:" % (filename,)) 682 if stat.S_ISLNK(info.external_attr >> 16): 683 new_data = data 684 else: 685 new_data = RewriteProps(data.decode()) 686 common.ZipWriteStr(output_tf_zip, out_info, new_data) 687 688 # Replace the certs in *mac_permissions.xml (there could be multiple, such 689 # as {system,vendor}/etc/selinux/{plat,vendor}_mac_permissions.xml). 690 elif filename.endswith("mac_permissions.xml"): 691 print("Rewriting %s with new keys." % (filename,)) 692 new_data = ReplaceCerts(data.decode()) 693 common.ZipWriteStr(output_tf_zip, out_info, new_data) 694 695 # Ask add_img_to_target_files to rebuild the recovery patch if needed. 696 elif filename in ("SYSTEM/recovery-from-boot.p", 697 "VENDOR/recovery-from-boot.p", 698 699 "SYSTEM/etc/recovery.img", 700 "VENDOR/etc/recovery.img", 701 702 "SYSTEM/bin/install-recovery.sh", 703 "VENDOR/bin/install-recovery.sh"): 704 OPTIONS.rebuild_recovery = True 705 706 # Don't copy OTA certs if we're replacing them. 707 # Replacement of update-payload-key.pub.pem was removed in b/116660991. 708 elif OPTIONS.replace_ota_keys and filename.endswith("/otacerts.zip"): 709 pass 710 711 # Skip META/misc_info.txt since we will write back the new values later. 712 elif filename == "META/misc_info.txt": 713 pass 714 715 elif (OPTIONS.remove_avb_public_keys and 716 (filename.startswith("BOOT/RAMDISK/avb/") or 717 filename.startswith("BOOT/RAMDISK/first_stage_ramdisk/avb/"))): 718 matched_removal = False 719 for key_to_remove in OPTIONS.remove_avb_public_keys: 720 if filename.endswith(key_to_remove): 721 matched_removal = True 722 print("Removing AVB public key from ramdisk: %s" % filename) 723 break 724 if not matched_removal: 725 # Copy it verbatim if we don't want to remove it. 726 common.ZipWriteStr(output_tf_zip, out_info, data) 727 728 # Skip the vbmeta digest as we will recalculate it. 729 elif filename == "META/vbmeta_digest.txt": 730 pass 731 732 # Skip the care_map as we will regenerate the system/vendor images. 733 elif filename in ["META/care_map.pb", "META/care_map.txt"]: 734 pass 735 736 # Skip apex_info.pb because we sign/modify apexes 737 elif filename == "META/apex_info.pb": 738 pass 739 740 # Updates system_other.avbpubkey in /product/etc/. 741 elif filename in ( 742 "PRODUCT/etc/security/avb/system_other.avbpubkey", 743 "SYSTEM/product/etc/security/avb/system_other.avbpubkey"): 744 # Only update system_other's public key, if the corresponding signing 745 # key is specified via --avb_system_other_key. 746 signing_key = OPTIONS.avb_keys.get("system_other") 747 if signing_key: 748 public_key = common.ExtractAvbPublicKey( 749 misc_info['avb_avbtool'], signing_key) 750 print(" Rewriting AVB public key of system_other in /product") 751 common.ZipWrite(output_tf_zip, public_key, filename) 752 753 # Updates pvmfw embedded public key with the virt APEX payload key. 754 elif filename == "PREBUILT_IMAGES/pvmfw.img": 755 # Find the name of the virt APEX in the target files. 756 namelist = input_tf_zip.namelist() 757 apex_gen = (GetApexFilename(f) for f in namelist if IsApexFile(f)) 758 virt_apex_re = re.compile("^com\.([^\.]+\.)?android\.virt\.apex$") 759 virt_apex = next((a for a in apex_gen if virt_apex_re.match(a)), None) 760 if not virt_apex: 761 print("Removing %s from ramdisk: virt APEX not found" % filename) 762 else: 763 print("Replacing %s embedded key with %s key" % (filename, virt_apex)) 764 # Get the current and new embedded keys. 765 payload_key, container_key, sign_tool = apex_keys[virt_apex] 766 new_pubkey_path = common.ExtractAvbPublicKey( 767 misc_info['avb_avbtool'], payload_key) 768 with open(new_pubkey_path, 'rb') as f: 769 new_pubkey = f.read() 770 pubkey_info = copy.copy( 771 input_tf_zip.getinfo("PREBUILT_IMAGES/pvmfw_embedded.avbpubkey")) 772 old_pubkey = input_tf_zip.read(pubkey_info.filename) 773 # Validate the keys and image. 774 if len(old_pubkey) != len(new_pubkey): 775 raise common.ExternalError("pvmfw embedded public key size mismatch") 776 pos = data.find(old_pubkey) 777 if pos == -1: 778 raise common.ExternalError("pvmfw embedded public key not found") 779 # Replace the key and copy new files. 780 new_data = data[:pos] + new_pubkey + data[pos+len(old_pubkey):] 781 common.ZipWriteStr(output_tf_zip, out_info, new_data) 782 common.ZipWriteStr(output_tf_zip, pubkey_info, new_pubkey) 783 elif filename == "PREBUILT_IMAGES/pvmfw_embedded.avbpubkey": 784 pass 785 786 # Should NOT sign boot-debug.img. 787 elif filename in ( 788 "BOOT/RAMDISK/force_debuggable", 789 "BOOT/RAMDISK/first_stage_ramdisk/force_debuggable"): 790 raise common.ExternalError("debuggable boot.img cannot be signed") 791 792 # Should NOT sign userdebug sepolicy file. 793 elif filename in ( 794 "SYSTEM_EXT/etc/selinux/userdebug_plat_sepolicy.cil", 795 "SYSTEM/system_ext/etc/selinux/userdebug_plat_sepolicy.cil"): 796 if not OPTIONS.allow_gsi_debug_sepolicy: 797 raise common.ExternalError("debug sepolicy shouldn't be included") 798 else: 799 # Copy it verbatim if we allow the file to exist. 800 common.ZipWriteStr(output_tf_zip, out_info, data) 801 802 # Sign microdroid_vendor.img. 803 elif filename == "VENDOR/etc/avf/microdroid/microdroid_vendor.img": 804 vendor_key = OPTIONS.avb_keys.get("vendor") 805 vendor_algorithm = OPTIONS.avb_algorithms.get("vendor") 806 with tempfile.NamedTemporaryFile() as image: 807 image.write(data) 808 image.flush() 809 ReplaceKeyInAvbHashtreeFooter(image, vendor_key, vendor_algorithm, 810 misc_info) 811 common.ZipWrite(output_tf_zip, image.name, filename) 812 # A non-APK file; copy it verbatim. 813 else: 814 common.ZipWriteStr(output_tf_zip, out_info, data) 815 816 if OPTIONS.replace_ota_keys: 817 ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info) 818 819 # Replace the AVB signing keys, if any. 820 ReplaceAvbSigningKeys(misc_info) 821 822 # Rewrite the props in AVB signing args. 823 if misc_info.get('avb_enable') == 'true': 824 RewriteAvbProps(misc_info) 825 826 # Write back misc_info with the latest values. 827 ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info) 828 829# Parse string output of `avbtool info_image`. 830def ParseAvbInfo(info_raw): 831 # line_matcher is for parsing each output line of `avbtool info_image`. 832 # example string input: " Hash Algorithm: sha1" 833 # example matched input: (" ", "Hash Algorithm", "sha1") 834 line_matcher = re.compile(r'^(\s*)([^:]+):\s*(.*)$') 835 # prop_matcher is for parsing value part of 'Prop' in `avbtool info_image`. 836 # example string input: "example_prop_key -> 'example_prop_value'" 837 # example matched output: ("example_prop_key", "example_prop_value") 838 prop_matcher = re.compile(r"(.+)\s->\s'(.+)'") 839 info = {} 840 indent_stack = [[-1, info]] 841 for line_info_raw in info_raw.split('\n'): 842 # Parse the line 843 line_info_parsed = line_matcher.match(line_info_raw) 844 if not line_info_parsed: 845 continue 846 indent = len(line_info_parsed.group(1)) 847 key = line_info_parsed.group(2).strip() 848 value = line_info_parsed.group(3).strip() 849 850 # Pop indentation stack 851 while indent <= indent_stack[-1][0]: 852 del indent_stack[-1] 853 854 # Insert information into 'info'. 855 cur_info = indent_stack[-1][1] 856 if value == "": 857 if key == "Descriptors": 858 empty_list = [] 859 cur_info[key] = empty_list 860 indent_stack.append([indent, empty_list]) 861 else: 862 empty_dict = {} 863 cur_info.append({key:empty_dict}) 864 indent_stack.append([indent, empty_dict]) 865 elif key == "Prop": 866 prop_parsed = prop_matcher.match(value) 867 if not prop_parsed: 868 raise ValueError( 869 "Failed to parse prop while getting avb information.") 870 cur_info.append({key:{prop_parsed.group(1):prop_parsed.group(2)}}) 871 else: 872 cur_info[key] = value 873 return info 874 875def ReplaceKeyInAvbHashtreeFooter(image, new_key, new_algorithm, misc_info): 876 # Get avb information about the image by parsing avbtool info_image. 877 def GetAvbInfo(avbtool, image_name): 878 # Get information with raw string by `avbtool info_image`. 879 info_raw = common.RunAndCheckOutput([ 880 avbtool, 'info_image', 881 '--image', image_name 882 ]) 883 return ParseAvbInfo(info_raw) 884 885 # Get hashtree descriptor from info 886 def GetAvbHashtreeDescriptor(avb_info): 887 hashtree_descriptors = tuple(filter(lambda x: "Hashtree descriptor" in x, 888 info.get('Descriptors'))) 889 if len(hashtree_descriptors) != 1: 890 raise ValueError("The number of hashtree descriptor is not 1.") 891 return hashtree_descriptors[0]["Hashtree descriptor"] 892 893 # Get avb info 894 avbtool = misc_info['avb_avbtool'] 895 info = GetAvbInfo(avbtool, image.name) 896 hashtree_descriptor = GetAvbHashtreeDescriptor(info) 897 898 # Generate command 899 cmd = [avbtool, 'add_hashtree_footer', 900 '--key', new_key, 901 '--algorithm', new_algorithm, 902 '--partition_name', hashtree_descriptor.get("Partition Name"), 903 '--partition_size', info.get("Image size").removesuffix(" bytes"), 904 '--hash_algorithm', hashtree_descriptor.get("Hash Algorithm"), 905 '--salt', hashtree_descriptor.get("Salt"), 906 '--do_not_generate_fec', 907 '--image', image.name 908 ] 909 910 # Append properties into command 911 props = map(lambda x: x.get("Prop"), filter(lambda x: "Prop" in x, 912 info.get('Descriptors'))) 913 for prop_wrapped in props: 914 prop = tuple(prop_wrapped.items()) 915 if len(prop) != 1: 916 raise ValueError("The number of property is not 1.") 917 cmd.append('--prop') 918 cmd.append(prop[0][0] + ':' + prop[0][1]) 919 920 # Replace Hashtree Footer with new key 921 common.RunAndCheckOutput(cmd) 922 923 # Check root digest is not changed 924 new_info = GetAvbInfo(avbtool, image.name) 925 new_hashtree_descriptor = GetAvbHashtreeDescriptor(info) 926 root_digest = hashtree_descriptor.get("Root Digest") 927 new_root_digest = new_hashtree_descriptor.get("Root Digest") 928 assert root_digest == new_root_digest, \ 929 ("Root digest in hashtree descriptor shouldn't be changed. Old: {}, New: " 930 "{}").format(root_digest, new_root_digest) 931 932def ReplaceCerts(data): 933 """Replaces all the occurences of X.509 certs with the new ones. 934 935 The mapping info is read from OPTIONS.key_map. Non-existent certificate will 936 be skipped. After the replacement, it additionally checks for duplicate 937 entries, which would otherwise fail the policy loading code in 938 frameworks/base/services/core/java/com/android/server/pm/SELinuxMMAC.java. 939 940 Args: 941 data: Input string that contains a set of X.509 certs. 942 943 Returns: 944 A string after the replacement. 945 946 Raises: 947 AssertionError: On finding duplicate entries. 948 """ 949 for old, new in OPTIONS.key_map.items(): 950 if OPTIONS.verbose: 951 print(" Replacing %s.x509.pem with %s.x509.pem" % (old, new)) 952 953 try: 954 with open(old + ".x509.pem") as old_fp: 955 old_cert16 = base64.b16encode( 956 common.ParseCertificate(old_fp.read())).decode().lower() 957 with open(new + ".x509.pem") as new_fp: 958 new_cert16 = base64.b16encode( 959 common.ParseCertificate(new_fp.read())).decode().lower() 960 except IOError as e: 961 if OPTIONS.verbose or e.errno != errno.ENOENT: 962 print(" Error accessing %s: %s.\nSkip replacing %s.x509.pem with " 963 "%s.x509.pem." % (e.filename, e.strerror, old, new)) 964 continue 965 966 # Only match entire certs. 967 pattern = "\\b" + old_cert16 + "\\b" 968 (data, num) = re.subn(pattern, new_cert16, data, flags=re.IGNORECASE) 969 970 if OPTIONS.verbose: 971 print(" Replaced %d occurence(s) of %s.x509.pem with %s.x509.pem" % ( 972 num, old, new)) 973 974 # Verify that there're no duplicate entries after the replacement. Note that 975 # it's only checking entries with global seinfo at the moment (i.e. ignoring 976 # the ones with inner packages). (Bug: 69479366) 977 root = ElementTree.fromstring(data) 978 signatures = [signer.attrib['signature'] 979 for signer in root.findall('signer')] 980 assert len(signatures) == len(set(signatures)), \ 981 "Found duplicate entries after cert replacement: {}".format(data) 982 983 return data 984 985 986def EditTags(tags): 987 """Applies the edits to the tag string as specified in OPTIONS.tag_changes. 988 989 Args: 990 tags: The input string that contains comma-separated tags. 991 992 Returns: 993 The updated tags (comma-separated and sorted). 994 """ 995 tags = set(tags.split(",")) 996 for ch in OPTIONS.tag_changes: 997 if ch[0] == "-": 998 tags.discard(ch[1:]) 999 elif ch[0] == "+": 1000 tags.add(ch[1:]) 1001 return ",".join(sorted(tags)) 1002 1003 1004def RewriteProps(data): 1005 """Rewrites the system properties in the given string. 1006 1007 Each property is expected in 'key=value' format. The properties that contain 1008 build tags (i.e. test-keys, dev-keys) will be updated accordingly by calling 1009 EditTags(). 1010 1011 Args: 1012 data: Input string, separated by newlines. 1013 1014 Returns: 1015 The string with modified properties. 1016 """ 1017 output = [] 1018 for line in data.split("\n"): 1019 line = line.strip() 1020 original_line = line 1021 if line and line[0] != '#' and "=" in line: 1022 key, value = line.split("=", 1) 1023 if (key.startswith("ro.") and 1024 key.endswith((".build.fingerprint", ".build.thumbprint"))): 1025 pieces = value.split("/") 1026 pieces[-1] = EditTags(pieces[-1]) 1027 value = "/".join(pieces) 1028 elif key == "ro.bootimage.build.fingerprint": 1029 pieces = value.split("/") 1030 pieces[-1] = EditTags(pieces[-1]) 1031 value = "/".join(pieces) 1032 elif key == "ro.build.description": 1033 pieces = value.split() 1034 assert pieces[-1].endswith("-keys") 1035 pieces[-1] = EditTags(pieces[-1]) 1036 value = " ".join(pieces) 1037 elif key.startswith("ro.") and key.endswith(".build.tags"): 1038 value = EditTags(value) 1039 elif key == "ro.build.display.id": 1040 # change, eg, "JWR66N dev-keys" to "JWR66N" 1041 value = value.split() 1042 if len(value) > 1 and value[-1].endswith("-keys"): 1043 value.pop() 1044 value = " ".join(value) 1045 line = key + "=" + value 1046 if line != original_line: 1047 print(" replace: ", original_line) 1048 print(" with: ", line) 1049 output.append(line) 1050 return "\n".join(output) + "\n" 1051 1052 1053def WriteOtacerts(output_zip, filename, keys): 1054 """Constructs a zipfile from given keys; and writes it to output_zip. 1055 1056 Args: 1057 output_zip: The output target_files zip. 1058 filename: The archive name in the output zip. 1059 keys: A list of public keys to use during OTA package verification. 1060 """ 1061 temp_file = io.BytesIO() 1062 certs_zip = zipfile.ZipFile(temp_file, "w", allowZip64=True) 1063 for k in keys: 1064 common.ZipWrite(certs_zip, k) 1065 common.ZipClose(certs_zip) 1066 common.ZipWriteStr(output_zip, filename, temp_file.getvalue()) 1067 1068 1069def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info): 1070 try: 1071 keylist = input_tf_zip.read("META/otakeys.txt").split() 1072 except KeyError: 1073 raise common.ExternalError("can't read META/otakeys.txt from input") 1074 1075 extra_ota_keys_info = misc_info.get("extra_ota_keys") 1076 if extra_ota_keys_info: 1077 extra_ota_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem" 1078 for k in extra_ota_keys_info.split()] 1079 print("extra ota key(s): " + ", ".join(extra_ota_keys)) 1080 else: 1081 extra_ota_keys = [] 1082 for k in extra_ota_keys: 1083 if not os.path.isfile(k): 1084 raise common.ExternalError(k + " does not exist or is not a file") 1085 1086 extra_recovery_keys_info = misc_info.get("extra_recovery_keys") 1087 if extra_recovery_keys_info: 1088 extra_recovery_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem" 1089 for k in extra_recovery_keys_info.split()] 1090 print("extra recovery-only key(s): " + ", ".join(extra_recovery_keys)) 1091 else: 1092 extra_recovery_keys = [] 1093 for k in extra_recovery_keys: 1094 if not os.path.isfile(k): 1095 raise common.ExternalError(k + " does not exist or is not a file") 1096 1097 mapped_keys = [] 1098 for k in keylist: 1099 m = re.match(r"^(.*)\.x509\.pem$", k) 1100 if not m: 1101 raise common.ExternalError( 1102 "can't parse \"%s\" from META/otakeys.txt" % (k,)) 1103 k = m.group(1) 1104 mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem") 1105 1106 if mapped_keys: 1107 print("using:\n ", "\n ".join(mapped_keys)) 1108 print("for OTA package verification") 1109 else: 1110 devkey = misc_info.get("default_system_dev_certificate", 1111 "build/make/target/product/security/testkey") 1112 mapped_devkey = OPTIONS.key_map.get(devkey, devkey) 1113 if mapped_devkey != devkey: 1114 misc_info["default_system_dev_certificate"] = mapped_devkey 1115 mapped_keys.append(mapped_devkey + ".x509.pem") 1116 print("META/otakeys.txt has no keys; using %s for OTA package" 1117 " verification." % (mapped_keys[0],)) 1118 for k in mapped_keys: 1119 if not os.path.isfile(k): 1120 raise common.ExternalError(k + " does not exist or is not a file") 1121 1122 otacerts = [info 1123 for info in input_tf_zip.infolist() 1124 if info.filename.endswith("/otacerts.zip")] 1125 for info in otacerts: 1126 if info.filename.startswith(("BOOT/", "RECOVERY/", "VENDOR_BOOT/")): 1127 extra_keys = extra_recovery_keys 1128 else: 1129 extra_keys = extra_ota_keys 1130 print("Rewriting OTA key:", info.filename, mapped_keys + extra_keys) 1131 WriteOtacerts(output_tf_zip, info.filename, mapped_keys + extra_keys) 1132 1133 1134def ReplaceMiscInfoTxt(input_zip, output_zip, misc_info): 1135 """Replaces META/misc_info.txt. 1136 1137 Only writes back the ones in the original META/misc_info.txt. Because the 1138 current in-memory dict contains additional items computed at runtime. 1139 """ 1140 misc_info_old = common.LoadDictionaryFromLines( 1141 input_zip.read('META/misc_info.txt').decode().split('\n')) 1142 items = [] 1143 for key in sorted(misc_info): 1144 if key in misc_info_old: 1145 items.append('%s=%s' % (key, misc_info[key])) 1146 common.ZipWriteStr(output_zip, "META/misc_info.txt", '\n'.join(items)) 1147 1148 1149def ReplaceAvbSigningKeys(misc_info): 1150 """Replaces the AVB signing keys.""" 1151 1152 def ReplaceAvbPartitionSigningKey(partition): 1153 key = OPTIONS.avb_keys.get(partition) 1154 if not key: 1155 return 1156 1157 algorithm = OPTIONS.avb_algorithms.get(partition) 1158 assert algorithm, 'Missing AVB signing algorithm for %s' % (partition,) 1159 1160 print('Replacing AVB signing key for %s with "%s" (%s)' % ( 1161 partition, key, algorithm)) 1162 misc_info['avb_' + partition + '_algorithm'] = algorithm 1163 misc_info['avb_' + partition + '_key_path'] = key 1164 1165 extra_args = OPTIONS.avb_extra_args.get(partition) 1166 if extra_args: 1167 print('Setting extra AVB signing args for %s to "%s"' % ( 1168 partition, extra_args)) 1169 args_key = AVB_FOOTER_ARGS_BY_PARTITION.get( 1170 partition, 1171 # custom partition 1172 "avb_{}_add_hashtree_footer_args".format(partition)) 1173 misc_info[args_key] = (misc_info.get(args_key, '') + ' ' + extra_args) 1174 1175 for partition in AVB_FOOTER_ARGS_BY_PARTITION: 1176 ReplaceAvbPartitionSigningKey(partition) 1177 1178 for custom_partition in misc_info.get( 1179 "avb_custom_images_partition_list", "").strip().split(): 1180 ReplaceAvbPartitionSigningKey(custom_partition) 1181 1182 1183def RewriteAvbProps(misc_info): 1184 """Rewrites the props in AVB signing args.""" 1185 for partition, args_key in AVB_FOOTER_ARGS_BY_PARTITION.items(): 1186 args = misc_info.get(args_key) 1187 if not args: 1188 continue 1189 1190 tokens = [] 1191 changed = False 1192 for token in args.split(): 1193 fingerprint_key = 'com.android.build.{}.fingerprint'.format(partition) 1194 if not token.startswith(fingerprint_key): 1195 tokens.append(token) 1196 continue 1197 prefix, tag = token.rsplit('/', 1) 1198 tokens.append('{}/{}'.format(prefix, EditTags(tag))) 1199 changed = True 1200 1201 if changed: 1202 result = ' '.join(tokens) 1203 print('Rewriting AVB prop for {}:\n'.format(partition)) 1204 print(' replace: {}'.format(args)) 1205 print(' with: {}'.format(result)) 1206 misc_info[args_key] = result 1207 1208 1209def BuildKeyMap(misc_info, key_mapping_options): 1210 for s, d in key_mapping_options: 1211 if s is None: # -d option 1212 devkey = misc_info.get("default_system_dev_certificate", 1213 "build/make/target/product/security/testkey") 1214 devkeydir = os.path.dirname(devkey) 1215 1216 OPTIONS.key_map.update({ 1217 devkeydir + "/testkey": d + "/releasekey", 1218 devkeydir + "/devkey": d + "/releasekey", 1219 devkeydir + "/media": d + "/media", 1220 devkeydir + "/shared": d + "/shared", 1221 devkeydir + "/platform": d + "/platform", 1222 devkeydir + "/networkstack": d + "/networkstack", 1223 devkeydir + "/sdk_sandbox": d + "/sdk_sandbox", 1224 }) 1225 else: 1226 OPTIONS.key_map[s] = d 1227 1228 1229def GetApiLevelAndCodename(input_tf_zip): 1230 data = input_tf_zip.read("SYSTEM/build.prop").decode() 1231 api_level = None 1232 codename = None 1233 for line in data.split("\n"): 1234 line = line.strip() 1235 if line and line[0] != '#' and "=" in line: 1236 key, value = line.split("=", 1) 1237 key = key.strip() 1238 if key == "ro.build.version.sdk": 1239 api_level = int(value.strip()) 1240 elif key == "ro.build.version.codename": 1241 codename = value.strip() 1242 1243 if api_level is None: 1244 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop") 1245 if codename is None: 1246 raise ValueError("No ro.build.version.codename in SYSTEM/build.prop") 1247 1248 return (api_level, codename) 1249 1250 1251def GetCodenameToApiLevelMap(input_tf_zip): 1252 data = input_tf_zip.read("SYSTEM/build.prop").decode() 1253 api_level = None 1254 codenames = None 1255 for line in data.split("\n"): 1256 line = line.strip() 1257 if line and line[0] != '#' and "=" in line: 1258 key, value = line.split("=", 1) 1259 key = key.strip() 1260 if key == "ro.build.version.sdk": 1261 api_level = int(value.strip()) 1262 elif key == "ro.build.version.all_codenames": 1263 codenames = value.strip().split(",") 1264 1265 if api_level is None: 1266 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop") 1267 if codenames is None: 1268 raise ValueError("No ro.build.version.all_codenames in SYSTEM/build.prop") 1269 1270 result = {} 1271 for codename in codenames: 1272 codename = codename.strip() 1273 if codename: 1274 result[codename] = api_level 1275 return result 1276 1277 1278def ReadApexKeysInfo(tf_zip): 1279 """Parses the APEX keys info from a given target-files zip. 1280 1281 Given a target-files ZipFile, parses the META/apexkeys.txt entry and returns a 1282 dict that contains the mapping from APEX names (e.g. com.android.tzdata) to a 1283 tuple of (payload_key, container_key, sign_tool). 1284 1285 Args: 1286 tf_zip: The input target_files ZipFile (already open). 1287 1288 Returns: 1289 (payload_key, container_key, sign_tool): 1290 - payload_key contains the path to the payload signing key 1291 - container_key contains the path to the container signing key 1292 - sign_tool is an apex-specific signing tool for its payload contents 1293 """ 1294 keys = {} 1295 for line in tf_zip.read('META/apexkeys.txt').decode().split('\n'): 1296 line = line.strip() 1297 if not line: 1298 continue 1299 matches = re.match( 1300 r'^name="(?P<NAME>.*)"\s+' 1301 r'public_key="(?P<PAYLOAD_PUBLIC_KEY>.*)"\s+' 1302 r'private_key="(?P<PAYLOAD_PRIVATE_KEY>.*)"\s+' 1303 r'container_certificate="(?P<CONTAINER_CERT>.*)"\s+' 1304 r'container_private_key="(?P<CONTAINER_PRIVATE_KEY>.*?)"' 1305 r'(\s+partition="(?P<PARTITION>.*?)")?' 1306 r'(\s+sign_tool="(?P<SIGN_TOOL>.*?)")?$', 1307 line) 1308 if not matches: 1309 continue 1310 1311 name = matches.group('NAME') 1312 payload_private_key = matches.group("PAYLOAD_PRIVATE_KEY") 1313 1314 def CompareKeys(pubkey, pubkey_suffix, privkey, privkey_suffix): 1315 pubkey_suffix_len = len(pubkey_suffix) 1316 privkey_suffix_len = len(privkey_suffix) 1317 return (pubkey.endswith(pubkey_suffix) and 1318 privkey.endswith(privkey_suffix) and 1319 pubkey[:-pubkey_suffix_len] == privkey[:-privkey_suffix_len]) 1320 1321 # Check the container key names, as we'll carry them without the 1322 # extensions. This doesn't apply to payload keys though, which we will use 1323 # full names only. 1324 container_cert = matches.group("CONTAINER_CERT") 1325 container_private_key = matches.group("CONTAINER_PRIVATE_KEY") 1326 if container_cert == 'PRESIGNED' and container_private_key == 'PRESIGNED': 1327 container_key = 'PRESIGNED' 1328 elif CompareKeys( 1329 container_cert, OPTIONS.public_key_suffix, 1330 container_private_key, OPTIONS.private_key_suffix): 1331 container_key = container_cert[:-len(OPTIONS.public_key_suffix)] 1332 else: 1333 raise ValueError("Failed to parse container keys: \n{}".format(line)) 1334 1335 sign_tool = matches.group("SIGN_TOOL") 1336 keys[name] = (payload_private_key, container_key, sign_tool) 1337 1338 return keys 1339 1340 1341def BuildVendorPartitions(output_zip_path): 1342 """Builds OPTIONS.vendor_partitions using OPTIONS.vendor_otatools.""" 1343 if OPTIONS.vendor_partitions.difference(ALLOWED_VENDOR_PARTITIONS): 1344 logger.warning("Allowed --vendor_partitions: %s", 1345 ",".join(ALLOWED_VENDOR_PARTITIONS)) 1346 OPTIONS.vendor_partitions = ALLOWED_VENDOR_PARTITIONS.intersection( 1347 OPTIONS.vendor_partitions) 1348 1349 logger.info("Building vendor partitions using vendor otatools.") 1350 vendor_tempdir = common.UnzipTemp(output_zip_path, [ 1351 "META/*", 1352 "SYSTEM/build.prop", 1353 "RECOVERY/*", 1354 "BOOT/*", 1355 "OTA/", 1356 ] + ["{}/*".format(p.upper()) for p in OPTIONS.vendor_partitions]) 1357 1358 # Disable various partitions that build based on misc_info fields. 1359 # Only partitions in ALLOWED_VENDOR_PARTITIONS can be rebuilt using 1360 # vendor otatools. These other partitions will be rebuilt using the main 1361 # otatools if necessary. 1362 vendor_misc_info_path = os.path.join(vendor_tempdir, "META/misc_info.txt") 1363 vendor_misc_info = common.LoadDictionaryFromFile(vendor_misc_info_path) 1364 # Ignore if not rebuilding recovery 1365 if not OPTIONS.rebuild_recovery: 1366 vendor_misc_info["no_boot"] = "true" # boot 1367 vendor_misc_info["vendor_boot"] = "false" # vendor_boot 1368 vendor_misc_info["no_recovery"] = "true" # recovery 1369 vendor_misc_info["avb_enable"] = "false" # vbmeta 1370 1371 vendor_misc_info["has_dtbo"] = "false" # dtbo 1372 vendor_misc_info["has_pvmfw"] = "false" # pvmfw 1373 vendor_misc_info["avb_custom_images_partition_list"] = "" # avb custom images 1374 vendor_misc_info["avb_building_vbmeta_image"] = "false" # skip building vbmeta 1375 vendor_misc_info["custom_images_partition_list"] = "" # custom images 1376 vendor_misc_info["use_dynamic_partitions"] = "false" # super_empty 1377 vendor_misc_info["build_super_partition"] = "false" # super split 1378 vendor_misc_info["avb_vbmeta_system"] = "" # skip building vbmeta_system 1379 with open(vendor_misc_info_path, "w") as output: 1380 for key in sorted(vendor_misc_info): 1381 output.write("{}={}\n".format(key, vendor_misc_info[key])) 1382 1383 # Disable system partition by a placeholder of IMAGES/system.img, 1384 # instead of removing SYSTEM folder. 1385 # Because SYSTEM/build.prop is still needed for: 1386 # add_img_to_target_files.CreateImage -> 1387 # common.BuildInfo -> 1388 # common.BuildInfo.CalculateFingerprint 1389 vendor_images_path = os.path.join(vendor_tempdir, "IMAGES") 1390 if not os.path.exists(vendor_images_path): 1391 os.makedirs(vendor_images_path) 1392 with open(os.path.join(vendor_images_path, "system.img"), "w") as output: 1393 pass 1394 1395 # Disable care_map.pb as not all ab_partitions are available when 1396 # vendor otatools regenerates vendor images. 1397 if os.path.exists(os.path.join(vendor_tempdir, "META/ab_partitions.txt")): 1398 os.remove(os.path.join(vendor_tempdir, "META/ab_partitions.txt")) 1399 # Disable RADIO images 1400 if os.path.exists(os.path.join(vendor_tempdir, "META/pack_radioimages.txt")): 1401 os.remove(os.path.join(vendor_tempdir, "META/pack_radioimages.txt")) 1402 1403 # Build vendor images using vendor otatools. 1404 # Accept either a zip file or extracted directory. 1405 if os.path.isfile(OPTIONS.vendor_otatools): 1406 vendor_otatools_dir = common.MakeTempDir(prefix="vendor_otatools_") 1407 common.UnzipToDir(OPTIONS.vendor_otatools, vendor_otatools_dir) 1408 else: 1409 vendor_otatools_dir = OPTIONS.vendor_otatools 1410 cmd = [ 1411 os.path.join(vendor_otatools_dir, "bin", "add_img_to_target_files"), 1412 "--is_signing", 1413 "--add_missing", 1414 "--verbose", 1415 vendor_tempdir, 1416 ] 1417 if OPTIONS.rebuild_recovery: 1418 cmd.insert(4, "--rebuild_recovery") 1419 1420 common.RunAndCheckOutput(cmd, verbose=True) 1421 1422 logger.info("Writing vendor partitions to output archive.") 1423 with zipfile.ZipFile( 1424 output_zip_path, "a", compression=zipfile.ZIP_DEFLATED, 1425 allowZip64=True) as output_zip: 1426 for p in OPTIONS.vendor_partitions: 1427 img_file_path = "IMAGES/{}.img".format(p) 1428 map_file_path = "IMAGES/{}.map".format(p) 1429 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, img_file_path), img_file_path) 1430 if os.path.exists(os.path.join(vendor_tempdir, map_file_path)): 1431 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, map_file_path), map_file_path) 1432 # copy recovery.img, boot.img, recovery patch & install.sh 1433 if OPTIONS.rebuild_recovery: 1434 recovery_img = "IMAGES/recovery.img" 1435 boot_img = "IMAGES/boot.img" 1436 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, recovery_img), recovery_img) 1437 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, boot_img), boot_img) 1438 recovery_patch_path = "VENDOR/recovery-from-boot.p" 1439 recovery_sh_path = "VENDOR/bin/install-recovery.sh" 1440 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, recovery_patch_path), recovery_patch_path) 1441 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, recovery_sh_path), recovery_sh_path) 1442 1443 1444def main(argv): 1445 1446 key_mapping_options = [] 1447 1448 def option_handler(o, a): 1449 if o in ("-e", "--extra_apks"): 1450 names, key = a.split("=") 1451 names = names.split(",") 1452 for n in names: 1453 OPTIONS.extra_apks[n] = key 1454 elif o == "--extra_apex_payload_key": 1455 apex_names, key = a.split("=") 1456 for name in apex_names.split(","): 1457 OPTIONS.extra_apex_payload_keys[name] = key 1458 elif o == "--skip_apks_with_path_prefix": 1459 # Check the prefix, which must be in all upper case. 1460 prefix = a.split('/')[0] 1461 if not prefix or prefix != prefix.upper(): 1462 raise ValueError("Invalid path prefix '%s'" % (a,)) 1463 OPTIONS.skip_apks_with_path_prefix.add(a) 1464 elif o in ("-d", "--default_key_mappings"): 1465 key_mapping_options.append((None, a)) 1466 elif o in ("-k", "--key_mapping"): 1467 key_mapping_options.append(a.split("=", 1)) 1468 elif o in ("-o", "--replace_ota_keys"): 1469 OPTIONS.replace_ota_keys = True 1470 elif o in ("-t", "--tag_changes"): 1471 new = [] 1472 for i in a.split(","): 1473 i = i.strip() 1474 if not i or i[0] not in "-+": 1475 raise ValueError("Bad tag change '%s'" % (i,)) 1476 new.append(i[0] + i[1:].strip()) 1477 OPTIONS.tag_changes = tuple(new) 1478 elif o == "--replace_verity_public_key": 1479 raise ValueError("--replace_verity_public_key is no longer supported," 1480 " please switch to AVB") 1481 elif o == "--replace_verity_private_key": 1482 raise ValueError("--replace_verity_private_key is no longer supported," 1483 " please switch to AVB") 1484 elif o == "--replace_verity_keyid": 1485 raise ValueError("--replace_verity_keyid is no longer supported, please" 1486 " switch to AVB") 1487 elif o == "--remove_avb_public_keys": 1488 OPTIONS.remove_avb_public_keys = a.split(",") 1489 elif o == "--avb_vbmeta_key": 1490 OPTIONS.avb_keys['vbmeta'] = a 1491 elif o == "--avb_vbmeta_algorithm": 1492 OPTIONS.avb_algorithms['vbmeta'] = a 1493 elif o == "--avb_vbmeta_extra_args": 1494 OPTIONS.avb_extra_args['vbmeta'] = a 1495 elif o == "--avb_boot_key": 1496 OPTIONS.avb_keys['boot'] = a 1497 elif o == "--avb_boot_algorithm": 1498 OPTIONS.avb_algorithms['boot'] = a 1499 elif o == "--avb_boot_extra_args": 1500 OPTIONS.avb_extra_args['boot'] = a 1501 elif o == "--avb_dtbo_key": 1502 OPTIONS.avb_keys['dtbo'] = a 1503 elif o == "--avb_dtbo_algorithm": 1504 OPTIONS.avb_algorithms['dtbo'] = a 1505 elif o == "--avb_dtbo_extra_args": 1506 OPTIONS.avb_extra_args['dtbo'] = a 1507 elif o == "--avb_init_boot_key": 1508 OPTIONS.avb_keys['init_boot'] = a 1509 elif o == "--avb_init_boot_algorithm": 1510 OPTIONS.avb_algorithms['init_boot'] = a 1511 elif o == "--avb_init_boot_extra_args": 1512 OPTIONS.avb_extra_args['init_boot'] = a 1513 elif o == "--avb_recovery_key": 1514 OPTIONS.avb_keys['recovery'] = a 1515 elif o == "--avb_recovery_algorithm": 1516 OPTIONS.avb_algorithms['recovery'] = a 1517 elif o == "--avb_recovery_extra_args": 1518 OPTIONS.avb_extra_args['recovery'] = a 1519 elif o == "--avb_system_key": 1520 OPTIONS.avb_keys['system'] = a 1521 elif o == "--avb_system_algorithm": 1522 OPTIONS.avb_algorithms['system'] = a 1523 elif o == "--avb_system_extra_args": 1524 OPTIONS.avb_extra_args['system'] = a 1525 elif o == "--avb_system_other_key": 1526 OPTIONS.avb_keys['system_other'] = a 1527 elif o == "--avb_system_other_algorithm": 1528 OPTIONS.avb_algorithms['system_other'] = a 1529 elif o == "--avb_system_other_extra_args": 1530 OPTIONS.avb_extra_args['system_other'] = a 1531 elif o == "--avb_vendor_key": 1532 OPTIONS.avb_keys['vendor'] = a 1533 elif o == "--avb_vendor_algorithm": 1534 OPTIONS.avb_algorithms['vendor'] = a 1535 elif o == "--avb_vendor_extra_args": 1536 OPTIONS.avb_extra_args['vendor'] = a 1537 elif o == "--avb_vbmeta_system_key": 1538 OPTIONS.avb_keys['vbmeta_system'] = a 1539 elif o == "--avb_vbmeta_system_algorithm": 1540 OPTIONS.avb_algorithms['vbmeta_system'] = a 1541 elif o == "--avb_vbmeta_system_extra_args": 1542 OPTIONS.avb_extra_args['vbmeta_system'] = a 1543 elif o == "--avb_vbmeta_vendor_key": 1544 OPTIONS.avb_keys['vbmeta_vendor'] = a 1545 elif o == "--avb_vbmeta_vendor_algorithm": 1546 OPTIONS.avb_algorithms['vbmeta_vendor'] = a 1547 elif o == "--avb_vbmeta_vendor_extra_args": 1548 OPTIONS.avb_extra_args['vbmeta_vendor'] = a 1549 elif o == "--avb_apex_extra_args": 1550 OPTIONS.avb_extra_args['apex'] = a 1551 elif o == "--avb_extra_custom_image_key": 1552 partition, key = a.split("=") 1553 OPTIONS.avb_keys[partition] = key 1554 elif o == "--avb_extra_custom_image_algorithm": 1555 partition, algorithm = a.split("=") 1556 OPTIONS.avb_algorithms[partition] = algorithm 1557 elif o == "--avb_extra_custom_image_extra_args": 1558 # Setting the maxsplit parameter to one, which will return a list with 1559 # two elements. e.g., the second '=' should not be splitted for 1560 # 'oem=--signing_helper_with_files=/tmp/avbsigner.sh'. 1561 partition, extra_args = a.split("=", 1) 1562 OPTIONS.avb_extra_args[partition] = extra_args 1563 elif o == "--vendor_otatools": 1564 OPTIONS.vendor_otatools = a 1565 elif o == "--vendor_partitions": 1566 OPTIONS.vendor_partitions = set(a.split(",")) 1567 elif o == "--allow_gsi_debug_sepolicy": 1568 OPTIONS.allow_gsi_debug_sepolicy = True 1569 elif o == "--override_apk_keys": 1570 OPTIONS.override_apk_keys = a 1571 elif o == "--override_apex_keys": 1572 OPTIONS.override_apex_keys = a 1573 elif o in ("--gki_signing_key", "--gki_signing_algorithm", "--gki_signing_extra_args"): 1574 print(f"{o} is deprecated and does nothing") 1575 else: 1576 return False 1577 return True 1578 1579 args = common.ParseOptions( 1580 argv, __doc__, 1581 extra_opts="e:d:k:ot:", 1582 extra_long_opts=[ 1583 "extra_apks=", 1584 "extra_apex_payload_key=", 1585 "skip_apks_with_path_prefix=", 1586 "default_key_mappings=", 1587 "key_mapping=", 1588 "replace_ota_keys", 1589 "tag_changes=", 1590 "replace_verity_public_key=", 1591 "replace_verity_private_key=", 1592 "replace_verity_keyid=", 1593 "remove_avb_public_keys=", 1594 "avb_apex_extra_args=", 1595 "avb_vbmeta_algorithm=", 1596 "avb_vbmeta_key=", 1597 "avb_vbmeta_extra_args=", 1598 "avb_boot_algorithm=", 1599 "avb_boot_key=", 1600 "avb_boot_extra_args=", 1601 "avb_dtbo_algorithm=", 1602 "avb_dtbo_key=", 1603 "avb_dtbo_extra_args=", 1604 "avb_init_boot_algorithm=", 1605 "avb_init_boot_key=", 1606 "avb_init_boot_extra_args=", 1607 "avb_recovery_algorithm=", 1608 "avb_recovery_key=", 1609 "avb_recovery_extra_args=", 1610 "avb_system_algorithm=", 1611 "avb_system_key=", 1612 "avb_system_extra_args=", 1613 "avb_system_other_algorithm=", 1614 "avb_system_other_key=", 1615 "avb_system_other_extra_args=", 1616 "avb_vendor_algorithm=", 1617 "avb_vendor_key=", 1618 "avb_vendor_extra_args=", 1619 "avb_vbmeta_system_algorithm=", 1620 "avb_vbmeta_system_key=", 1621 "avb_vbmeta_system_extra_args=", 1622 "avb_vbmeta_vendor_algorithm=", 1623 "avb_vbmeta_vendor_key=", 1624 "avb_vbmeta_vendor_extra_args=", 1625 "avb_extra_custom_image_key=", 1626 "avb_extra_custom_image_algorithm=", 1627 "avb_extra_custom_image_extra_args=", 1628 "gki_signing_key=", 1629 "gki_signing_algorithm=", 1630 "gki_signing_extra_args=", 1631 "vendor_partitions=", 1632 "vendor_otatools=", 1633 "allow_gsi_debug_sepolicy", 1634 "override_apk_keys=", 1635 "override_apex_keys=", 1636 ], 1637 extra_option_handler=[option_handler, payload_signer.signer_options]) 1638 1639 if len(args) != 2: 1640 common.Usage(__doc__) 1641 sys.exit(1) 1642 1643 common.InitLogging() 1644 1645 input_zip = zipfile.ZipFile(args[0], "r", allowZip64=True) 1646 output_zip = zipfile.ZipFile(args[1], "w", 1647 compression=zipfile.ZIP_DEFLATED, 1648 allowZip64=True) 1649 1650 misc_info = common.LoadInfoDict(input_zip) 1651 if OPTIONS.package_key is None: 1652 OPTIONS.package_key = misc_info.get( 1653 "default_system_dev_certificate", 1654 "build/make/target/product/security/testkey") 1655 1656 BuildKeyMap(misc_info, key_mapping_options) 1657 1658 apk_keys_info, compressed_extension = common.ReadApkCerts(input_zip) 1659 apk_keys = GetApkCerts(apk_keys_info) 1660 1661 apex_keys_info = ReadApexKeysInfo(input_zip) 1662 apex_keys = GetApexKeys(apex_keys_info, apk_keys) 1663 1664 # TODO(xunchang) check for the apks inside the apex files, and abort early if 1665 # the keys are not available. 1666 CheckApkAndApexKeysAvailable( 1667 input_zip, 1668 set(apk_keys.keys()) | set(apex_keys.keys()), 1669 compressed_extension, 1670 apex_keys) 1671 1672 key_passwords = common.GetKeyPasswords( 1673 set(apk_keys.values()) | set(itertools.chain(*apex_keys.values()))) 1674 platform_api_level, _ = GetApiLevelAndCodename(input_zip) 1675 codename_to_api_level_map = GetCodenameToApiLevelMap(input_zip) 1676 1677 ProcessTargetFiles(input_zip, output_zip, misc_info, 1678 apk_keys, apex_keys, key_passwords, 1679 platform_api_level, codename_to_api_level_map, 1680 compressed_extension) 1681 1682 common.ZipClose(input_zip) 1683 common.ZipClose(output_zip) 1684 1685 if OPTIONS.vendor_partitions and OPTIONS.vendor_otatools: 1686 BuildVendorPartitions(args[1]) 1687 1688 # Skip building userdata.img and cache.img when signing the target files. 1689 new_args = ["--is_signing", "--add_missing", "--verbose"] 1690 # add_img_to_target_files builds the system image from scratch, so the 1691 # recovery patch is guaranteed to be regenerated there. 1692 if OPTIONS.rebuild_recovery: 1693 new_args.append("--rebuild_recovery") 1694 new_args.append(args[1]) 1695 add_img_to_target_files.main(new_args) 1696 1697 print("done.") 1698 1699 1700if __name__ == '__main__': 1701 try: 1702 main(sys.argv[1:]) 1703 finally: 1704 common.Cleanup() 1705