1 #!/usr/bin/env python 2 # 3 # Copyright (C) 2008 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, produces an OTA package that installs 19 that build. An incremental OTA is produced if -i is given, otherwise 20 a full OTA is produced. 21 22 Usage: ota_from_target_files [flags] input_target_files output_ota_package 23 24 --board_config <file> 25 Deprecated. 26 27 -k (--package_key) <key> Key to use to sign the package (default is 28 the value of default_system_dev_certificate from the input 29 target-files's META/misc_info.txt, or 30 "build/target/product/security/testkey" if that value is not 31 specified). 32 33 For incremental OTAs, the default value is based on the source 34 target-file, not the target build. 35 36 -i (--incremental_from) <file> 37 Generate an incremental OTA using the given target-files zip as 38 the starting build. 39 40 --full_radio 41 When generating an incremental OTA, always include a full copy of 42 radio image. This option is only meaningful when -i is specified, 43 because a full radio is always included in a full OTA if applicable. 44 45 --full_bootloader 46 Similar to --full_radio. When generating an incremental OTA, always 47 include a full copy of bootloader image. 48 49 -v (--verify) 50 Remount and verify the checksums of the files written to the 51 system and vendor (if used) partitions. Incremental builds only. 52 53 -o (--oem_settings) <main_file[,additional_files...]> 54 Comma seperated list of files used to specify the expected OEM-specific 55 properties on the OEM partition of the intended device. 56 Multiple expected values can be used by providing multiple files. 57 58 --oem_no_mount 59 For devices with OEM-specific properties but without an OEM partition, 60 do not mount the OEM partition in the updater-script. This should be 61 very rarely used, since it's expected to have a dedicated OEM partition 62 for OEM-specific properties. Only meaningful when -o is specified. 63 64 -w (--wipe_user_data) 65 Generate an OTA package that will wipe the user data partition 66 when installed. 67 68 --downgrade 69 Intentionally generate an incremental OTA that updates from a newer 70 build to an older one (based on timestamp comparison). "post-timestamp" 71 will be replaced by "ota-downgrade=yes" in the metadata file. A data 72 wipe will always be enforced, so "ota-wipe=yes" will also be included in 73 the metadata file. The update-binary in the source build will be used in 74 the OTA package, unless --binary flag is specified. Please also check the 75 doc for --override_timestamp below. 76 77 --override_timestamp 78 Intentionally generate an incremental OTA that updates from a newer 79 build to an older one (based on timestamp comparison), by overriding the 80 timestamp in package metadata. This differs from --downgrade flag: we 81 know for sure this is NOT an actual downgrade case, but two builds are 82 cut in a reverse order. A legit use case is that we cut a new build C 83 (after having A and B), but want to enfore an update path of A -> C -> B. 84 Specifying --downgrade may not help since that would enforce a data wipe 85 for C -> B update. The value of "post-timestamp" will be set to the newer 86 timestamp plus one, so that the package can be pushed and applied. 87 88 -e (--extra_script) <file> 89 Insert the contents of file at the end of the update script. 90 91 -2 (--two_step) 92 Generate a 'two-step' OTA package, where recovery is updated 93 first, so that any changes made to the system partition are done 94 using the new recovery (new kernel, etc.). 95 96 --block 97 Generate a block-based OTA for non-A/B device. We have deprecated the 98 support for file-based OTA since O. Block-based OTA will be used by 99 default for all non-A/B devices. Keeping this flag here to not break 100 existing callers. 101 102 -b (--binary) <file> 103 Use the given binary as the update-binary in the output package, 104 instead of the binary in the build's target_files. Use for 105 development only. 106 107 -t (--worker_threads) <int> 108 Specifies the number of worker-threads that will be used when 109 generating patches for incremental updates (defaults to 3). 110 111 --stash_threshold <float> 112 Specifies the threshold that will be used to compute the maximum 113 allowed stash size (defaults to 0.8). 114 115 --gen_verify 116 Generate an OTA package that verifies the partitions. 117 118 --log_diff <file> 119 Generate a log file that shows the differences in the source and target 120 builds for an incremental package. This option is only meaningful when 121 -i is specified. 122 123 --payload_signer <signer> 124 Specify the signer when signing the payload and metadata for A/B OTAs. 125 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign 126 with the package private key. If the private key cannot be accessed 127 directly, a payload signer that knows how to do that should be specified. 128 The signer will be supplied with "-inkey <path_to_key>", 129 "-in <input_file>" and "-out <output_file>" parameters. 130 131 --payload_signer_args <args> 132 Specify the arguments needed for payload signer. 133 """ 134 135 from __future__ import print_function 136 137 import sys 138 139 if sys.hexversion < 0x02070000: 140 print("Python 2.7 or newer is required.", file=sys.stderr) 141 sys.exit(1) 142 143 import copy 144 import multiprocessing 145 import os.path 146 import subprocess 147 import shlex 148 import tempfile 149 import zipfile 150 151 import common 152 import edify_generator 153 import sparse_img 154 155 OPTIONS = common.OPTIONS 156 OPTIONS.package_key = None 157 OPTIONS.incremental_source = None 158 OPTIONS.verify = False 159 OPTIONS.patch_threshold = 0.95 160 OPTIONS.wipe_user_data = False 161 OPTIONS.downgrade = False 162 OPTIONS.timestamp = False 163 OPTIONS.extra_script = None 164 OPTIONS.worker_threads = multiprocessing.cpu_count() // 2 165 if OPTIONS.worker_threads == 0: 166 OPTIONS.worker_threads = 1 167 OPTIONS.two_step = False 168 OPTIONS.no_signing = False 169 OPTIONS.block_based = True 170 OPTIONS.updater_binary = None 171 OPTIONS.oem_source = None 172 OPTIONS.oem_no_mount = False 173 OPTIONS.fallback_to_full = True 174 OPTIONS.full_radio = False 175 OPTIONS.full_bootloader = False 176 # Stash size cannot exceed cache_size * threshold. 177 OPTIONS.cache_size = None 178 OPTIONS.stash_threshold = 0.8 179 OPTIONS.gen_verify = False 180 OPTIONS.log_diff = None 181 OPTIONS.payload_signer = None 182 OPTIONS.payload_signer_args = [] 183 OPTIONS.extracted_input = None 184 OPTIONS.key_passwords = [] 185 186 METADATA_NAME = 'META-INF/com/android/metadata' 187 UNZIP_PATTERN = ['IMAGES/*', 'META/*'] 188 189 190 def SignOutput(temp_zip_name, output_zip_name): 191 pw = OPTIONS.key_passwords[OPTIONS.package_key] 192 193 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw, 194 whole_file=True) 195 196 197 def AppendAssertions(script, info_dict, oem_dicts=None): 198 oem_props = info_dict.get("oem_fingerprint_properties") 199 if not oem_props: 200 device = GetBuildProp("ro.product.device", info_dict) 201 script.AssertDevice(device) 202 else: 203 if not oem_dicts: 204 raise common.ExternalError( 205 "No OEM file provided to answer expected assertions") 206 for prop in oem_props.split(): 207 values = [] 208 for oem_dict in oem_dicts: 209 if oem_dict.get(prop): 210 values.append(oem_dict[prop]) 211 if not values: 212 raise common.ExternalError( 213 "The OEM file is missing the property %s" % prop) 214 script.AssertOemProperty(prop, values) 215 216 217 def _LoadOemDicts(script, recovery_mount_options=None): 218 """Returns the list of loaded OEM properties dict.""" 219 oem_dicts = None 220 if OPTIONS.oem_source is None: 221 raise common.ExternalError("OEM source required for this build") 222 if not OPTIONS.oem_no_mount and script: 223 script.Mount("/oem", recovery_mount_options) 224 oem_dicts = [] 225 for oem_file in OPTIONS.oem_source: 226 oem_dicts.append(common.LoadDictionaryFromLines( 227 open(oem_file).readlines())) 228 return oem_dicts 229 230 231 def _WriteRecoveryImageToBoot(script, output_zip): 232 """Find and write recovery image to /boot in two-step OTA. 233 234 In two-step OTAs, we write recovery image to /boot as the first step so that 235 we can reboot to there and install a new recovery image to /recovery. 236 A special "recovery-two-step.img" will be preferred, which encodes the correct 237 path of "/boot". Otherwise the device may show "device is corrupt" message 238 when booting into /boot. 239 240 Fall back to using the regular recovery.img if the two-step recovery image 241 doesn't exist. Note that rebuilding the special image at this point may be 242 infeasible, because we don't have the desired boot signer and keys when 243 calling ota_from_target_files.py. 244 """ 245 246 recovery_two_step_img_name = "recovery-two-step.img" 247 recovery_two_step_img_path = os.path.join( 248 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name) 249 if os.path.exists(recovery_two_step_img_path): 250 recovery_two_step_img = common.GetBootableImage( 251 recovery_two_step_img_name, recovery_two_step_img_name, 252 OPTIONS.input_tmp, "RECOVERY") 253 common.ZipWriteStr( 254 output_zip, recovery_two_step_img_name, recovery_two_step_img.data) 255 print("two-step package: using %s in stage 1/3" % ( 256 recovery_two_step_img_name,)) 257 script.WriteRawImage("/boot", recovery_two_step_img_name) 258 else: 259 print("two-step package: using recovery.img in stage 1/3") 260 # The "recovery.img" entry has been written into package earlier. 261 script.WriteRawImage("/boot", "recovery.img") 262 263 264 def HasRecoveryPatch(target_files_zip): 265 namelist = [name for name in target_files_zip.namelist()] 266 return ("SYSTEM/recovery-from-boot.p" in namelist or 267 "SYSTEM/etc/recovery.img" in namelist) 268 269 270 def HasVendorPartition(target_files_zip): 271 try: 272 target_files_zip.getinfo("VENDOR/") 273 return True 274 except KeyError: 275 return False 276 277 278 def GetOemProperty(name, oem_props, oem_dict, info_dict): 279 if oem_props is not None and name in oem_props: 280 return oem_dict[name] 281 return GetBuildProp(name, info_dict) 282 283 284 def CalculateFingerprint(oem_props, oem_dict, info_dict): 285 if oem_props is None: 286 return GetBuildProp("ro.build.fingerprint", info_dict) 287 return "%s/%s/%s:%s" % ( 288 GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict), 289 GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict), 290 GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict), 291 GetBuildProp("ro.build.thumbprint", info_dict)) 292 293 294 def GetImage(which, tmpdir): 295 """Returns an image object suitable for passing to BlockImageDiff. 296 297 'which' partition must be "system" or "vendor". A prebuilt image and file 298 map must already exist in tmpdir. 299 """ 300 301 assert which in ("system", "vendor") 302 303 path = os.path.join(tmpdir, "IMAGES", which + ".img") 304 mappath = os.path.join(tmpdir, "IMAGES", which + ".map") 305 306 # The image and map files must have been created prior to calling 307 # ota_from_target_files.py (since LMP). 308 assert os.path.exists(path) and os.path.exists(mappath) 309 310 # Bug: http://b/20939131 311 # In ext4 filesystems, block 0 might be changed even being mounted 312 # R/O. We add it to clobbered_blocks so that it will be written to the 313 # target unconditionally. Note that they are still part of care_map. 314 clobbered_blocks = "0" 315 316 return sparse_img.SparseImage(path, mappath, clobbered_blocks) 317 318 319 def AddCompatibilityArchive(target_zip, output_zip, system_included=True, 320 vendor_included=True): 321 """Adds compatibility info from target files into the output zip. 322 323 Metadata used for on-device compatibility verification is retrieved from 324 target_zip then added to compatibility.zip which is added to the output_zip 325 archive. 326 327 Compatibility archive should only be included for devices with a vendor 328 partition as checking provides value when system and vendor are independently 329 versioned. 330 331 Args: 332 target_zip: Zip file containing the source files to be included for OTA. 333 output_zip: Zip file that will be sent for OTA. 334 system_included: If True, the system image will be updated and therefore 335 its metadata should be included. 336 vendor_included: If True, the vendor image will be updated and therefore 337 its metadata should be included. 338 """ 339 340 # Determine what metadata we need. Files are names relative to META/. 341 compatibility_files = [] 342 vendor_metadata = ("vendor_manifest.xml", "vendor_matrix.xml") 343 system_metadata = ("system_manifest.xml", "system_matrix.xml") 344 if vendor_included: 345 compatibility_files += vendor_metadata 346 if system_included: 347 compatibility_files += system_metadata 348 349 # Create new archive. 350 compatibility_archive = tempfile.NamedTemporaryFile() 351 compatibility_archive_zip = zipfile.ZipFile(compatibility_archive, "w", 352 compression=zipfile.ZIP_DEFLATED) 353 354 # Add metadata. 355 for file_name in compatibility_files: 356 target_file_name = "META/" + file_name 357 358 if target_file_name in target_zip.namelist(): 359 data = target_zip.read(target_file_name) 360 common.ZipWriteStr(compatibility_archive_zip, file_name, data) 361 362 # Ensure files are written before we copy into output_zip. 363 compatibility_archive_zip.close() 364 365 # Only add the archive if we have any compatibility info. 366 if compatibility_archive_zip.namelist(): 367 common.ZipWrite(output_zip, compatibility_archive.name, 368 arcname="compatibility.zip", 369 compress_type=zipfile.ZIP_STORED) 370 371 372 def WriteFullOTAPackage(input_zip, output_zip): 373 # TODO: how to determine this? We don't know what version it will 374 # be installed on top of. For now, we expect the API just won't 375 # change very often. Similarly for fstab, it might have changed 376 # in the target build. 377 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict) 378 379 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options") 380 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties") 381 oem_dicts = None 382 if oem_props: 383 oem_dicts = _LoadOemDicts(script, recovery_mount_options) 384 385 target_fp = CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0], 386 OPTIONS.info_dict) 387 metadata = { 388 "post-build": target_fp, 389 "pre-device": GetOemProperty("ro.product.device", oem_props, 390 oem_dicts and oem_dicts[0], 391 OPTIONS.info_dict), 392 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict), 393 } 394 395 device_specific = common.DeviceSpecificParams( 396 input_zip=input_zip, 397 input_version=OPTIONS.info_dict["recovery_api_version"], 398 output_zip=output_zip, 399 script=script, 400 input_tmp=OPTIONS.input_tmp, 401 metadata=metadata, 402 info_dict=OPTIONS.info_dict) 403 404 assert HasRecoveryPatch(input_zip) 405 406 metadata["ota-type"] = "BLOCK" 407 408 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict) 409 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict) 410 script.AssertOlderBuild(ts, ts_text) 411 412 AppendAssertions(script, OPTIONS.info_dict, oem_dicts) 413 device_specific.FullOTA_Assertions() 414 415 # Two-step package strategy (in chronological order, which is *not* 416 # the order in which the generated script has things): 417 # 418 # if stage is not "2/3" or "3/3": 419 # write recovery image to boot partition 420 # set stage to "2/3" 421 # reboot to boot partition and restart recovery 422 # else if stage is "2/3": 423 # write recovery image to recovery partition 424 # set stage to "3/3" 425 # reboot to recovery partition and restart recovery 426 # else: 427 # (stage must be "3/3") 428 # set stage to "" 429 # do normal full package installation: 430 # wipe and install system, boot image, etc. 431 # set up system to update recovery partition on first boot 432 # complete script normally 433 # (allow recovery to mark itself finished and reboot) 434 435 recovery_img = common.GetBootableImage("recovery.img", "recovery.img", 436 OPTIONS.input_tmp, "RECOVERY") 437 if OPTIONS.two_step: 438 if not OPTIONS.info_dict.get("multistage_support", None): 439 assert False, "two-step packages not supported by this build" 440 fs = OPTIONS.info_dict["fstab"]["/misc"] 441 assert fs.fs_type.upper() == "EMMC", \ 442 "two-step packages only supported on devices with EMMC /misc partitions" 443 bcb_dev = {"bcb_dev": fs.device} 444 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data) 445 script.AppendExtra(""" 446 if get_stage("%(bcb_dev)s") == "2/3" then 447 """ % bcb_dev) 448 449 # Stage 2/3: Write recovery image to /recovery (currently running /boot). 450 script.Comment("Stage 2/3") 451 script.WriteRawImage("/recovery", "recovery.img") 452 script.AppendExtra(""" 453 set_stage("%(bcb_dev)s", "3/3"); 454 reboot_now("%(bcb_dev)s", "recovery"); 455 else if get_stage("%(bcb_dev)s") == "3/3" then 456 """ % bcb_dev) 457 458 # Stage 3/3: Make changes. 459 script.Comment("Stage 3/3") 460 461 # Dump fingerprints 462 script.Print("Target: %s" % target_fp) 463 464 device_specific.FullOTA_InstallBegin() 465 466 system_progress = 0.75 467 468 if OPTIONS.wipe_user_data: 469 system_progress -= 0.1 470 if HasVendorPartition(input_zip): 471 system_progress -= 0.1 472 473 # Place a copy of file_contexts.bin into the OTA package which will be used 474 # by the recovery program. 475 if "selinux_fc" in OPTIONS.info_dict: 476 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip) 477 478 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options") 479 480 script.ShowProgress(system_progress, 0) 481 482 # Full OTA is done as an "incremental" against an empty source image. This 483 # has the effect of writing new data from the package to the entire 484 # partition, but lets us reuse the updater code that writes incrementals to 485 # do it. 486 system_tgt = GetImage("system", OPTIONS.input_tmp) 487 system_tgt.ResetFileMap() 488 system_diff = common.BlockDifference("system", system_tgt, src=None) 489 system_diff.WriteScript(script, output_zip) 490 491 boot_img = common.GetBootableImage( 492 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT") 493 494 if HasVendorPartition(input_zip): 495 script.ShowProgress(0.1, 0) 496 497 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp) 498 vendor_tgt.ResetFileMap() 499 vendor_diff = common.BlockDifference("vendor", vendor_tgt) 500 vendor_diff.WriteScript(script, output_zip) 501 502 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict) 503 common.ZipWriteStr(output_zip, "boot.img", boot_img.data) 504 505 script.ShowProgress(0.05, 5) 506 script.WriteRawImage("/boot", "boot.img") 507 508 script.ShowProgress(0.2, 10) 509 device_specific.FullOTA_InstallEnd() 510 511 if OPTIONS.extra_script is not None: 512 script.AppendExtra(OPTIONS.extra_script) 513 514 script.UnmountAll() 515 516 if OPTIONS.wipe_user_data: 517 script.ShowProgress(0.1, 10) 518 script.FormatPartition("/data") 519 520 if OPTIONS.two_step: 521 script.AppendExtra(""" 522 set_stage("%(bcb_dev)s", ""); 523 """ % bcb_dev) 524 script.AppendExtra("else\n") 525 526 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot. 527 script.Comment("Stage 1/3") 528 _WriteRecoveryImageToBoot(script, output_zip) 529 530 script.AppendExtra(""" 531 set_stage("%(bcb_dev)s", "2/3"); 532 reboot_now("%(bcb_dev)s", ""); 533 endif; 534 endif; 535 """ % bcb_dev) 536 537 script.SetProgress(1) 538 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary) 539 metadata["ota-required-cache"] = str(script.required_cache) 540 WriteMetadata(metadata, output_zip) 541 542 543 def WritePolicyConfig(file_name, output_zip): 544 common.ZipWrite(output_zip, file_name, os.path.basename(file_name)) 545 546 547 def WriteMetadata(metadata, output_zip): 548 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())]) 549 common.ZipWriteStr(output_zip, METADATA_NAME, value, 550 compress_type=zipfile.ZIP_STORED) 551 552 553 def GetBuildProp(prop, info_dict): 554 """Return the fingerprint of the build of a given target-files info_dict.""" 555 try: 556 return info_dict.get("build.prop", {})[prop] 557 except KeyError: 558 raise common.ExternalError("couldn't find %s in build.prop" % (prop,)) 559 560 561 def HandleDowngradeMetadata(metadata): 562 # Only incremental OTAs are allowed to reach here. 563 assert OPTIONS.incremental_source is not None 564 565 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict) 566 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict) 567 is_downgrade = long(post_timestamp) < long(pre_timestamp) 568 569 if OPTIONS.downgrade: 570 if not is_downgrade: 571 raise RuntimeError("--downgrade specified but no downgrade detected: " 572 "pre: %s, post: %s" % (pre_timestamp, post_timestamp)) 573 metadata["ota-downgrade"] = "yes" 574 elif OPTIONS.timestamp: 575 if not is_downgrade: 576 raise RuntimeError("--timestamp specified but no timestamp hack needed: " 577 "pre: %s, post: %s" % (pre_timestamp, post_timestamp)) 578 metadata["post-timestamp"] = str(long(pre_timestamp) + 1) 579 else: 580 if is_downgrade: 581 raise RuntimeError("Downgrade detected based on timestamp check: " 582 "pre: %s, post: %s. Need to specify --timestamp OR " 583 "--downgrade to allow building the incremental." % ( 584 pre_timestamp, post_timestamp)) 585 metadata["post-timestamp"] = post_timestamp 586 587 588 def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip): 589 source_version = OPTIONS.source_info_dict["recovery_api_version"] 590 target_version = OPTIONS.target_info_dict["recovery_api_version"] 591 592 if source_version == 0: 593 print("WARNING: generating edify script for a source that " 594 "can't install it.") 595 script = edify_generator.EdifyGenerator( 596 source_version, OPTIONS.target_info_dict, 597 fstab=OPTIONS.source_info_dict["fstab"]) 598 599 recovery_mount_options = OPTIONS.source_info_dict.get( 600 "recovery_mount_options") 601 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties") 602 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties") 603 oem_dicts = None 604 if source_oem_props and target_oem_props: 605 oem_dicts = _LoadOemDicts(script, recovery_mount_options) 606 607 metadata = { 608 "pre-device": GetOemProperty("ro.product.device", source_oem_props, 609 oem_dicts and oem_dicts[0], 610 OPTIONS.source_info_dict), 611 "ota-type": "BLOCK", 612 } 613 614 HandleDowngradeMetadata(metadata) 615 616 device_specific = common.DeviceSpecificParams( 617 source_zip=source_zip, 618 source_version=source_version, 619 target_zip=target_zip, 620 target_version=target_version, 621 output_zip=output_zip, 622 script=script, 623 metadata=metadata, 624 info_dict=OPTIONS.source_info_dict) 625 626 source_fp = CalculateFingerprint(source_oem_props, oem_dicts and oem_dicts[0], 627 OPTIONS.source_info_dict) 628 target_fp = CalculateFingerprint(target_oem_props, oem_dicts and oem_dicts[0], 629 OPTIONS.target_info_dict) 630 metadata["pre-build"] = source_fp 631 metadata["post-build"] = target_fp 632 metadata["pre-build-incremental"] = GetBuildProp( 633 "ro.build.version.incremental", OPTIONS.source_info_dict) 634 metadata["post-build-incremental"] = GetBuildProp( 635 "ro.build.version.incremental", OPTIONS.target_info_dict) 636 637 source_boot = common.GetBootableImage( 638 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", 639 OPTIONS.source_info_dict) 640 target_boot = common.GetBootableImage( 641 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT") 642 updating_boot = (not OPTIONS.two_step and 643 (source_boot.data != target_boot.data)) 644 645 target_recovery = common.GetBootableImage( 646 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY") 647 648 system_src = GetImage("system", OPTIONS.source_tmp) 649 system_tgt = GetImage("system", OPTIONS.target_tmp) 650 651 blockimgdiff_version = 1 652 if OPTIONS.info_dict: 653 blockimgdiff_version = max( 654 int(i) for i in 655 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(",")) 656 657 # Check the first block of the source system partition for remount R/W only 658 # if the filesystem is ext4. 659 system_src_partition = OPTIONS.source_info_dict["fstab"]["/system"] 660 check_first_block = system_src_partition.fs_type == "ext4" 661 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be 662 # in zip formats. However with squashfs, a) all files are compressed in LZ4; 663 # b) the blocks listed in block map may not contain all the bytes for a given 664 # file (because they're rounded to be 4K-aligned). 665 system_tgt_partition = OPTIONS.target_info_dict["fstab"]["/system"] 666 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or 667 system_tgt_partition.fs_type == "squashfs") 668 system_diff = common.BlockDifference("system", system_tgt, system_src, 669 check_first_block, 670 version=blockimgdiff_version, 671 disable_imgdiff=disable_imgdiff) 672 673 if HasVendorPartition(target_zip): 674 if not HasVendorPartition(source_zip): 675 raise RuntimeError("can't generate incremental that adds /vendor") 676 vendor_src = GetImage("vendor", OPTIONS.source_tmp) 677 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp) 678 679 # Check first block of vendor partition for remount R/W only if 680 # disk type is ext4 681 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"] 682 check_first_block = vendor_partition.fs_type == "ext4" 683 disable_imgdiff = vendor_partition.fs_type == "squashfs" 684 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src, 685 check_first_block, 686 version=blockimgdiff_version, 687 disable_imgdiff=disable_imgdiff) 688 else: 689 vendor_diff = None 690 691 AppendAssertions(script, OPTIONS.target_info_dict, oem_dicts) 692 device_specific.IncrementalOTA_Assertions() 693 694 # Two-step incremental package strategy (in chronological order, 695 # which is *not* the order in which the generated script has 696 # things): 697 # 698 # if stage is not "2/3" or "3/3": 699 # do verification on current system 700 # write recovery image to boot partition 701 # set stage to "2/3" 702 # reboot to boot partition and restart recovery 703 # else if stage is "2/3": 704 # write recovery image to recovery partition 705 # set stage to "3/3" 706 # reboot to recovery partition and restart recovery 707 # else: 708 # (stage must be "3/3") 709 # perform update: 710 # patch system files, etc. 711 # force full install of new boot image 712 # set up system to update recovery partition on first boot 713 # complete script normally 714 # (allow recovery to mark itself finished and reboot) 715 716 if OPTIONS.two_step: 717 if not OPTIONS.source_info_dict.get("multistage_support", None): 718 assert False, "two-step packages not supported by this build" 719 fs = OPTIONS.source_info_dict["fstab"]["/misc"] 720 assert fs.fs_type.upper() == "EMMC", \ 721 "two-step packages only supported on devices with EMMC /misc partitions" 722 bcb_dev = {"bcb_dev": fs.device} 723 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data) 724 script.AppendExtra(""" 725 if get_stage("%(bcb_dev)s") == "2/3" then 726 """ % bcb_dev) 727 728 # Stage 2/3: Write recovery image to /recovery (currently running /boot). 729 script.Comment("Stage 2/3") 730 script.AppendExtra("sleep(20);\n") 731 script.WriteRawImage("/recovery", "recovery.img") 732 script.AppendExtra(""" 733 set_stage("%(bcb_dev)s", "3/3"); 734 reboot_now("%(bcb_dev)s", "recovery"); 735 else if get_stage("%(bcb_dev)s") != "3/3" then 736 """ % bcb_dev) 737 738 # Stage 1/3: (a) Verify the current system. 739 script.Comment("Stage 1/3") 740 741 # Dump fingerprints 742 script.Print("Source: %s" % (source_fp,)) 743 script.Print("Target: %s" % (target_fp,)) 744 745 script.Print("Verifying current system...") 746 747 device_specific.IncrementalOTA_VerifyBegin() 748 749 # When blockimgdiff version is less than 3 (non-resumable block-based OTA), 750 # patching on a device that's already on the target build will damage the 751 # system. Because operations like move don't check the block state, they 752 # always apply the changes unconditionally. 753 if blockimgdiff_version <= 2: 754 if source_oem_props is None: 755 script.AssertSomeFingerprint(source_fp) 756 else: 757 script.AssertSomeThumbprint( 758 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict)) 759 760 else: # blockimgdiff_version > 2 761 if source_oem_props is None and target_oem_props is None: 762 script.AssertSomeFingerprint(source_fp, target_fp) 763 elif source_oem_props is not None and target_oem_props is not None: 764 script.AssertSomeThumbprint( 765 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict), 766 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict)) 767 elif source_oem_props is None and target_oem_props is not None: 768 script.AssertFingerprintOrThumbprint( 769 source_fp, 770 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict)) 771 else: 772 script.AssertFingerprintOrThumbprint( 773 target_fp, 774 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict)) 775 776 # Check the required cache size (i.e. stashed blocks). 777 size = [] 778 if system_diff: 779 size.append(system_diff.required_cache) 780 if vendor_diff: 781 size.append(vendor_diff.required_cache) 782 783 if updating_boot: 784 boot_type, boot_device = common.GetTypeAndDevice( 785 "/boot", OPTIONS.source_info_dict) 786 d = common.Difference(target_boot, source_boot) 787 _, _, d = d.ComputePatch() 788 if d is None: 789 include_full_boot = True 790 common.ZipWriteStr(output_zip, "boot.img", target_boot.data) 791 else: 792 include_full_boot = False 793 794 print("boot target: %d source: %d diff: %d" % ( 795 target_boot.size, source_boot.size, len(d))) 796 797 common.ZipWriteStr(output_zip, "patch/boot.img.p", d) 798 799 script.PatchCheck("%s:%s:%d:%s:%d:%s" % 800 (boot_type, boot_device, 801 source_boot.size, source_boot.sha1, 802 target_boot.size, target_boot.sha1)) 803 size.append(target_boot.size) 804 805 if size: 806 script.CacheFreeSpaceCheck(max(size)) 807 808 device_specific.IncrementalOTA_VerifyEnd() 809 810 if OPTIONS.two_step: 811 # Stage 1/3: (b) Write recovery image to /boot. 812 _WriteRecoveryImageToBoot(script, output_zip) 813 814 script.AppendExtra(""" 815 set_stage("%(bcb_dev)s", "2/3"); 816 reboot_now("%(bcb_dev)s", ""); 817 else 818 """ % bcb_dev) 819 820 # Stage 3/3: Make changes. 821 script.Comment("Stage 3/3") 822 823 # Verify the existing partitions. 824 system_diff.WriteVerifyScript(script, touched_blocks_only=True) 825 if vendor_diff: 826 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True) 827 828 script.Comment("---- start making changes here ----") 829 830 device_specific.IncrementalOTA_InstallBegin() 831 832 system_diff.WriteScript(script, output_zip, 833 progress=0.8 if vendor_diff else 0.9) 834 835 if vendor_diff: 836 vendor_diff.WriteScript(script, output_zip, progress=0.1) 837 838 if OPTIONS.two_step: 839 common.ZipWriteStr(output_zip, "boot.img", target_boot.data) 840 script.WriteRawImage("/boot", "boot.img") 841 print("writing full boot image (forced by two-step mode)") 842 843 if not OPTIONS.two_step: 844 if updating_boot: 845 if include_full_boot: 846 print("boot image changed; including full.") 847 script.Print("Installing boot image...") 848 script.WriteRawImage("/boot", "boot.img") 849 else: 850 # Produce the boot image by applying a patch to the current 851 # contents of the boot partition, and write it back to the 852 # partition. 853 print("boot image changed; including patch.") 854 script.Print("Patching boot image...") 855 script.ShowProgress(0.1, 10) 856 script.ApplyPatch("%s:%s:%d:%s:%d:%s" 857 % (boot_type, boot_device, 858 source_boot.size, source_boot.sha1, 859 target_boot.size, target_boot.sha1), 860 "-", 861 target_boot.size, target_boot.sha1, 862 source_boot.sha1, "patch/boot.img.p") 863 else: 864 print("boot image unchanged; skipping.") 865 866 # Do device-specific installation (eg, write radio image). 867 device_specific.IncrementalOTA_InstallEnd() 868 869 if OPTIONS.extra_script is not None: 870 script.AppendExtra(OPTIONS.extra_script) 871 872 if OPTIONS.wipe_user_data: 873 script.Print("Erasing user data...") 874 script.FormatPartition("/data") 875 metadata["ota-wipe"] = "yes" 876 877 if OPTIONS.two_step: 878 script.AppendExtra(""" 879 set_stage("%(bcb_dev)s", ""); 880 endif; 881 endif; 882 """ % bcb_dev) 883 884 script.SetProgress(1) 885 # For downgrade OTAs, we prefer to use the update-binary in the source 886 # build that is actually newer than the one in the target build. 887 if OPTIONS.downgrade: 888 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary) 889 else: 890 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary) 891 metadata["ota-required-cache"] = str(script.required_cache) 892 WriteMetadata(metadata, output_zip) 893 894 895 def WriteVerifyPackage(input_zip, output_zip): 896 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict) 897 898 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties") 899 recovery_mount_options = OPTIONS.info_dict.get( 900 "recovery_mount_options") 901 oem_dicts = None 902 if oem_props: 903 oem_dicts = _LoadOemDicts(script, recovery_mount_options) 904 905 target_fp = CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0], 906 OPTIONS.info_dict) 907 metadata = { 908 "post-build": target_fp, 909 "pre-device": GetOemProperty("ro.product.device", oem_props, 910 oem_dicts and oem_dicts[0], 911 OPTIONS.info_dict), 912 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict), 913 } 914 915 device_specific = common.DeviceSpecificParams( 916 input_zip=input_zip, 917 input_version=OPTIONS.info_dict["recovery_api_version"], 918 output_zip=output_zip, 919 script=script, 920 input_tmp=OPTIONS.input_tmp, 921 metadata=metadata, 922 info_dict=OPTIONS.info_dict) 923 924 AppendAssertions(script, OPTIONS.info_dict, oem_dicts) 925 926 script.Print("Verifying device images against %s..." % target_fp) 927 script.AppendExtra("") 928 929 script.Print("Verifying boot...") 930 boot_img = common.GetBootableImage( 931 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT") 932 boot_type, boot_device = common.GetTypeAndDevice( 933 "/boot", OPTIONS.info_dict) 934 script.Verify("%s:%s:%d:%s" % ( 935 boot_type, boot_device, boot_img.size, boot_img.sha1)) 936 script.AppendExtra("") 937 938 script.Print("Verifying recovery...") 939 recovery_img = common.GetBootableImage( 940 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY") 941 recovery_type, recovery_device = common.GetTypeAndDevice( 942 "/recovery", OPTIONS.info_dict) 943 script.Verify("%s:%s:%d:%s" % ( 944 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1)) 945 script.AppendExtra("") 946 947 system_tgt = GetImage("system", OPTIONS.input_tmp) 948 system_tgt.ResetFileMap() 949 system_diff = common.BlockDifference("system", system_tgt, src=None) 950 system_diff.WriteStrictVerifyScript(script) 951 952 if HasVendorPartition(input_zip): 953 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp) 954 vendor_tgt.ResetFileMap() 955 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None) 956 vendor_diff.WriteStrictVerifyScript(script) 957 958 # Device specific partitions, such as radio, bootloader and etc. 959 device_specific.VerifyOTA_Assertions() 960 961 script.SetProgress(1.0) 962 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary) 963 metadata["ota-required-cache"] = str(script.required_cache) 964 WriteMetadata(metadata, output_zip) 965 966 967 def WriteABOTAPackageWithBrilloScript(target_file, output_file, 968 source_file=None): 969 """Generate an Android OTA package that has A/B update payload.""" 970 971 def ComputeStreamingMetadata(zip_file, reserve_space=False, 972 expected_length=None): 973 """Compute the streaming metadata for a given zip. 974 975 When 'reserve_space' is True, we reserve extra space for the offset and 976 length of the metadata entry itself, although we don't know the final 977 values until the package gets signed. This function will be called again 978 after signing. We then write the actual values and pad the string to the 979 length we set earlier. Note that we can't use the actual length of the 980 metadata entry in the second run. Otherwise the offsets for other entries 981 will be changing again. 982 """ 983 984 def ComputeEntryOffsetSize(name): 985 """Compute the zip entry offset and size.""" 986 info = zip_file.getinfo(name) 987 offset = info.header_offset + len(info.FileHeader()) 988 size = info.file_size 989 return '%s:%d:%d' % (os.path.basename(name), offset, size) 990 991 # payload.bin and payload_properties.txt must exist. 992 offsets = [ComputeEntryOffsetSize('payload.bin'), 993 ComputeEntryOffsetSize('payload_properties.txt')] 994 995 # care_map.txt is available only if dm-verity is enabled. 996 if 'care_map.txt' in zip_file.namelist(): 997 offsets.append(ComputeEntryOffsetSize('care_map.txt')) 998 999 if 'compatibility.zip' in zip_file.namelist(): 1000 offsets.append(ComputeEntryOffsetSize('compatibility.zip')) 1001 1002 # 'META-INF/com/android/metadata' is required. We don't know its actual 1003 # offset and length (as well as the values for other entries). So we 1004 # reserve 10-byte as a placeholder, which is to cover the space for metadata 1005 # entry ('xx:xxx', since it's ZIP_STORED which should appear at the 1006 # beginning of the zip), as well as the possible value changes in other 1007 # entries. 1008 if reserve_space: 1009 offsets.append('metadata:' + ' ' * 10) 1010 else: 1011 offsets.append(ComputeEntryOffsetSize(METADATA_NAME)) 1012 1013 value = ','.join(offsets) 1014 if expected_length is not None: 1015 assert len(value) <= expected_length, \ 1016 'Insufficient reserved space: reserved=%d, actual=%d' % ( 1017 expected_length, len(value)) 1018 value += ' ' * (expected_length - len(value)) 1019 return value 1020 1021 # The place where the output from the subprocess should go. 1022 log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE 1023 1024 # A/B updater expects a signing key in RSA format. Gets the key ready for 1025 # later use in step 3, unless a payload_signer has been specified. 1026 if OPTIONS.payload_signer is None: 1027 cmd = ["openssl", "pkcs8", 1028 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix, 1029 "-inform", "DER"] 1030 pw = OPTIONS.key_passwords[OPTIONS.package_key] 1031 cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"]) 1032 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key") 1033 cmd.extend(["-out", rsa_key]) 1034 p1 = common.Run(cmd, verbose=False, stdout=log_file, stderr=subprocess.STDOUT) 1035 p1.communicate() 1036 assert p1.returncode == 0, "openssl pkcs8 failed" 1037 1038 # Stage the output zip package for package signing. 1039 temp_zip_file = tempfile.NamedTemporaryFile() 1040 output_zip = zipfile.ZipFile(temp_zip_file, "w", 1041 compression=zipfile.ZIP_DEFLATED) 1042 1043 # Metadata to comply with Android OTA package format. 1044 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None) 1045 oem_dicts = None 1046 if oem_props: 1047 oem_dicts = _LoadOemDicts(None) 1048 1049 metadata = { 1050 "post-build": CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0], 1051 OPTIONS.info_dict), 1052 "post-build-incremental" : GetBuildProp("ro.build.version.incremental", 1053 OPTIONS.info_dict), 1054 "pre-device": GetOemProperty("ro.product.device", oem_props, 1055 oem_dicts and oem_dicts[0], 1056 OPTIONS.info_dict), 1057 "ota-required-cache": "0", 1058 "ota-type": "AB", 1059 } 1060 1061 if source_file is not None: 1062 metadata["pre-build"] = CalculateFingerprint(oem_props, 1063 oem_dicts and oem_dicts[0], 1064 OPTIONS.source_info_dict) 1065 metadata["pre-build-incremental"] = GetBuildProp( 1066 "ro.build.version.incremental", OPTIONS.source_info_dict) 1067 1068 HandleDowngradeMetadata(metadata) 1069 else: 1070 metadata["post-timestamp"] = GetBuildProp( 1071 "ro.build.date.utc", OPTIONS.info_dict) 1072 1073 # 1. Generate payload. 1074 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin") 1075 cmd = ["brillo_update_payload", "generate", 1076 "--payload", payload_file, 1077 "--target_image", target_file] 1078 if source_file is not None: 1079 cmd.extend(["--source_image", source_file]) 1080 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT) 1081 p1.communicate() 1082 assert p1.returncode == 0, "brillo_update_payload generate failed" 1083 1084 # 2. Generate hashes of the payload and metadata files. 1085 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin") 1086 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin") 1087 cmd = ["brillo_update_payload", "hash", 1088 "--unsigned_payload", payload_file, 1089 "--signature_size", "256", 1090 "--metadata_hash_file", metadata_sig_file, 1091 "--payload_hash_file", payload_sig_file] 1092 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT) 1093 p1.communicate() 1094 assert p1.returncode == 0, "brillo_update_payload hash failed" 1095 1096 # 3. Sign the hashes and insert them back into the payload file. 1097 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-", 1098 suffix=".bin") 1099 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-", 1100 suffix=".bin") 1101 # 3a. Sign the payload hash. 1102 if OPTIONS.payload_signer is not None: 1103 cmd = [OPTIONS.payload_signer] 1104 cmd.extend(OPTIONS.payload_signer_args) 1105 else: 1106 cmd = ["openssl", "pkeyutl", "-sign", 1107 "-inkey", rsa_key, 1108 "-pkeyopt", "digest:sha256"] 1109 cmd.extend(["-in", payload_sig_file, 1110 "-out", signed_payload_sig_file]) 1111 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT) 1112 p1.communicate() 1113 assert p1.returncode == 0, "openssl sign payload failed" 1114 1115 # 3b. Sign the metadata hash. 1116 if OPTIONS.payload_signer is not None: 1117 cmd = [OPTIONS.payload_signer] 1118 cmd.extend(OPTIONS.payload_signer_args) 1119 else: 1120 cmd = ["openssl", "pkeyutl", "-sign", 1121 "-inkey", rsa_key, 1122 "-pkeyopt", "digest:sha256"] 1123 cmd.extend(["-in", metadata_sig_file, 1124 "-out", signed_metadata_sig_file]) 1125 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT) 1126 p1.communicate() 1127 assert p1.returncode == 0, "openssl sign metadata failed" 1128 1129 # 3c. Insert the signatures back into the payload file. 1130 signed_payload_file = common.MakeTempFile(prefix="signed-payload-", 1131 suffix=".bin") 1132 cmd = ["brillo_update_payload", "sign", 1133 "--unsigned_payload", payload_file, 1134 "--payload", signed_payload_file, 1135 "--signature_size", "256", 1136 "--metadata_signature_file", signed_metadata_sig_file, 1137 "--payload_signature_file", signed_payload_sig_file] 1138 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT) 1139 p1.communicate() 1140 assert p1.returncode == 0, "brillo_update_payload sign failed" 1141 1142 # 4. Dump the signed payload properties. 1143 properties_file = common.MakeTempFile(prefix="payload-properties-", 1144 suffix=".txt") 1145 cmd = ["brillo_update_payload", "properties", 1146 "--payload", signed_payload_file, 1147 "--properties_file", properties_file] 1148 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT) 1149 p1.communicate() 1150 assert p1.returncode == 0, "brillo_update_payload properties failed" 1151 1152 if OPTIONS.wipe_user_data: 1153 with open(properties_file, "a") as f: 1154 f.write("POWERWASH=1\n") 1155 metadata["ota-wipe"] = "yes" 1156 1157 # Add the signed payload file and properties into the zip. In order to 1158 # support streaming, we pack payload.bin, payload_properties.txt and 1159 # care_map.txt as ZIP_STORED. So these entries can be read directly with 1160 # the offset and length pairs. 1161 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin", 1162 compress_type=zipfile.ZIP_STORED) 1163 common.ZipWrite(output_zip, properties_file, 1164 arcname="payload_properties.txt", 1165 compress_type=zipfile.ZIP_STORED) 1166 1167 # If dm-verity is supported for the device, copy contents of care_map 1168 # into A/B OTA package. 1169 target_zip = zipfile.ZipFile(target_file, "r") 1170 if (OPTIONS.info_dict.get("verity") == "true" or 1171 OPTIONS.info_dict.get("avb_enable") == "true"): 1172 care_map_path = "META/care_map.txt" 1173 namelist = target_zip.namelist() 1174 if care_map_path in namelist: 1175 care_map_data = target_zip.read(care_map_path) 1176 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data, 1177 compress_type=zipfile.ZIP_STORED) 1178 else: 1179 print("Warning: cannot find care map file in target_file package") 1180 1181 if HasVendorPartition(target_zip): 1182 update_vendor = True 1183 update_system = True 1184 1185 # If incremental then figure out what is being updated so metadata only for 1186 # the updated image is included. 1187 if source_file is not None: 1188 input_tmp, input_zip = common.UnzipTemp( 1189 target_file, UNZIP_PATTERN) 1190 source_tmp, source_zip = common.UnzipTemp( 1191 source_file, UNZIP_PATTERN) 1192 1193 vendor_src = GetImage("vendor", source_tmp) 1194 vendor_tgt = GetImage("vendor", input_tmp) 1195 system_src = GetImage("system", source_tmp) 1196 system_tgt = GetImage("system", input_tmp) 1197 1198 update_system = system_src.TotalSha1() != system_tgt.TotalSha1() 1199 update_vendor = vendor_src.TotalSha1() != vendor_tgt.TotalSha1() 1200 1201 input_zip.close() 1202 source_zip.close() 1203 1204 target_zip = zipfile.ZipFile(target_file, "r") 1205 AddCompatibilityArchive(target_zip, output_zip, update_system, 1206 update_vendor) 1207 common.ZipClose(target_zip) 1208 1209 # Write the current metadata entry with placeholders. 1210 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata( 1211 output_zip, reserve_space=True) 1212 WriteMetadata(metadata, output_zip) 1213 common.ZipClose(output_zip) 1214 1215 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the 1216 # zip entries, as well as padding the entry headers. We do a preliminary 1217 # signing (with an incomplete metadata entry) to allow that to happen. Then 1218 # compute the zip entry offsets, write back the final metadata and do the 1219 # final signing. 1220 prelim_signing = tempfile.NamedTemporaryFile() 1221 SignOutput(temp_zip_file.name, prelim_signing.name) 1222 common.ZipClose(temp_zip_file) 1223 1224 # Open the signed zip. Compute the final metadata that's needed for streaming. 1225 prelim_zip = zipfile.ZipFile(prelim_signing, "r", 1226 compression=zipfile.ZIP_DEFLATED) 1227 expected_length = len(metadata['ota-streaming-property-files']) 1228 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata( 1229 prelim_zip, reserve_space=False, expected_length=expected_length) 1230 1231 # Copy the zip entries, as we cannot update / delete entries with zipfile. 1232 final_signing = tempfile.NamedTemporaryFile() 1233 output_zip = zipfile.ZipFile(final_signing, "w", 1234 compression=zipfile.ZIP_DEFLATED) 1235 for item in prelim_zip.infolist(): 1236 if item.filename == METADATA_NAME: 1237 continue 1238 1239 data = prelim_zip.read(item.filename) 1240 out_info = copy.copy(item) 1241 common.ZipWriteStr(output_zip, out_info, data) 1242 1243 # Now write the final metadata entry. 1244 WriteMetadata(metadata, output_zip) 1245 common.ZipClose(prelim_zip) 1246 common.ZipClose(output_zip) 1247 1248 # Re-sign the package after updating the metadata entry. 1249 SignOutput(final_signing.name, output_file) 1250 final_signing.close() 1251 1252 # Reopen the final signed zip to double check the streaming metadata. 1253 output_zip = zipfile.ZipFile(output_file, "r") 1254 actual = metadata['ota-streaming-property-files'].strip() 1255 expected = ComputeStreamingMetadata(output_zip) 1256 assert actual == expected, \ 1257 "Mismatching streaming metadata: %s vs %s." % (actual, expected) 1258 common.ZipClose(output_zip) 1259 1260 1261 def main(argv): 1262 1263 def option_handler(o, a): 1264 if o == "--board_config": 1265 pass # deprecated 1266 elif o in ("-k", "--package_key"): 1267 OPTIONS.package_key = a 1268 elif o in ("-i", "--incremental_from"): 1269 OPTIONS.incremental_source = a 1270 elif o == "--full_radio": 1271 OPTIONS.full_radio = True 1272 elif o == "--full_bootloader": 1273 OPTIONS.full_bootloader = True 1274 elif o in ("-w", "--wipe_user_data"): 1275 OPTIONS.wipe_user_data = True 1276 elif o == "--downgrade": 1277 OPTIONS.downgrade = True 1278 OPTIONS.wipe_user_data = True 1279 elif o == "--override_timestamp": 1280 OPTIONS.timestamp = True 1281 elif o in ("-o", "--oem_settings"): 1282 OPTIONS.oem_source = a.split(',') 1283 elif o == "--oem_no_mount": 1284 OPTIONS.oem_no_mount = True 1285 elif o in ("-e", "--extra_script"): 1286 OPTIONS.extra_script = a 1287 elif o in ("-t", "--worker_threads"): 1288 if a.isdigit(): 1289 OPTIONS.worker_threads = int(a) 1290 else: 1291 raise ValueError("Cannot parse value %r for option %r - only " 1292 "integers are allowed." % (a, o)) 1293 elif o in ("-2", "--two_step"): 1294 OPTIONS.two_step = True 1295 elif o == "--no_signing": 1296 OPTIONS.no_signing = True 1297 elif o == "--verify": 1298 OPTIONS.verify = True 1299 elif o == "--block": 1300 OPTIONS.block_based = True 1301 elif o in ("-b", "--binary"): 1302 OPTIONS.updater_binary = a 1303 elif o in ("--no_fallback_to_full",): 1304 OPTIONS.fallback_to_full = False 1305 elif o == "--stash_threshold": 1306 try: 1307 OPTIONS.stash_threshold = float(a) 1308 except ValueError: 1309 raise ValueError("Cannot parse value %r for option %r - expecting " 1310 "a float" % (a, o)) 1311 elif o == "--gen_verify": 1312 OPTIONS.gen_verify = True 1313 elif o == "--log_diff": 1314 OPTIONS.log_diff = a 1315 elif o == "--payload_signer": 1316 OPTIONS.payload_signer = a 1317 elif o == "--payload_signer_args": 1318 OPTIONS.payload_signer_args = shlex.split(a) 1319 elif o == "--extracted_input_target_files": 1320 OPTIONS.extracted_input = a 1321 else: 1322 return False 1323 return True 1324 1325 args = common.ParseOptions(argv, __doc__, 1326 extra_opts="b:k:i:d:we:t:2o:", 1327 extra_long_opts=[ 1328 "board_config=", 1329 "package_key=", 1330 "incremental_from=", 1331 "full_radio", 1332 "full_bootloader", 1333 "wipe_user_data", 1334 "downgrade", 1335 "override_timestamp", 1336 "extra_script=", 1337 "worker_threads=", 1338 "two_step", 1339 "no_signing", 1340 "block", 1341 "binary=", 1342 "oem_settings=", 1343 "oem_no_mount", 1344 "verify", 1345 "no_fallback_to_full", 1346 "stash_threshold=", 1347 "gen_verify", 1348 "log_diff=", 1349 "payload_signer=", 1350 "payload_signer_args=", 1351 "extracted_input_target_files=", 1352 ], extra_option_handler=option_handler) 1353 1354 if len(args) != 2: 1355 common.Usage(__doc__) 1356 sys.exit(1) 1357 1358 if OPTIONS.downgrade: 1359 # Sanity check to enforce a data wipe. 1360 if not OPTIONS.wipe_user_data: 1361 raise ValueError("Cannot downgrade without a data wipe") 1362 1363 # We should only allow downgrading incrementals (as opposed to full). 1364 # Otherwise the device may go back from arbitrary build with this full 1365 # OTA package. 1366 if OPTIONS.incremental_source is None: 1367 raise ValueError("Cannot generate downgradable full OTAs") 1368 1369 assert not (OPTIONS.downgrade and OPTIONS.timestamp), \ 1370 "Cannot have --downgrade AND --override_timestamp both" 1371 1372 # Load the dict file from the zip directly to have a peek at the OTA type. 1373 # For packages using A/B update, unzipping is not needed. 1374 if OPTIONS.extracted_input is not None: 1375 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input, OPTIONS.extracted_input) 1376 else: 1377 input_zip = zipfile.ZipFile(args[0], "r") 1378 OPTIONS.info_dict = common.LoadInfoDict(input_zip) 1379 common.ZipClose(input_zip) 1380 1381 ab_update = OPTIONS.info_dict.get("ab_update") == "true" 1382 1383 # Use the default key to sign the package if not specified with package_key. 1384 # package_keys are needed on ab_updates, so always define them if an 1385 # ab_update is getting created. 1386 if not OPTIONS.no_signing or ab_update: 1387 if OPTIONS.package_key is None: 1388 OPTIONS.package_key = OPTIONS.info_dict.get( 1389 "default_system_dev_certificate", 1390 "build/target/product/security/testkey") 1391 # Get signing keys 1392 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key]) 1393 1394 if ab_update: 1395 if OPTIONS.incremental_source is not None: 1396 OPTIONS.target_info_dict = OPTIONS.info_dict 1397 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r") 1398 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip) 1399 common.ZipClose(source_zip) 1400 1401 if OPTIONS.verbose: 1402 print("--- target info ---") 1403 common.DumpInfoDict(OPTIONS.info_dict) 1404 1405 if OPTIONS.incremental_source is not None: 1406 print("--- source info ---") 1407 common.DumpInfoDict(OPTIONS.source_info_dict) 1408 1409 WriteABOTAPackageWithBrilloScript( 1410 target_file=args[0], 1411 output_file=args[1], 1412 source_file=OPTIONS.incremental_source) 1413 1414 print("done.") 1415 return 1416 1417 if OPTIONS.extra_script is not None: 1418 OPTIONS.extra_script = open(OPTIONS.extra_script).read() 1419 1420 if OPTIONS.extracted_input is not None: 1421 OPTIONS.input_tmp = OPTIONS.extracted_input 1422 OPTIONS.target_tmp = OPTIONS.input_tmp 1423 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.input_tmp, OPTIONS.input_tmp) 1424 input_zip = zipfile.ZipFile(args[0], "r") 1425 else: 1426 print("unzipping target target-files...") 1427 OPTIONS.input_tmp, input_zip = common.UnzipTemp( 1428 args[0], UNZIP_PATTERN) 1429 1430 OPTIONS.target_tmp = OPTIONS.input_tmp 1431 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp) 1432 1433 if OPTIONS.verbose: 1434 print("--- target info ---") 1435 common.DumpInfoDict(OPTIONS.info_dict) 1436 1437 # If the caller explicitly specified the device-specific extensions 1438 # path via -s/--device_specific, use that. Otherwise, use 1439 # META/releasetools.py if it is present in the target target_files. 1440 # Otherwise, take the path of the file from 'tool_extensions' in the 1441 # info dict and look for that in the local filesystem, relative to 1442 # the current directory. 1443 1444 if OPTIONS.device_specific is None: 1445 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py") 1446 if os.path.exists(from_input): 1447 print("(using device-specific extensions from target_files)") 1448 OPTIONS.device_specific = from_input 1449 else: 1450 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None) 1451 1452 if OPTIONS.device_specific is not None: 1453 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific) 1454 1455 if OPTIONS.info_dict.get("no_recovery") == "true": 1456 raise common.ExternalError( 1457 "--- target build has specified no recovery ---") 1458 1459 # Set up the output zip. Create a temporary zip file if signing is needed. 1460 if OPTIONS.no_signing: 1461 if os.path.exists(args[1]): 1462 os.unlink(args[1]) 1463 output_zip = zipfile.ZipFile(args[1], "w", 1464 compression=zipfile.ZIP_DEFLATED) 1465 else: 1466 temp_zip_file = tempfile.NamedTemporaryFile() 1467 output_zip = zipfile.ZipFile(temp_zip_file, "w", 1468 compression=zipfile.ZIP_DEFLATED) 1469 1470 # Non A/B OTAs rely on /cache partition to store temporary files. 1471 cache_size = OPTIONS.info_dict.get("cache_size", None) 1472 if cache_size is None: 1473 print("--- can't determine the cache partition size ---") 1474 OPTIONS.cache_size = cache_size 1475 1476 # Generate a verify package. 1477 if OPTIONS.gen_verify: 1478 WriteVerifyPackage(input_zip, output_zip) 1479 1480 # Generate a full OTA. 1481 elif OPTIONS.incremental_source is None: 1482 WriteFullOTAPackage(input_zip, output_zip) 1483 1484 # Generate an incremental OTA. It will fall back to generate a full OTA on 1485 # failure unless no_fallback_to_full is specified. 1486 else: 1487 print("unzipping source target-files...") 1488 OPTIONS.source_tmp, source_zip = common.UnzipTemp( 1489 OPTIONS.incremental_source, 1490 UNZIP_PATTERN) 1491 OPTIONS.target_info_dict = OPTIONS.info_dict 1492 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip, 1493 OPTIONS.source_tmp) 1494 if OPTIONS.verbose: 1495 print("--- source info ---") 1496 common.DumpInfoDict(OPTIONS.source_info_dict) 1497 try: 1498 WriteBlockIncrementalOTAPackage(input_zip, source_zip, output_zip) 1499 if OPTIONS.log_diff: 1500 out_file = open(OPTIONS.log_diff, 'w') 1501 import target_files_diff 1502 target_files_diff.recursiveDiff('', 1503 OPTIONS.source_tmp, 1504 OPTIONS.input_tmp, 1505 out_file) 1506 out_file.close() 1507 except ValueError: 1508 if not OPTIONS.fallback_to_full: 1509 raise 1510 print("--- failed to build incremental; falling back to full ---") 1511 OPTIONS.incremental_source = None 1512 WriteFullOTAPackage(input_zip, output_zip) 1513 1514 common.ZipClose(output_zip) 1515 1516 # Sign the generated zip package unless no_signing is specified. 1517 if not OPTIONS.no_signing: 1518 SignOutput(temp_zip_file.name, args[1]) 1519 temp_zip_file.close() 1520 1521 print("done.") 1522 1523 1524 if __name__ == '__main__': 1525 try: 1526 common.CloseInheritedPipes() 1527 main(sys.argv[1:]) 1528 except common.ExternalError as e: 1529 print("\n ERROR: %s\n" % (e,)) 1530 sys.exit(1) 1531 finally: 1532 common.Cleanup() 1533