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