1#!/usr/bin/env python 2# 3# Copyright (C) 2014 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""" 18Given a target-files zipfile that does not contain images (ie, does 19not have an IMAGES/ top-level subdirectory), produce the images and 20add them to the zipfile. 21 22Usage: add_img_to_target_files [flag] target_files 23 24 -a (--add_missing) 25 Build and add missing images to "IMAGES/". If this option is 26 not specified, this script will simply exit when "IMAGES/" 27 directory exists in the target file. 28 29 -r (--rebuild_recovery) 30 Rebuild the recovery patch and write it to the system image. Only 31 meaningful when system image needs to be rebuilt and there're separate 32 boot / recovery images. 33 34 --replace_verity_private_key 35 Replace the private key used for verity signing. (same as the option 36 in sign_target_files_apks) 37 38 --replace_verity_public_key 39 Replace the certificate (public key) used for verity verification. (same 40 as the option in sign_target_files_apks) 41 42 --is_signing 43 Skip building & adding the images for "userdata" and "cache" if we 44 are signing the target files. 45""" 46 47from __future__ import print_function 48 49import datetime 50import logging 51import os 52import shlex 53import shutil 54import stat 55import sys 56import uuid 57import zipfile 58 59import build_image 60import build_super_image 61import common 62import verity_utils 63import ota_metadata_pb2 64 65from apex_utils import GetApexInfoFromTargetFiles 66from common import AddCareMapForAbOta 67 68if sys.hexversion < 0x02070000: 69 print("Python 2.7 or newer is required.", file=sys.stderr) 70 sys.exit(1) 71 72logger = logging.getLogger(__name__) 73 74OPTIONS = common.OPTIONS 75OPTIONS.add_missing = False 76OPTIONS.rebuild_recovery = False 77OPTIONS.replace_updated_files_list = [] 78OPTIONS.replace_verity_public_key = False 79OPTIONS.replace_verity_private_key = False 80OPTIONS.is_signing = False 81 82# Use a fixed timestamp (01/01/2009 00:00:00 UTC) for files when packaging 83# images. (b/24377993, b/80600931) 84FIXED_FILE_TIMESTAMP = int(( 85 datetime.datetime(2009, 1, 1, 0, 0, 0, 0, None) - 86 datetime.datetime.utcfromtimestamp(0)).total_seconds()) 87 88 89class OutputFile(object): 90 """A helper class to write a generated file to the given dir or zip. 91 92 When generating images, we want the outputs to go into the given zip file, or 93 the given dir. 94 95 Attributes: 96 name: The name of the output file, regardless of the final destination. 97 """ 98 99 def __init__(self, output_zip, input_dir, *args): 100 # We write the intermediate output file under the given input_dir, even if 101 # the final destination is a zip archive. 102 self.name = os.path.join(input_dir, *args) 103 self._output_zip = output_zip 104 if self._output_zip: 105 self._zip_name = os.path.join(*args) 106 107 def Write(self): 108 if self._output_zip: 109 common.ZipWrite(self._output_zip, self.name, self._zip_name) 110 111 112def AddSystem(output_zip, recovery_img=None, boot_img=None): 113 """Turn the contents of SYSTEM into a system image and store it in 114 output_zip. Returns the name of the system image file.""" 115 116 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "system.img") 117 if os.path.exists(img.name): 118 logger.info("system.img already exists; no need to rebuild...") 119 return img.name 120 121 def output_sink(fn, data): 122 output_file = os.path.join(OPTIONS.input_tmp, "SYSTEM", fn) 123 with open(output_file, "wb") as ofile: 124 ofile.write(data) 125 126 if output_zip: 127 arc_name = "SYSTEM/" + fn 128 if arc_name in output_zip.namelist(): 129 OPTIONS.replace_updated_files_list.append(arc_name) 130 else: 131 common.ZipWrite(output_zip, output_file, arc_name) 132 133 board_uses_vendorimage = OPTIONS.info_dict.get( 134 "board_uses_vendorimage") == "true" 135 136 if (OPTIONS.rebuild_recovery and not board_uses_vendorimage and 137 recovery_img is not None and boot_img is not None): 138 logger.info("Building new recovery patch on system at system/vendor") 139 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink, recovery_img, 140 boot_img, info_dict=OPTIONS.info_dict) 141 142 block_list = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "system.map") 143 CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "system", img, 144 block_list=block_list) 145 return img.name 146 147 148def AddSystemOther(output_zip): 149 """Turn the contents of SYSTEM_OTHER into a system_other image 150 and store it in output_zip.""" 151 152 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "system_other.img") 153 if os.path.exists(img.name): 154 logger.info("system_other.img already exists; no need to rebuild...") 155 return 156 157 CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "system_other", img) 158 159 160def AddVendor(output_zip, recovery_img=None, boot_img=None): 161 """Turn the contents of VENDOR into a vendor image and store in it 162 output_zip.""" 163 164 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "vendor.img") 165 if os.path.exists(img.name): 166 logger.info("vendor.img already exists; no need to rebuild...") 167 return img.name 168 169 def output_sink(fn, data): 170 ofile = open(os.path.join(OPTIONS.input_tmp, "VENDOR", fn), "w") 171 ofile.write(data) 172 ofile.close() 173 174 if output_zip: 175 arc_name = "VENDOR/" + fn 176 if arc_name in output_zip.namelist(): 177 OPTIONS.replace_updated_files_list.append(arc_name) 178 else: 179 common.ZipWrite(output_zip, ofile.name, arc_name) 180 181 board_uses_vendorimage = OPTIONS.info_dict.get( 182 "board_uses_vendorimage") == "true" 183 184 if (OPTIONS.rebuild_recovery and board_uses_vendorimage and 185 recovery_img is not None and boot_img is not None): 186 logger.info("Building new recovery patch on vendor") 187 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink, recovery_img, 188 boot_img, info_dict=OPTIONS.info_dict) 189 190 block_list = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "vendor.map") 191 CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "vendor", img, 192 block_list=block_list) 193 return img.name 194 195 196def AddProduct(output_zip): 197 """Turn the contents of PRODUCT into a product image and store it in 198 output_zip.""" 199 200 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "product.img") 201 if os.path.exists(img.name): 202 logger.info("product.img already exists; no need to rebuild...") 203 return img.name 204 205 block_list = OutputFile( 206 output_zip, OPTIONS.input_tmp, "IMAGES", "product.map") 207 CreateImage( 208 OPTIONS.input_tmp, OPTIONS.info_dict, "product", img, 209 block_list=block_list) 210 return img.name 211 212 213def AddSystemExt(output_zip): 214 """Turn the contents of SYSTEM_EXT into a system_ext image and store it in 215 output_zip.""" 216 217 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", 218 "system_ext.img") 219 if os.path.exists(img.name): 220 logger.info("system_ext.img already exists; no need to rebuild...") 221 return img.name 222 223 block_list = OutputFile( 224 output_zip, OPTIONS.input_tmp, "IMAGES", "system_ext.map") 225 CreateImage( 226 OPTIONS.input_tmp, OPTIONS.info_dict, "system_ext", img, 227 block_list=block_list) 228 return img.name 229 230 231def AddOdm(output_zip): 232 """Turn the contents of ODM into an odm image and store it in output_zip.""" 233 234 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "odm.img") 235 if os.path.exists(img.name): 236 logger.info("odm.img already exists; no need to rebuild...") 237 return img.name 238 239 block_list = OutputFile( 240 output_zip, OPTIONS.input_tmp, "IMAGES", "odm.map") 241 CreateImage( 242 OPTIONS.input_tmp, OPTIONS.info_dict, "odm", img, 243 block_list=block_list) 244 return img.name 245 246 247def AddVendorDlkm(output_zip): 248 """Turn the contents of VENDOR_DLKM into an vendor_dlkm image and store it in output_zip.""" 249 250 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "vendor_dlkm.img") 251 if os.path.exists(img.name): 252 logger.info("vendor_dlkm.img already exists; no need to rebuild...") 253 return img.name 254 255 block_list = OutputFile( 256 output_zip, OPTIONS.input_tmp, "IMAGES", "vendor_dlkm.map") 257 CreateImage( 258 OPTIONS.input_tmp, OPTIONS.info_dict, "vendor_dlkm", img, 259 block_list=block_list) 260 return img.name 261 262 263def AddOdmDlkm(output_zip): 264 """Turn the contents of OdmDlkm into an odm_dlkm image and store it in output_zip.""" 265 266 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "odm_dlkm.img") 267 if os.path.exists(img.name): 268 logger.info("odm_dlkm.img already exists; no need to rebuild...") 269 return img.name 270 271 block_list = OutputFile( 272 output_zip, OPTIONS.input_tmp, "IMAGES", "odm_dlkm.map") 273 CreateImage( 274 OPTIONS.input_tmp, OPTIONS.info_dict, "odm_dlkm", img, 275 block_list=block_list) 276 return img.name 277 278 279def AddDtbo(output_zip): 280 """Adds the DTBO image. 281 282 Uses the image under IMAGES/ if it already exists. Otherwise looks for the 283 image under PREBUILT_IMAGES/, signs it as needed, and returns the image name. 284 """ 285 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "dtbo.img") 286 if os.path.exists(img.name): 287 logger.info("dtbo.img already exists; no need to rebuild...") 288 return img.name 289 290 dtbo_prebuilt_path = os.path.join( 291 OPTIONS.input_tmp, "PREBUILT_IMAGES", "dtbo.img") 292 assert os.path.exists(dtbo_prebuilt_path) 293 shutil.copy(dtbo_prebuilt_path, img.name) 294 295 # AVB-sign the image as needed. 296 if OPTIONS.info_dict.get("avb_enable") == "true": 297 # Signing requires +w 298 os.chmod(img.name, os.stat(img.name).st_mode | stat.S_IWUSR) 299 300 avbtool = OPTIONS.info_dict["avb_avbtool"] 301 part_size = OPTIONS.info_dict["dtbo_size"] 302 # The AVB hash footer will be replaced if already present. 303 cmd = [avbtool, "add_hash_footer", "--image", img.name, 304 "--partition_size", str(part_size), "--partition_name", "dtbo"] 305 common.AppendAVBSigningArgs(cmd, "dtbo") 306 args = OPTIONS.info_dict.get("avb_dtbo_add_hash_footer_args") 307 if args and args.strip(): 308 cmd.extend(shlex.split(args)) 309 common.RunAndCheckOutput(cmd) 310 311 img.Write() 312 return img.name 313 314 315def AddPvmfw(output_zip): 316 """Adds the pvmfw image. 317 318 Uses the image under IMAGES/ if it already exists. Otherwise looks for the 319 image under PREBUILT_IMAGES/, signs it as needed, and returns the image name. 320 """ 321 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "pvmfw.img") 322 if os.path.exists(img.name): 323 logger.info("pvmfw.img already exists; no need to rebuild...") 324 return img.name 325 326 pvmfw_prebuilt_path = os.path.join( 327 OPTIONS.input_tmp, "PREBUILT_IMAGES", "pvmfw.img") 328 assert os.path.exists(pvmfw_prebuilt_path) 329 shutil.copy(pvmfw_prebuilt_path, img.name) 330 331 # AVB-sign the image as needed. 332 if OPTIONS.info_dict.get("avb_enable") == "true": 333 # Signing requires +w 334 os.chmod(img.name, os.stat(img.name).st_mode | stat.S_IWUSR) 335 336 avbtool = OPTIONS.info_dict["avb_avbtool"] 337 part_size = OPTIONS.info_dict["pvmfw_size"] 338 # The AVB hash footer will be replaced if already present. 339 cmd = [avbtool, "add_hash_footer", "--image", img.name, 340 "--partition_size", str(part_size), "--partition_name", "pvmfw"] 341 common.AppendAVBSigningArgs(cmd, "pvmfw") 342 args = OPTIONS.info_dict.get("avb_pvmfw_add_hash_footer_args") 343 if args and args.strip(): 344 cmd.extend(shlex.split(args)) 345 common.RunAndCheckOutput(cmd) 346 347 img.Write() 348 return img.name 349 350 351def AddCustomImages(output_zip, partition_name): 352 """Adds and signs custom images in IMAGES/. 353 354 Args: 355 output_zip: The output zip file (needs to be already open), or None to 356 write images to OPTIONS.input_tmp/. 357 358 Uses the image under IMAGES/ if it already exists. Otherwise looks for the 359 image under PREBUILT_IMAGES/, signs it as needed, and returns the image name. 360 361 Raises: 362 AssertionError: If image can't be found. 363 """ 364 365 key_path = OPTIONS.info_dict.get("avb_{}_key_path".format(partition_name)) 366 algorithm = OPTIONS.info_dict.get("avb_{}_algorithm".format(partition_name)) 367 extra_args = OPTIONS.info_dict.get( 368 "avb_{}_add_hashtree_footer_args".format(partition_name)) 369 partition_size = OPTIONS.info_dict.get( 370 "avb_{}_partition_size".format(partition_name)) 371 372 builder = verity_utils.CreateCustomImageBuilder( 373 OPTIONS.info_dict, partition_name, partition_size, 374 key_path, algorithm, extra_args) 375 376 for img_name in OPTIONS.info_dict.get( 377 "avb_{}_image_list".format(partition_name)).split(): 378 custom_image = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", img_name) 379 if os.path.exists(custom_image.name): 380 continue 381 382 custom_image_prebuilt_path = os.path.join( 383 OPTIONS.input_tmp, "PREBUILT_IMAGES", img_name) 384 assert os.path.exists(custom_image_prebuilt_path), \ 385 "Failed to find %s at %s" % (img_name, custom_image_prebuilt_path) 386 387 shutil.copy(custom_image_prebuilt_path, custom_image.name) 388 389 if builder is not None: 390 builder.Build(custom_image.name) 391 392 custom_image.Write() 393 394 default = os.path.join(OPTIONS.input_tmp, "IMAGES", partition_name + ".img") 395 assert os.path.exists(default), \ 396 "There should be one %s.img" % (partition_name) 397 return default 398 399 400def CreateImage(input_dir, info_dict, what, output_file, block_list=None): 401 logger.info("creating %s.img...", what) 402 403 image_props = build_image.ImagePropFromGlobalDict(info_dict, what) 404 image_props["timestamp"] = FIXED_FILE_TIMESTAMP 405 406 if what == "system": 407 fs_config_prefix = "" 408 else: 409 fs_config_prefix = what + "_" 410 411 fs_config = os.path.join( 412 input_dir, "META/" + fs_config_prefix + "filesystem_config.txt") 413 if not os.path.exists(fs_config): 414 fs_config = None 415 416 # Override values loaded from info_dict. 417 if fs_config: 418 image_props["fs_config"] = fs_config 419 if block_list: 420 image_props["block_list"] = block_list.name 421 422 # Use repeatable ext4 FS UUID and hash_seed UUID (based on partition name and 423 # build fingerprint). Also use the legacy build id, because the vbmeta digest 424 # isn't available at this point. 425 build_info = common.BuildInfo(info_dict, use_legacy_id=True) 426 uuid_seed = what + "-" + build_info.GetPartitionFingerprint(what) 427 image_props["uuid"] = str(uuid.uuid5(uuid.NAMESPACE_URL, uuid_seed)) 428 hash_seed = "hash_seed-" + uuid_seed 429 image_props["hash_seed"] = str(uuid.uuid5(uuid.NAMESPACE_URL, hash_seed)) 430 431 build_image.BuildImage( 432 os.path.join(input_dir, what.upper()), image_props, output_file.name) 433 434 output_file.Write() 435 if block_list: 436 block_list.Write() 437 438 # Set the '_image_size' for given image size. 439 is_verity_partition = "verity_block_device" in image_props 440 verity_supported = (image_props.get("verity") == "true" or 441 image_props.get("avb_enable") == "true") 442 is_avb_enable = image_props.get("avb_hashtree_enable") == "true" 443 if verity_supported and (is_verity_partition or is_avb_enable): 444 image_size = image_props.get("image_size") 445 if image_size: 446 image_size_key = what + "_image_size" 447 info_dict[image_size_key] = int(image_size) 448 449 use_dynamic_size = ( 450 info_dict.get("use_dynamic_partition_size") == "true" and 451 what in shlex.split(info_dict.get("dynamic_partition_list", "").strip())) 452 if use_dynamic_size: 453 info_dict.update(build_image.GlobalDictFromImageProp(image_props, what)) 454 455 456def AddUserdata(output_zip): 457 """Create a userdata image and store it in output_zip. 458 459 In most case we just create and store an empty userdata.img; 460 But the invoker can also request to create userdata.img with real 461 data from the target files, by setting "userdata_img_with_data=true" 462 in OPTIONS.info_dict. 463 """ 464 465 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "userdata.img") 466 if os.path.exists(img.name): 467 logger.info("userdata.img already exists; no need to rebuild...") 468 return 469 470 # Skip userdata.img if no size. 471 image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, "data") 472 if not image_props.get("partition_size"): 473 return 474 475 logger.info("creating userdata.img...") 476 477 image_props["timestamp"] = FIXED_FILE_TIMESTAMP 478 479 if OPTIONS.info_dict.get("userdata_img_with_data") == "true": 480 user_dir = os.path.join(OPTIONS.input_tmp, "DATA") 481 else: 482 user_dir = common.MakeTempDir() 483 484 build_image.BuildImage(user_dir, image_props, img.name) 485 486 common.CheckSize(img.name, "userdata.img", OPTIONS.info_dict) 487 img.Write() 488 489 490def AddVBMeta(output_zip, partitions, name, needed_partitions): 491 """Creates a VBMeta image and stores it in output_zip. 492 493 It generates the requested VBMeta image. The requested image could be for 494 top-level or chained VBMeta image, which is determined based on the name. 495 496 Args: 497 output_zip: The output zip file, which needs to be already open. 498 partitions: A dict that's keyed by partition names with image paths as 499 values. Only valid partition names are accepted, as partitions listed 500 in common.AVB_PARTITIONS and custom partitions listed in 501 OPTIONS.info_dict.get("avb_custom_images_partition_list") 502 name: Name of the VBMeta partition, e.g. 'vbmeta', 'vbmeta_system'. 503 needed_partitions: Partitions whose descriptors should be included into the 504 generated VBMeta image. 505 506 Returns: 507 Path to the created image. 508 509 Raises: 510 AssertionError: On invalid input args. 511 """ 512 assert needed_partitions, "Needed partitions must be specified" 513 514 img = OutputFile( 515 output_zip, OPTIONS.input_tmp, "IMAGES", "{}.img".format(name)) 516 if os.path.exists(img.name): 517 logger.info("%s.img already exists; not rebuilding...", name) 518 return img.name 519 520 common.BuildVBMeta(img.name, partitions, name, needed_partitions) 521 img.Write() 522 return img.name 523 524 525def AddPartitionTable(output_zip): 526 """Create a partition table image and store it in output_zip.""" 527 528 img = OutputFile( 529 output_zip, OPTIONS.input_tmp, "IMAGES", "partition-table.img") 530 bpt = OutputFile( 531 output_zip, OPTIONS.input_tmp, "META", "partition-table.bpt") 532 533 # use BPTTOOL from environ, or "bpttool" if empty or not set. 534 bpttool = os.getenv("BPTTOOL") or "bpttool" 535 cmd = [bpttool, "make_table", "--output_json", bpt.name, 536 "--output_gpt", img.name] 537 input_files_str = OPTIONS.info_dict["board_bpt_input_files"] 538 input_files = input_files_str.split(" ") 539 for i in input_files: 540 cmd.extend(["--input", i]) 541 disk_size = OPTIONS.info_dict.get("board_bpt_disk_size") 542 if disk_size: 543 cmd.extend(["--disk_size", disk_size]) 544 args = OPTIONS.info_dict.get("board_bpt_make_table_args") 545 if args: 546 cmd.extend(shlex.split(args)) 547 common.RunAndCheckOutput(cmd) 548 549 img.Write() 550 bpt.Write() 551 552 553def AddCache(output_zip): 554 """Create an empty cache image and store it in output_zip.""" 555 556 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "cache.img") 557 if os.path.exists(img.name): 558 logger.info("cache.img already exists; no need to rebuild...") 559 return 560 561 image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, "cache") 562 # The build system has to explicitly request for cache.img. 563 if "fs_type" not in image_props: 564 return 565 566 logger.info("creating cache.img...") 567 568 image_props["timestamp"] = FIXED_FILE_TIMESTAMP 569 570 user_dir = common.MakeTempDir() 571 build_image.BuildImage(user_dir, image_props, img.name) 572 573 common.CheckSize(img.name, "cache.img", OPTIONS.info_dict) 574 img.Write() 575 576 577def CheckAbOtaImages(output_zip, ab_partitions): 578 """Checks that all the listed A/B partitions have their images available. 579 580 The images need to be available under IMAGES/ or RADIO/, with the former takes 581 a priority. 582 583 Args: 584 output_zip: The output zip file (needs to be already open), or None to 585 find images in OPTIONS.input_tmp/. 586 ab_partitions: The list of A/B partitions. 587 588 Raises: 589 AssertionError: If it can't find an image. 590 """ 591 for partition in ab_partitions: 592 img_name = partition.strip() + ".img" 593 594 # Assert that the image is present under IMAGES/ now. 595 if output_zip: 596 # Zip spec says: All slashes MUST be forward slashes. 597 images_path = "IMAGES/" + img_name 598 radio_path = "RADIO/" + img_name 599 available = (images_path in output_zip.namelist() or 600 radio_path in output_zip.namelist()) 601 else: 602 images_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name) 603 radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name) 604 available = os.path.exists(images_path) or os.path.exists(radio_path) 605 606 assert available, "Failed to find " + img_name 607 608 609def AddPackRadioImages(output_zip, images): 610 """Copies images listed in META/pack_radioimages.txt from RADIO/ to IMAGES/. 611 612 Args: 613 output_zip: The output zip file (needs to be already open), or None to 614 write images to OPTIONS.input_tmp/. 615 images: A list of image names. 616 617 Raises: 618 AssertionError: If a listed image can't be found. 619 """ 620 for image in images: 621 img_name = image.strip() 622 _, ext = os.path.splitext(img_name) 623 if not ext: 624 img_name += ".img" 625 626 prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name) 627 if os.path.exists(prebuilt_path): 628 logger.info("%s already exists, no need to overwrite...", img_name) 629 continue 630 631 img_radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name) 632 assert os.path.exists(img_radio_path), \ 633 "Failed to find %s at %s" % (img_name, img_radio_path) 634 635 if output_zip: 636 common.ZipWrite(output_zip, img_radio_path, "IMAGES/" + img_name) 637 else: 638 shutil.copy(img_radio_path, prebuilt_path) 639 640 641def AddSuperEmpty(output_zip): 642 """Create a super_empty.img and store it in output_zip.""" 643 644 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "super_empty.img") 645 build_super_image.BuildSuperImage(OPTIONS.info_dict, img.name) 646 img.Write() 647 648 649def AddSuperSplit(output_zip): 650 """Create split super_*.img and store it in output_zip.""" 651 652 outdir = os.path.join(OPTIONS.input_tmp, "OTA") 653 built = build_super_image.BuildSuperImage(OPTIONS.input_tmp, outdir) 654 655 if built: 656 for dev in OPTIONS.info_dict['super_block_devices'].strip().split(): 657 img = OutputFile(output_zip, OPTIONS.input_tmp, "OTA", 658 "super_" + dev + ".img") 659 img.Write() 660 661 662def ReplaceUpdatedFiles(zip_filename, files_list): 663 """Updates all the ZIP entries listed in files_list. 664 665 For now the list includes META/care_map.pb, and the related files under 666 SYSTEM/ after rebuilding recovery. 667 """ 668 common.ZipDelete(zip_filename, files_list) 669 output_zip = zipfile.ZipFile(zip_filename, "a", 670 compression=zipfile.ZIP_DEFLATED, 671 allowZip64=True) 672 for item in files_list: 673 file_path = os.path.join(OPTIONS.input_tmp, item) 674 assert os.path.exists(file_path) 675 common.ZipWrite(output_zip, file_path, arcname=item) 676 common.ZipClose(output_zip) 677 678 679def HasPartition(partition_name): 680 """Determines if the target files archive should build a given partition.""" 681 682 return ((os.path.isdir( 683 os.path.join(OPTIONS.input_tmp, partition_name.upper())) and 684 OPTIONS.info_dict.get( 685 "building_{}_image".format(partition_name)) == "true") or 686 os.path.exists( 687 os.path.join(OPTIONS.input_tmp, "IMAGES", 688 "{}.img".format(partition_name)))) 689 690def AddApexInfo(output_zip): 691 apex_infos = GetApexInfoFromTargetFiles(OPTIONS.input_tmp, 'system') 692 apex_metadata_proto = ota_metadata_pb2.ApexMetadata() 693 apex_metadata_proto.apex_info.extend(apex_infos) 694 apex_info_bytes = apex_metadata_proto.SerializeToString() 695 696 output_file = os.path.join(OPTIONS.input_tmp, "META", "apex_info.pb") 697 with open(output_file, "wb") as ofile: 698 ofile.write(apex_info_bytes) 699 if output_zip: 700 arc_name = "META/apex_info.pb" 701 if arc_name in output_zip.namelist(): 702 OPTIONS.replace_updated_files_list.append(arc_name) 703 else: 704 common.ZipWrite(output_zip, output_file, arc_name) 705 706 707def AddVbmetaDigest(output_zip): 708 """Write the vbmeta digest to the output dir and zipfile.""" 709 710 # Calculate the vbmeta digest and put the result in to META/ 711 boot_images = OPTIONS.info_dict.get("boot_images") 712 # Disable the digest calculation if the target_file is used as a container 713 # for boot images. 714 boot_container = boot_images and len(boot_images.split()) >= 2 715 if (OPTIONS.info_dict.get("avb_enable") == "true" and not boot_container and 716 OPTIONS.info_dict.get("avb_building_vbmeta_image") == "true"): 717 avbtool = OPTIONS.info_dict["avb_avbtool"] 718 digest = verity_utils.CalculateVbmetaDigest(OPTIONS.input_tmp, avbtool) 719 vbmeta_digest_txt = os.path.join(OPTIONS.input_tmp, "META", 720 "vbmeta_digest.txt") 721 with open(vbmeta_digest_txt, 'w') as f: 722 f.write(digest) 723 # writes to the output zipfile 724 if output_zip: 725 arc_name = "META/vbmeta_digest.txt" 726 if arc_name in output_zip.namelist(): 727 OPTIONS.replace_updated_files_list.append(arc_name) 728 else: 729 common.ZipWriteStr(output_zip, arc_name, digest) 730 731 732def AddImagesToTargetFiles(filename): 733 """Creates and adds images (boot/recovery/system/...) to a target_files.zip. 734 735 It works with either a zip file (zip mode), or a directory that contains the 736 files to be packed into a target_files.zip (dir mode). The latter is used when 737 being called from build/make/core/Makefile. 738 739 The images will be created under IMAGES/ in the input target_files.zip. 740 741 Args: 742 filename: the target_files.zip, or the zip root directory. 743 """ 744 if os.path.isdir(filename): 745 OPTIONS.input_tmp = os.path.abspath(filename) 746 else: 747 OPTIONS.input_tmp = common.UnzipTemp(filename) 748 749 if not OPTIONS.add_missing: 750 if os.path.isdir(os.path.join(OPTIONS.input_tmp, "IMAGES")): 751 logger.warning("target_files appears to already contain images.") 752 sys.exit(1) 753 754 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.input_tmp, repacking=True) 755 756 has_recovery = OPTIONS.info_dict.get("no_recovery") != "true" 757 has_boot = OPTIONS.info_dict.get("no_boot") != "true" 758 has_vendor_boot = OPTIONS.info_dict.get("vendor_boot") == "true" 759 760 # {vendor,odm,product,system_ext,vendor_dlkm,odm_dlkm, system, system_other}.img 761 # can be built from source, or dropped into target_files.zip as a prebuilt blob. 762 has_vendor = HasPartition("vendor") 763 has_odm = HasPartition("odm") 764 has_vendor_dlkm = HasPartition("vendor_dlkm") 765 has_odm_dlkm = HasPartition("odm_dlkm") 766 has_product = HasPartition("product") 767 has_system_ext = HasPartition("system_ext") 768 has_system = HasPartition("system") 769 has_system_other = HasPartition("system_other") 770 has_userdata = OPTIONS.info_dict.get("building_userdata_image") == "true" 771 has_cache = OPTIONS.info_dict.get("building_cache_image") == "true" 772 773 # Set up the output destination. It writes to the given directory for dir 774 # mode; otherwise appends to the given ZIP. 775 if os.path.isdir(filename): 776 output_zip = None 777 else: 778 output_zip = zipfile.ZipFile(filename, "a", 779 compression=zipfile.ZIP_DEFLATED, 780 allowZip64=True) 781 782 # Always make input_tmp/IMAGES available, since we may stage boot / recovery 783 # images there even under zip mode. The directory will be cleaned up as part 784 # of OPTIONS.input_tmp. 785 images_dir = os.path.join(OPTIONS.input_tmp, "IMAGES") 786 if not os.path.isdir(images_dir): 787 os.makedirs(images_dir) 788 789 # A map between partition names and their paths, which could be used when 790 # generating AVB vbmeta image. 791 partitions = {} 792 793 def banner(s): 794 logger.info("\n\n++++ %s ++++\n\n", s) 795 796 boot_image = None 797 if has_boot: 798 banner("boot") 799 boot_images = OPTIONS.info_dict.get("boot_images") 800 if boot_images is None: 801 boot_images = "boot.img" 802 for index,b in enumerate(boot_images.split()): 803 # common.GetBootableImage() returns the image directly if present. 804 boot_image = common.GetBootableImage( 805 "IMAGES/" + b, b, OPTIONS.input_tmp, "BOOT") 806 # boot.img may be unavailable in some targets (e.g. aosp_arm64). 807 if boot_image: 808 boot_image_path = os.path.join(OPTIONS.input_tmp, "IMAGES", b) 809 # Although multiple boot images can be generated, include the image 810 # descriptor of only the first boot image in vbmeta 811 if index == 0: 812 partitions['boot'] = boot_image_path 813 if not os.path.exists(boot_image_path): 814 boot_image.WriteToDir(OPTIONS.input_tmp) 815 if output_zip: 816 boot_image.AddToZip(output_zip) 817 818 if has_vendor_boot: 819 banner("vendor_boot") 820 vendor_boot_image = common.GetVendorBootImage( 821 "IMAGES/vendor_boot.img", "vendor_boot.img", OPTIONS.input_tmp, 822 "VENDOR_BOOT") 823 if vendor_boot_image: 824 partitions['vendor_boot'] = os.path.join(OPTIONS.input_tmp, "IMAGES", 825 "vendor_boot.img") 826 if not os.path.exists(partitions['vendor_boot']): 827 vendor_boot_image.WriteToDir(OPTIONS.input_tmp) 828 if output_zip: 829 vendor_boot_image.AddToZip(output_zip) 830 831 recovery_image = None 832 if has_recovery: 833 banner("recovery") 834 recovery_image = common.GetBootableImage( 835 "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY") 836 assert recovery_image, "Failed to create recovery.img." 837 partitions['recovery'] = os.path.join( 838 OPTIONS.input_tmp, "IMAGES", "recovery.img") 839 if not os.path.exists(partitions['recovery']): 840 recovery_image.WriteToDir(OPTIONS.input_tmp) 841 if output_zip: 842 recovery_image.AddToZip(output_zip) 843 844 banner("recovery (two-step image)") 845 # The special recovery.img for two-step package use. 846 recovery_two_step_image = common.GetBootableImage( 847 "OTA/recovery-two-step.img", "recovery-two-step.img", 848 OPTIONS.input_tmp, "RECOVERY", two_step_image=True) 849 assert recovery_two_step_image, "Failed to create recovery-two-step.img." 850 recovery_two_step_image_path = os.path.join( 851 OPTIONS.input_tmp, "OTA", "recovery-two-step.img") 852 if not os.path.exists(recovery_two_step_image_path): 853 recovery_two_step_image.WriteToDir(OPTIONS.input_tmp) 854 if output_zip: 855 recovery_two_step_image.AddToZip(output_zip) 856 857 if has_system: 858 banner("system") 859 partitions['system'] = AddSystem( 860 output_zip, recovery_img=recovery_image, boot_img=boot_image) 861 862 if has_vendor: 863 banner("vendor") 864 partitions['vendor'] = AddVendor( 865 output_zip, recovery_img=recovery_image, boot_img=boot_image) 866 867 if has_product: 868 banner("product") 869 partitions['product'] = AddProduct(output_zip) 870 871 if has_system_ext: 872 banner("system_ext") 873 partitions['system_ext'] = AddSystemExt(output_zip) 874 875 if has_odm: 876 banner("odm") 877 partitions['odm'] = AddOdm(output_zip) 878 879 if has_vendor_dlkm: 880 banner("vendor_dlkm") 881 partitions['vendor_dlkm'] = AddVendorDlkm(output_zip) 882 883 if has_odm_dlkm: 884 banner("odm_dlkm") 885 partitions['odm_dlkm'] = AddOdmDlkm(output_zip) 886 887 if has_system_other: 888 banner("system_other") 889 AddSystemOther(output_zip) 890 891 AddApexInfo(output_zip) 892 893 if not OPTIONS.is_signing: 894 banner("userdata") 895 AddUserdata(output_zip) 896 banner("cache") 897 AddCache(output_zip) 898 899 if OPTIONS.info_dict.get("board_bpt_enable") == "true": 900 banner("partition-table") 901 AddPartitionTable(output_zip) 902 903 if OPTIONS.info_dict.get("has_dtbo") == "true": 904 banner("dtbo") 905 partitions['dtbo'] = AddDtbo(output_zip) 906 907 if OPTIONS.info_dict.get("has_pvmfw") == "true": 908 banner("pvmfw") 909 partitions['pvmfw'] = AddPvmfw(output_zip) 910 911 # Custom images. 912 custom_partitions = OPTIONS.info_dict.get( 913 "avb_custom_images_partition_list", "").strip().split() 914 for partition_name in custom_partitions: 915 partition_name = partition_name.strip() 916 banner("custom images for " + partition_name) 917 partitions[partition_name] = AddCustomImages(output_zip, partition_name) 918 919 if OPTIONS.info_dict.get("avb_enable") == "true": 920 # vbmeta_partitions includes the partitions that should be included into 921 # top-level vbmeta.img, which are the ones that are not included in any 922 # chained VBMeta image plus the chained VBMeta images themselves. 923 # Currently custom_partitions are all chained to VBMeta image. 924 vbmeta_partitions = common.AVB_PARTITIONS[:] + tuple(custom_partitions) 925 926 vbmeta_system = OPTIONS.info_dict.get("avb_vbmeta_system", "").strip() 927 if vbmeta_system: 928 banner("vbmeta_system") 929 partitions["vbmeta_system"] = AddVBMeta( 930 output_zip, partitions, "vbmeta_system", vbmeta_system.split()) 931 vbmeta_partitions = [ 932 item for item in vbmeta_partitions 933 if item not in vbmeta_system.split()] 934 vbmeta_partitions.append("vbmeta_system") 935 936 vbmeta_vendor = OPTIONS.info_dict.get("avb_vbmeta_vendor", "").strip() 937 if vbmeta_vendor: 938 banner("vbmeta_vendor") 939 partitions["vbmeta_vendor"] = AddVBMeta( 940 output_zip, partitions, "vbmeta_vendor", vbmeta_vendor.split()) 941 vbmeta_partitions = [ 942 item for item in vbmeta_partitions 943 if item not in vbmeta_vendor.split()] 944 vbmeta_partitions.append("vbmeta_vendor") 945 946 if OPTIONS.info_dict.get("avb_building_vbmeta_image") == "true": 947 banner("vbmeta") 948 AddVBMeta(output_zip, partitions, "vbmeta", vbmeta_partitions) 949 950 if OPTIONS.info_dict.get("use_dynamic_partitions") == "true": 951 if OPTIONS.info_dict.get("build_super_empty_partition") == "true": 952 banner("super_empty") 953 AddSuperEmpty(output_zip) 954 955 if OPTIONS.info_dict.get("build_super_partition") == "true": 956 if OPTIONS.info_dict.get( 957 "build_retrofit_dynamic_partitions_ota_package") == "true": 958 banner("super split images") 959 AddSuperSplit(output_zip) 960 961 banner("radio") 962 ab_partitions_txt = os.path.join(OPTIONS.input_tmp, "META", 963 "ab_partitions.txt") 964 if os.path.exists(ab_partitions_txt): 965 with open(ab_partitions_txt) as f: 966 ab_partitions = f.readlines() 967 968 # For devices using A/B update, make sure we have all the needed images 969 # ready under IMAGES/ or RADIO/. 970 CheckAbOtaImages(output_zip, ab_partitions) 971 972 # Generate care_map.pb for ab_partitions, then write this file to 973 # target_files package. 974 output_care_map = os.path.join(OPTIONS.input_tmp, "META", "care_map.pb") 975 AddCareMapForAbOta(output_zip if output_zip else output_care_map, 976 ab_partitions, partitions) 977 978 # Radio images that need to be packed into IMAGES/, and product-img.zip. 979 pack_radioimages_txt = os.path.join( 980 OPTIONS.input_tmp, "META", "pack_radioimages.txt") 981 if os.path.exists(pack_radioimages_txt): 982 with open(pack_radioimages_txt) as f: 983 AddPackRadioImages(output_zip, f.readlines()) 984 985 AddVbmetaDigest(output_zip) 986 987 if output_zip: 988 common.ZipClose(output_zip) 989 if OPTIONS.replace_updated_files_list: 990 ReplaceUpdatedFiles(output_zip.filename, 991 OPTIONS.replace_updated_files_list) 992 993 994def main(argv): 995 def option_handler(o, a): 996 if o in ("-a", "--add_missing"): 997 OPTIONS.add_missing = True 998 elif o in ("-r", "--rebuild_recovery",): 999 OPTIONS.rebuild_recovery = True 1000 elif o == "--replace_verity_private_key": 1001 OPTIONS.replace_verity_private_key = (True, a) 1002 elif o == "--replace_verity_public_key": 1003 OPTIONS.replace_verity_public_key = (True, a) 1004 elif o == "--is_signing": 1005 OPTIONS.is_signing = True 1006 else: 1007 return False 1008 return True 1009 1010 args = common.ParseOptions( 1011 argv, __doc__, extra_opts="ar", 1012 extra_long_opts=["add_missing", "rebuild_recovery", 1013 "replace_verity_public_key=", 1014 "replace_verity_private_key=", 1015 "is_signing"], 1016 extra_option_handler=option_handler) 1017 1018 if len(args) != 1: 1019 common.Usage(__doc__) 1020 sys.exit(1) 1021 1022 common.InitLogging() 1023 1024 AddImagesToTargetFiles(args[0]) 1025 logger.info("done.") 1026 1027if __name__ == '__main__': 1028 try: 1029 common.CloseInheritedPipes() 1030 main(sys.argv[1:]) 1031 except common.ExternalError: 1032 logger.exception("\n ERROR:\n") 1033 sys.exit(1) 1034 finally: 1035 common.Cleanup() 1036