add_img_to_target_files.py revision bea20ac722f60c29fc9475b76996eab85bbcac20
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. 32 33 --replace_verity_private_key 34 Replace the private key used for verity signing. (same as the option 35 in sign_target_files_apks) 36 37 --replace_verity_public_key 38 Replace the certificate (public key) used for verity verification. (same 39 as the option in sign_target_files_apks) 40 41 --is_signing 42 Skip building & adding the images for "userdata" and "cache" if we 43 are signing the target files. 44""" 45 46from __future__ import print_function 47 48import datetime 49import hashlib 50import os 51import shlex 52import shutil 53import subprocess 54import sys 55import tempfile 56import uuid 57import zipfile 58 59import build_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 68OPTIONS = common.OPTIONS 69 70OPTIONS.add_missing = False 71OPTIONS.rebuild_recovery = False 72OPTIONS.replace_updated_files_list = [] 73OPTIONS.replace_verity_public_key = False 74OPTIONS.replace_verity_private_key = False 75OPTIONS.is_signing = False 76 77 78class OutputFile(object): 79 def __init__(self, output_zip, input_dir, prefix, name): 80 self._output_zip = output_zip 81 self.input_name = os.path.join(input_dir, prefix, name) 82 83 if self._output_zip: 84 self._zip_name = os.path.join(prefix, name) 85 86 root, suffix = os.path.splitext(name) 87 self.name = common.MakeTempFile(prefix=root + '-', suffix=suffix) 88 else: 89 self.name = self.input_name 90 91 def Write(self): 92 if self._output_zip: 93 common.ZipWrite(self._output_zip, self.name, self._zip_name) 94 95 96def GetCareMap(which, imgname): 97 """Generate care_map of system (or vendor) partition""" 98 99 assert which in ("system", "vendor") 100 101 simg = sparse_img.SparseImage(imgname) 102 care_map_list = [which] 103 104 care_map_ranges = simg.care_map 105 key = which + "_adjusted_partition_size" 106 adjusted_blocks = OPTIONS.info_dict.get(key) 107 if adjusted_blocks: 108 assert adjusted_blocks > 0, "blocks should be positive for " + which 109 care_map_ranges = care_map_ranges.intersect(rangelib.RangeSet( 110 "0-%d" % (adjusted_blocks,))) 111 112 care_map_list.append(care_map_ranges.to_string_raw()) 113 return care_map_list 114 115 116def AddSystem(output_zip, prefix="IMAGES/", recovery_img=None, boot_img=None): 117 """Turn the contents of SYSTEM into a system image and store it in 118 output_zip. Returns the name of the system image file.""" 119 120 img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "system.img") 121 if os.path.exists(img.input_name): 122 print("system.img already exists in %s, no need to rebuild..." % (prefix,)) 123 return img.input_name 124 125 def output_sink(fn, data): 126 ofile = open(os.path.join(OPTIONS.input_tmp, "SYSTEM", fn), "w") 127 ofile.write(data) 128 ofile.close() 129 130 arc_name = "SYSTEM/" + fn 131 if arc_name in output_zip.namelist(): 132 OPTIONS.replace_updated_files_list.append(arc_name) 133 else: 134 common.ZipWrite(output_zip, ofile.name, arc_name) 135 136 if OPTIONS.rebuild_recovery: 137 print("Building new recovery patch") 138 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink, recovery_img, 139 boot_img, info_dict=OPTIONS.info_dict) 140 141 block_list = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "system.map") 142 CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "system", img, 143 block_list=block_list) 144 145 return img.name 146 147 148def AddSystemOther(output_zip, prefix="IMAGES/"): 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, prefix, "system_other.img") 153 if os.path.exists(img.input_name): 154 print("system_other.img already exists in %s, no need to rebuild..." % ( 155 prefix,)) 156 return 157 158 CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "system_other", img) 159 160 161def AddVendor(output_zip, prefix="IMAGES/"): 162 """Turn the contents of VENDOR into a vendor image and store in it 163 output_zip.""" 164 165 img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "vendor.img") 166 if os.path.exists(img.input_name): 167 print("vendor.img already exists in %s, no need to rebuild..." % (prefix,)) 168 return img.input_name 169 170 block_list = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "vendor.map") 171 CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "vendor", img, 172 block_list=block_list) 173 return img.name 174 175 176def AddDtbo(output_zip, prefix="IMAGES/"): 177 """Adds the DTBO image. 178 179 Uses the image under prefix if it already exists. Otherwise looks for the 180 image under PREBUILT_IMAGES/, signs it as needed, and returns the image name. 181 """ 182 183 img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "dtbo.img") 184 if os.path.exists(img.input_name): 185 print("dtbo.img already exists in %s, no need to rebuild..." % (prefix,)) 186 return img.input_name 187 188 dtbo_prebuilt_path = os.path.join( 189 OPTIONS.input_tmp, "PREBUILT_IMAGES", "dtbo.img") 190 assert os.path.exists(dtbo_prebuilt_path) 191 shutil.copy(dtbo_prebuilt_path, img.name) 192 193 # AVB-sign the image as needed. 194 if OPTIONS.info_dict.get("avb_enable") == "true": 195 avbtool = os.getenv('AVBTOOL') or OPTIONS.info_dict["avb_avbtool"] 196 part_size = OPTIONS.info_dict["dtbo_size"] 197 # The AVB hash footer will be replaced if already present. 198 cmd = [avbtool, "add_hash_footer", "--image", img.name, 199 "--partition_size", str(part_size), "--partition_name", "dtbo"] 200 common.AppendAVBSigningArgs(cmd, "dtbo") 201 args = OPTIONS.info_dict.get("avb_dtbo_add_hash_footer_args") 202 if args and args.strip(): 203 cmd.extend(shlex.split(args)) 204 p = common.Run(cmd, stdout=subprocess.PIPE) 205 p.communicate() 206 assert p.returncode == 0, \ 207 "avbtool add_hash_footer of %s failed" % (img.name,) 208 209 img.Write() 210 return img.name 211 212 213def CreateImage(input_dir, info_dict, what, output_file, block_list=None): 214 print("creating " + what + ".img...") 215 216 image_props = build_image.ImagePropFromGlobalDict(info_dict, what) 217 fstab = info_dict["fstab"] 218 mount_point = "/" + what 219 if fstab and mount_point in fstab: 220 image_props["fs_type"] = fstab[mount_point].fs_type 221 222 # Use a fixed timestamp (01/01/2009) when packaging the image. 223 # Bug: 24377993 224 epoch = datetime.datetime.fromtimestamp(0) 225 timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds() 226 image_props["timestamp"] = int(timestamp) 227 228 if what == "system": 229 fs_config_prefix = "" 230 else: 231 fs_config_prefix = what + "_" 232 233 fs_config = os.path.join( 234 input_dir, "META/" + fs_config_prefix + "filesystem_config.txt") 235 if not os.path.exists(fs_config): 236 fs_config = None 237 238 # Override values loaded from info_dict. 239 if fs_config: 240 image_props["fs_config"] = fs_config 241 if block_list: 242 image_props["block_list"] = block_list.name 243 244 # Use repeatable ext4 FS UUID and hash_seed UUID (based on partition name and 245 # build fingerprint). 246 uuid_seed = what + "-" 247 if "build.prop" in info_dict: 248 build_prop = info_dict["build.prop"] 249 if "ro.build.fingerprint" in build_prop: 250 uuid_seed += build_prop["ro.build.fingerprint"] 251 elif "ro.build.thumbprint" in build_prop: 252 uuid_seed += build_prop["ro.build.thumbprint"] 253 image_props["uuid"] = str(uuid.uuid5(uuid.NAMESPACE_URL, uuid_seed)) 254 hash_seed = "hash_seed-" + uuid_seed 255 image_props["hash_seed"] = str(uuid.uuid5(uuid.NAMESPACE_URL, hash_seed)) 256 257 succ = build_image.BuildImage(os.path.join(input_dir, what.upper()), 258 image_props, output_file.name) 259 assert succ, "build " + what + ".img image failed" 260 261 output_file.Write() 262 if block_list: 263 block_list.Write() 264 265 # Set the 'adjusted_partition_size' that excludes the verity blocks of the 266 # given image. When avb is enabled, this size is the max image size returned 267 # by the avb tool. 268 is_verity_partition = "verity_block_device" in image_props 269 verity_supported = (image_props.get("verity") == "true" or 270 image_props.get("avb_enable") == "true") 271 is_avb_enable = image_props.get("avb_hashtree_enable") == "true" 272 if verity_supported and (is_verity_partition or is_avb_enable): 273 adjusted_blocks_value = image_props.get("partition_size") 274 if adjusted_blocks_value: 275 adjusted_blocks_key = what + "_adjusted_partition_size" 276 info_dict[adjusted_blocks_key] = int(adjusted_blocks_value)/4096 - 1 277 278 279def AddUserdata(output_zip, prefix="IMAGES/"): 280 """Create a userdata image and store it in output_zip. 281 282 In most case we just create and store an empty userdata.img; 283 But the invoker can also request to create userdata.img with real 284 data from the target files, by setting "userdata_img_with_data=true" 285 in OPTIONS.info_dict. 286 """ 287 288 img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "userdata.img") 289 if os.path.exists(img.input_name): 290 print("userdata.img already exists in %s, no need to rebuild..." % ( 291 prefix,)) 292 return 293 294 # Skip userdata.img if no size. 295 image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, "data") 296 if not image_props.get("partition_size"): 297 return 298 299 print("creating userdata.img...") 300 301 # Use a fixed timestamp (01/01/2009) when packaging the image. 302 # Bug: 24377993 303 epoch = datetime.datetime.fromtimestamp(0) 304 timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds() 305 image_props["timestamp"] = int(timestamp) 306 307 if OPTIONS.info_dict.get("userdata_img_with_data") == "true": 308 user_dir = os.path.join(OPTIONS.input_tmp, "DATA") 309 else: 310 user_dir = common.MakeTempDir() 311 312 fstab = OPTIONS.info_dict["fstab"] 313 if fstab: 314 image_props["fs_type"] = fstab["/data"].fs_type 315 succ = build_image.BuildImage(user_dir, image_props, img.name) 316 assert succ, "build userdata.img image failed" 317 318 common.CheckSize(img.name, "userdata.img", OPTIONS.info_dict) 319 img.Write() 320 321 322def AppendVBMetaArgsForPartition(cmd, partition, img_path, public_key_dir): 323 if not img_path: 324 return 325 326 # Check if chain partition is used. 327 key_path = OPTIONS.info_dict.get("avb_" + partition + "_key_path") 328 if key_path: 329 # extract public key in AVB format to be included in vbmeta.img 330 avbtool = os.getenv('AVBTOOL') or OPTIONS.info_dict["avb_avbtool"] 331 public_key_path = os.path.join(public_key_dir, "%s.avbpubkey" % partition) 332 p = common.Run([avbtool, "extract_public_key", "--key", key_path, 333 "--output", public_key_path], 334 stdout=subprocess.PIPE, stderr=subprocess.PIPE) 335 p.communicate() 336 assert p.returncode == 0, \ 337 "avbtool extract_public_key fail for partition: %r" % partition 338 339 rollback_index_location = OPTIONS.info_dict[ 340 "avb_" + partition + "_rollback_index_location"] 341 cmd.extend(["--chain_partition", "%s:%s:%s" % ( 342 partition, rollback_index_location, public_key_path)]) 343 else: 344 cmd.extend(["--include_descriptors_from_image", img_path]) 345 346 347def AddVBMeta(output_zip, partitions, prefix="IMAGES/"): 348 """Creates a VBMeta image and store it in output_zip. 349 350 Args: 351 output_zip: The output zip file, which needs to be already open. 352 partitions: A dict that's keyed by partition names with image paths as 353 values. Only valid partition names are accepted, which include 'boot', 354 'recovery', 'system', 'vendor', 'dtbo'. 355 """ 356 img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "vbmeta.img") 357 if os.path.exists(img.input_name): 358 print("vbmeta.img already exists in %s; not rebuilding..." % (prefix,)) 359 return img.input_name 360 361 avbtool = os.getenv('AVBTOOL') or OPTIONS.info_dict["avb_avbtool"] 362 cmd = [avbtool, "make_vbmeta_image", "--output", img.name] 363 common.AppendAVBSigningArgs(cmd, "vbmeta") 364 365 public_key_dir = common.MakeTempDir(prefix="avbpubkey-") 366 for partition, path in partitions.items(): 367 assert partition in common.AVB_PARTITIONS, 'Unknown partition: %s' % ( 368 partition,) 369 assert os.path.exists(path), 'Failed to find %s for partition %s' % ( 370 path, partition) 371 AppendVBMetaArgsForPartition(cmd, partition, path, public_key_dir) 372 373 args = OPTIONS.info_dict.get("avb_vbmeta_args") 374 if args and args.strip(): 375 split_args = shlex.split(args) 376 for index, arg in enumerate(split_args[:-1]): 377 # Sanity check that the image file exists. Some images might be defined 378 # as a path relative to source tree, which may not be available at the 379 # same location when running this script (we have the input target_files 380 # zip only). For such cases, we additionally scan other locations (e.g. 381 # IMAGES/, RADIO/, etc) before bailing out. 382 if arg == '--include_descriptors_from_image': 383 image_path = split_args[index + 1] 384 if os.path.exists(image_path): 385 continue 386 found = False 387 for dir_name in ['IMAGES', 'RADIO', 'VENDOR_IMAGES', 'PREBUILT_IMAGES']: 388 alt_path = os.path.join( 389 OPTIONS.input_tmp, dir_name, os.path.basename(image_path)) 390 if os.path.exists(alt_path): 391 split_args[index + 1] = alt_path 392 found = True 393 break 394 assert found, 'failed to find %s' % (image_path,) 395 cmd.extend(split_args) 396 397 p = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 398 p.communicate() 399 assert p.returncode == 0, "avbtool make_vbmeta_image failed" 400 img.Write() 401 402 403def AddPartitionTable(output_zip, prefix="IMAGES/"): 404 """Create a partition table image and store it in output_zip.""" 405 406 img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "partition-table.img") 407 bpt = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "partition-table.bpt") 408 409 # use BPTTOOL from environ, or "bpttool" if empty or not set. 410 bpttool = os.getenv("BPTTOOL") or "bpttool" 411 cmd = [bpttool, "make_table", "--output_json", bpt.name, 412 "--output_gpt", img.name] 413 input_files_str = OPTIONS.info_dict["board_bpt_input_files"] 414 input_files = input_files_str.split(" ") 415 for i in input_files: 416 cmd.extend(["--input", i]) 417 disk_size = OPTIONS.info_dict.get("board_bpt_disk_size") 418 if disk_size: 419 cmd.extend(["--disk_size", disk_size]) 420 args = OPTIONS.info_dict.get("board_bpt_make_table_args") 421 if args: 422 cmd.extend(shlex.split(args)) 423 424 p = common.Run(cmd, stdout=subprocess.PIPE) 425 p.communicate() 426 assert p.returncode == 0, "bpttool make_table failed" 427 428 img.Write() 429 bpt.Write() 430 431 432def AddCache(output_zip, prefix="IMAGES/"): 433 """Create an empty cache image and store it in output_zip.""" 434 435 img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "cache.img") 436 if os.path.exists(img.input_name): 437 print("cache.img already exists in %s, no need to rebuild..." % (prefix,)) 438 return 439 440 image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, "cache") 441 # The build system has to explicitly request for cache.img. 442 if "fs_type" not in image_props: 443 return 444 445 print("creating cache.img...") 446 447 # Use a fixed timestamp (01/01/2009) when packaging the image. 448 # Bug: 24377993 449 epoch = datetime.datetime.fromtimestamp(0) 450 timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds() 451 image_props["timestamp"] = int(timestamp) 452 453 user_dir = common.MakeTempDir() 454 455 fstab = OPTIONS.info_dict["fstab"] 456 if fstab: 457 image_props["fs_type"] = fstab["/cache"].fs_type 458 succ = build_image.BuildImage(user_dir, image_props, img.name) 459 assert succ, "build cache.img image failed" 460 461 common.CheckSize(img.name, "cache.img", OPTIONS.info_dict) 462 img.Write() 463 464 465def AddRadioImagesForAbOta(output_zip, ab_partitions): 466 """Adds the radio images needed for A/B OTA to the output file. 467 468 It parses the list of A/B partitions, looks for the missing ones from RADIO/ 469 or VENDOR_IMAGES/ dirs, and copies them to IMAGES/ of the output file (or 470 dir). 471 472 It also ensures that on returning from the function all the listed A/B 473 partitions must have their images available under IMAGES/. 474 475 Args: 476 output_zip: The output zip file (needs to be already open), or None to 477 write images to OPTIONS.input_tmp/. 478 ab_partitions: The list of A/B partitions. 479 480 Raises: 481 AssertionError: If it can't find an image. 482 """ 483 for partition in ab_partitions: 484 img_name = partition.strip() + ".img" 485 prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name) 486 if os.path.exists(prebuilt_path): 487 print("%s already exists, no need to overwrite..." % (img_name,)) 488 continue 489 490 img_radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name) 491 if os.path.exists(img_radio_path): 492 if output_zip: 493 common.ZipWrite(output_zip, img_radio_path, 494 os.path.join("IMAGES", img_name)) 495 else: 496 shutil.copy(img_radio_path, prebuilt_path) 497 else: 498 img_vendor_dir = os.path.join(OPTIONS.input_tmp, "VENDOR_IMAGES") 499 for root, _, files in os.walk(img_vendor_dir): 500 if img_name in files: 501 if output_zip: 502 common.ZipWrite(output_zip, os.path.join(root, img_name), 503 os.path.join("IMAGES", img_name)) 504 else: 505 shutil.copy(os.path.join(root, img_name), prebuilt_path) 506 break 507 508 if output_zip: 509 # Zip spec says: All slashes MUST be forward slashes. 510 img_path = 'IMAGES/' + img_name 511 assert img_path in output_zip.namelist(), "cannot find " + img_name 512 else: 513 img_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name) 514 assert os.path.exists(img_path), "cannot find " + img_name 515 516 517def AddCareMapTxtForAbOta(output_zip, ab_partitions, image_paths): 518 """Generates and adds care_map.txt for system and vendor partitions. 519 520 Args: 521 output_zip: The output zip file (needs to be already open), or None to 522 write images to OPTIONS.input_tmp/. 523 ab_partitions: The list of A/B partitions. 524 image_paths: A map from the partition name to the image path. 525 """ 526 care_map_list = [] 527 for partition in ab_partitions: 528 partition = partition.strip() 529 if (partition == "system" and 530 ("system_verity_block_device" in OPTIONS.info_dict or 531 OPTIONS.info_dict.get("avb_system_hashtree_enable") == "true")): 532 system_img_path = image_paths[partition] 533 assert os.path.exists(system_img_path) 534 care_map_list += GetCareMap("system", system_img_path) 535 if (partition == "vendor" and 536 ("vendor_verity_block_device" in OPTIONS.info_dict or 537 OPTIONS.info_dict.get("avb_vendor_hashtree_enable") == "true")): 538 vendor_img_path = image_paths[partition] 539 assert os.path.exists(vendor_img_path) 540 care_map_list += GetCareMap("vendor", vendor_img_path) 541 542 if care_map_list: 543 care_map_path = "META/care_map.txt" 544 if output_zip and care_map_path not in output_zip.namelist(): 545 common.ZipWriteStr(output_zip, care_map_path, '\n'.join(care_map_list)) 546 else: 547 with open(os.path.join(OPTIONS.input_tmp, care_map_path), 'w') as fp: 548 fp.write('\n'.join(care_map_list)) 549 if output_zip: 550 OPTIONS.replace_updated_files_list.append(care_map_path) 551 552 553def AddPackRadioImages(output_zip, images): 554 """Copies images listed in META/pack_radioimages.txt from RADIO/ to IMAGES/. 555 556 Args: 557 output_zip: The output zip file (needs to be already open), or None to 558 write images to OPTIONS.input_tmp/. 559 images: A list of image names. 560 561 Raises: 562 AssertionError: If a listed image can't be found. 563 """ 564 for image in images: 565 img_name = image.strip() 566 _, ext = os.path.splitext(img_name) 567 if not ext: 568 img_name += ".img" 569 prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name) 570 if os.path.exists(prebuilt_path): 571 print("%s already exists, no need to overwrite..." % (img_name,)) 572 continue 573 574 img_radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name) 575 assert os.path.exists(img_radio_path), \ 576 "Failed to find %s at %s" % (img_name, img_radio_path) 577 if output_zip: 578 common.ZipWrite(output_zip, img_radio_path, 579 os.path.join("IMAGES", img_name)) 580 else: 581 shutil.copy(img_radio_path, prebuilt_path) 582 583 584def ReplaceUpdatedFiles(zip_filename, files_list): 585 """Updates all the ZIP entries listed in files_list. 586 587 For now the list includes META/care_map.txt, and the related files under 588 SYSTEM/ after rebuilding recovery. 589 """ 590 common.ZipDelete(zip_filename, files_list) 591 output_zip = zipfile.ZipFile(zip_filename, "a", 592 compression=zipfile.ZIP_DEFLATED, 593 allowZip64=True) 594 for item in files_list: 595 file_path = os.path.join(OPTIONS.input_tmp, item) 596 assert os.path.exists(file_path) 597 common.ZipWrite(output_zip, file_path, arcname=item) 598 common.ZipClose(output_zip) 599 600 601def AddImagesToTargetFiles(filename): 602 """Creates and adds images (boot/recovery/system/...) to a target_files.zip. 603 604 It works with either a zip file (zip mode), or a directory that contains the 605 files to be packed into a target_files.zip (dir mode). The latter is used when 606 being called from build/make/core/Makefile. 607 608 The images will be created under IMAGES/ in the input target_files.zip. 609 610 Args: 611 filename: the target_files.zip, or the zip root directory. 612 """ 613 if os.path.isdir(filename): 614 OPTIONS.input_tmp = os.path.abspath(filename) 615 input_zip = None 616 else: 617 OPTIONS.input_tmp, input_zip = common.UnzipTemp(filename) 618 619 if not OPTIONS.add_missing: 620 if os.path.isdir(os.path.join(OPTIONS.input_tmp, "IMAGES")): 621 print("target_files appears to already contain images.") 622 sys.exit(1) 623 624 # vendor.img is unlike system.img or system_other.img. Because it could be 625 # built from source, or dropped into target_files.zip as a prebuilt blob. We 626 # consider either of them as vendor.img being available, which could be used 627 # when generating vbmeta.img for AVB. 628 has_vendor = (os.path.isdir(os.path.join(OPTIONS.input_tmp, "VENDOR")) or 629 os.path.exists(os.path.join(OPTIONS.input_tmp, "IMAGES", 630 "vendor.img"))) 631 has_system_other = os.path.isdir(os.path.join(OPTIONS.input_tmp, 632 "SYSTEM_OTHER")) 633 634 if input_zip: 635 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.input_tmp) 636 637 common.ZipClose(input_zip) 638 output_zip = zipfile.ZipFile(filename, "a", 639 compression=zipfile.ZIP_DEFLATED, 640 allowZip64=True) 641 else: 642 OPTIONS.info_dict = common.LoadInfoDict(filename, filename) 643 output_zip = None 644 645 # Always make input_tmp/IMAGES available, since we may stage boot / recovery 646 # images there even under zip mode. The directory will be cleaned up as part 647 # of OPTIONS.input_tmp. 648 images_dir = os.path.join(OPTIONS.input_tmp, "IMAGES") 649 if not os.path.isdir(images_dir): 650 os.makedirs(images_dir) 651 652 has_recovery = (OPTIONS.info_dict.get("no_recovery") != "true") 653 654 if OPTIONS.info_dict.get("avb_enable") == "true": 655 fp = None 656 if "build.prop" in OPTIONS.info_dict: 657 build_prop = OPTIONS.info_dict["build.prop"] 658 if "ro.build.fingerprint" in build_prop: 659 fp = build_prop["ro.build.fingerprint"] 660 elif "ro.build.thumbprint" in build_prop: 661 fp = build_prop["ro.build.thumbprint"] 662 if fp: 663 OPTIONS.info_dict["avb_salt"] = hashlib.sha256(fp).hexdigest() 664 665 # A map between partition names and their paths, which could be used when 666 # generating AVB vbmeta image. 667 partitions = dict() 668 669 def banner(s): 670 print("\n\n++++ " + s + " ++++\n\n") 671 672 banner("boot") 673 # common.GetBootableImage() returns the image directly if present. 674 boot_image = common.GetBootableImage( 675 "IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT") 676 # boot.img may be unavailable in some targets (e.g. aosp_arm64). 677 if boot_image: 678 partitions['boot'] = os.path.join(OPTIONS.input_tmp, "IMAGES", "boot.img") 679 if not os.path.exists(partitions['boot']): 680 boot_image.WriteToDir(OPTIONS.input_tmp) 681 if output_zip: 682 boot_image.AddToZip(output_zip) 683 684 recovery_image = None 685 if has_recovery: 686 banner("recovery") 687 recovery_image = common.GetBootableImage( 688 "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY") 689 assert recovery_image, "Failed to create recovery.img." 690 partitions['recovery'] = os.path.join( 691 OPTIONS.input_tmp, "IMAGES", "recovery.img") 692 if not os.path.exists(partitions['recovery']): 693 recovery_image.WriteToDir(OPTIONS.input_tmp) 694 if output_zip: 695 recovery_image.AddToZip(output_zip) 696 697 banner("recovery (two-step image)") 698 # The special recovery.img for two-step package use. 699 recovery_two_step_image = common.GetBootableImage( 700 "IMAGES/recovery-two-step.img", "recovery-two-step.img", 701 OPTIONS.input_tmp, "RECOVERY", two_step_image=True) 702 assert recovery_two_step_image, "Failed to create recovery-two-step.img." 703 recovery_two_step_image_path = os.path.join( 704 OPTIONS.input_tmp, "IMAGES", "recovery-two-step.img") 705 if not os.path.exists(recovery_two_step_image_path): 706 recovery_two_step_image.WriteToDir(OPTIONS.input_tmp) 707 if output_zip: 708 recovery_two_step_image.AddToZip(output_zip) 709 710 banner("system") 711 partitions['system'] = AddSystem( 712 output_zip, recovery_img=recovery_image, boot_img=boot_image) 713 714 if has_vendor: 715 banner("vendor") 716 partitions['vendor'] = AddVendor(output_zip) 717 718 if has_system_other: 719 banner("system_other") 720 AddSystemOther(output_zip) 721 722 if not OPTIONS.is_signing: 723 banner("userdata") 724 AddUserdata(output_zip) 725 banner("cache") 726 AddCache(output_zip) 727 728 if OPTIONS.info_dict.get("board_bpt_enable") == "true": 729 banner("partition-table") 730 AddPartitionTable(output_zip) 731 732 if OPTIONS.info_dict.get("has_dtbo") == "true": 733 banner("dtbo") 734 partitions['dtbo'] = AddDtbo(output_zip) 735 736 if OPTIONS.info_dict.get("avb_enable") == "true": 737 banner("vbmeta") 738 AddVBMeta(output_zip, partitions) 739 740 banner("radio") 741 ab_partitions_txt = os.path.join(OPTIONS.input_tmp, "META", 742 "ab_partitions.txt") 743 if os.path.exists(ab_partitions_txt): 744 with open(ab_partitions_txt, 'r') as f: 745 ab_partitions = f.readlines() 746 747 # For devices using A/B update, copy over images from RADIO/ and/or 748 # VENDOR_IMAGES/ to IMAGES/ and make sure we have all the needed 749 # images ready under IMAGES/. All images should have '.img' as extension. 750 AddRadioImagesForAbOta(output_zip, ab_partitions) 751 752 # Generate care_map.txt for system and vendor partitions (if present), then 753 # write this file to target_files package. 754 AddCareMapTxtForAbOta(output_zip, ab_partitions, partitions) 755 756 # Radio images that need to be packed into IMAGES/, and product-img.zip. 757 pack_radioimages_txt = os.path.join( 758 OPTIONS.input_tmp, "META", "pack_radioimages.txt") 759 if os.path.exists(pack_radioimages_txt): 760 with open(pack_radioimages_txt, 'r') as f: 761 AddPackRadioImages(output_zip, f.readlines()) 762 763 if output_zip: 764 common.ZipClose(output_zip) 765 if OPTIONS.replace_updated_files_list: 766 ReplaceUpdatedFiles(output_zip.filename, 767 OPTIONS.replace_updated_files_list) 768 769 770def main(argv): 771 def option_handler(o, a): 772 if o in ("-a", "--add_missing"): 773 OPTIONS.add_missing = True 774 elif o in ("-r", "--rebuild_recovery",): 775 OPTIONS.rebuild_recovery = True 776 elif o == "--replace_verity_private_key": 777 OPTIONS.replace_verity_private_key = (True, a) 778 elif o == "--replace_verity_public_key": 779 OPTIONS.replace_verity_public_key = (True, a) 780 elif o == "--is_signing": 781 OPTIONS.is_signing = True 782 else: 783 return False 784 return True 785 786 args = common.ParseOptions( 787 argv, __doc__, extra_opts="ar", 788 extra_long_opts=["add_missing", "rebuild_recovery", 789 "replace_verity_public_key=", 790 "replace_verity_private_key=", 791 "is_signing"], 792 extra_option_handler=option_handler) 793 794 795 if len(args) != 1: 796 common.Usage(__doc__) 797 sys.exit(1) 798 799 AddImagesToTargetFiles(args[0]) 800 print("done.") 801 802if __name__ == '__main__': 803 try: 804 common.CloseInheritedPipes() 805 main(sys.argv[1:]) 806 except common.ExternalError as e: 807 print("\n ERROR: %s\n" % (e,)) 808 sys.exit(1) 809 finally: 810 common.Cleanup() 811