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