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