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 185 METADATA_NAME = 'META-INF/com/android/metadata' 186 UNZIP_PATTERN = ['IMAGES/*', 'META/*'] 187 188 189 def SignOutput(temp_zip_name, output_zip_name): 190 key_passwords = common.GetKeyPasswords([OPTIONS.package_key]) 191 pw = 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 # Setup signing keys. 1025 if OPTIONS.package_key is None: 1026 OPTIONS.package_key = OPTIONS.info_dict.get( 1027 "default_system_dev_certificate", 1028 "build/target/product/security/testkey") 1029 1030 # A/B updater expects a signing key in RSA format. Gets the key ready for 1031 # later use in step 3, unless a payload_signer has been specified. 1032 if OPTIONS.payload_signer is None: 1033 cmd = ["openssl", "pkcs8", 1034 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix, 1035 "-inform", "DER", "-nocrypt"] 1036 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key") 1037 cmd.extend(["-out", rsa_key]) 1038 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT) 1039 p1.communicate() 1040 assert p1.returncode == 0, "openssl pkcs8 failed" 1041 1042 # Stage the output zip package for package signing. 1043 temp_zip_file = tempfile.NamedTemporaryFile() 1044 output_zip = zipfile.ZipFile(temp_zip_file, "w", 1045 compression=zipfile.ZIP_DEFLATED) 1046 1047 # Metadata to comply with Android OTA package format. 1048 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None) 1049 oem_dicts = None 1050 if oem_props: 1051 oem_dicts = _LoadOemDicts(None) 1052 1053 metadata = { 1054 "post-build": CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0], 1055 OPTIONS.info_dict), 1056 "post-build-incremental" : GetBuildProp("ro.build.version.incremental", 1057 OPTIONS.info_dict), 1058 "pre-device": GetOemProperty("ro.product.device", oem_props, 1059 oem_dicts and oem_dicts[0], 1060 OPTIONS.info_dict), 1061 "ota-required-cache": "0", 1062 "ota-type": "AB", 1063 } 1064 1065 if source_file is not None: 1066 metadata["pre-build"] = CalculateFingerprint(oem_props, 1067 oem_dicts and oem_dicts[0], 1068 OPTIONS.source_info_dict) 1069 metadata["pre-build-incremental"] = GetBuildProp( 1070 "ro.build.version.incremental", OPTIONS.source_info_dict) 1071 1072 HandleDowngradeMetadata(metadata) 1073 else: 1074 metadata["post-timestamp"] = GetBuildProp( 1075 "ro.build.date.utc", OPTIONS.info_dict) 1076 1077 # 1. Generate payload. 1078 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin") 1079 cmd = ["brillo_update_payload", "generate", 1080 "--payload", payload_file, 1081 "--target_image", target_file] 1082 if source_file is not None: 1083 cmd.extend(["--source_image", source_file]) 1084 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT) 1085 p1.communicate() 1086 assert p1.returncode == 0, "brillo_update_payload generate failed" 1087 1088 # 2. Generate hashes of the payload and metadata files. 1089 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin") 1090 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin") 1091 cmd = ["brillo_update_payload", "hash", 1092 "--unsigned_payload", payload_file, 1093 "--signature_size", "256", 1094 "--metadata_hash_file", metadata_sig_file, 1095 "--payload_hash_file", payload_sig_file] 1096 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT) 1097 p1.communicate() 1098 assert p1.returncode == 0, "brillo_update_payload hash failed" 1099 1100 # 3. Sign the hashes and insert them back into the payload file. 1101 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-", 1102 suffix=".bin") 1103 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-", 1104 suffix=".bin") 1105 # 3a. Sign the payload hash. 1106 if OPTIONS.payload_signer is not None: 1107 cmd = [OPTIONS.payload_signer] 1108 cmd.extend(OPTIONS.payload_signer_args) 1109 else: 1110 cmd = ["openssl", "pkeyutl", "-sign", 1111 "-inkey", rsa_key, 1112 "-pkeyopt", "digest:sha256"] 1113 cmd.extend(["-in", payload_sig_file, 1114 "-out", signed_payload_sig_file]) 1115 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT) 1116 p1.communicate() 1117 assert p1.returncode == 0, "openssl sign payload failed" 1118 1119 # 3b. Sign the metadata hash. 1120 if OPTIONS.payload_signer is not None: 1121 cmd = [OPTIONS.payload_signer] 1122 cmd.extend(OPTIONS.payload_signer_args) 1123 else: 1124 cmd = ["openssl", "pkeyutl", "-sign", 1125 "-inkey", rsa_key, 1126 "-pkeyopt", "digest:sha256"] 1127 cmd.extend(["-in", metadata_sig_file, 1128 "-out", signed_metadata_sig_file]) 1129 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT) 1130 p1.communicate() 1131 assert p1.returncode == 0, "openssl sign metadata failed" 1132 1133 # 3c. Insert the signatures back into the payload file. 1134 signed_payload_file = common.MakeTempFile(prefix="signed-payload-", 1135 suffix=".bin") 1136 cmd = ["brillo_update_payload", "sign", 1137 "--unsigned_payload", payload_file, 1138 "--payload", signed_payload_file, 1139 "--signature_size", "256", 1140 "--metadata_signature_file", signed_metadata_sig_file, 1141 "--payload_signature_file", signed_payload_sig_file] 1142 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT) 1143 p1.communicate() 1144 assert p1.returncode == 0, "brillo_update_payload sign failed" 1145 1146 # 4. Dump the signed payload properties. 1147 properties_file = common.MakeTempFile(prefix="payload-properties-", 1148 suffix=".txt") 1149 cmd = ["brillo_update_payload", "properties", 1150 "--payload", signed_payload_file, 1151 "--properties_file", properties_file] 1152 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT) 1153 p1.communicate() 1154 assert p1.returncode == 0, "brillo_update_payload properties failed" 1155 1156 if OPTIONS.wipe_user_data: 1157 with open(properties_file, "a") as f: 1158 f.write("POWERWASH=1\n") 1159 metadata["ota-wipe"] = "yes" 1160 1161 # Add the signed payload file and properties into the zip. In order to 1162 # support streaming, we pack payload.bin, payload_properties.txt and 1163 # care_map.txt as ZIP_STORED. So these entries can be read directly with 1164 # the offset and length pairs. 1165 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin", 1166 compress_type=zipfile.ZIP_STORED) 1167 common.ZipWrite(output_zip, properties_file, 1168 arcname="payload_properties.txt", 1169 compress_type=zipfile.ZIP_STORED) 1170 1171 # If dm-verity is supported for the device, copy contents of care_map 1172 # into A/B OTA package. 1173 target_zip = zipfile.ZipFile(target_file, "r") 1174 if OPTIONS.info_dict.get("verity") == "true": 1175 care_map_path = "META/care_map.txt" 1176 namelist = target_zip.namelist() 1177 if care_map_path in namelist: 1178 care_map_data = target_zip.read(care_map_path) 1179 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data, 1180 compress_type=zipfile.ZIP_STORED) 1181 else: 1182 print("Warning: cannot find care map file in target_file package") 1183 1184 if HasVendorPartition(target_zip): 1185 update_vendor = True 1186 update_system = True 1187 1188 # If incremental then figure out what is being updated so metadata only for 1189 # the updated image is included. 1190 if source_file is not None: 1191 input_tmp, input_zip = common.UnzipTemp( 1192 target_file, UNZIP_PATTERN) 1193 source_tmp, source_zip = common.UnzipTemp( 1194 source_file, UNZIP_PATTERN) 1195 1196 vendor_src = GetImage("vendor", source_tmp) 1197 vendor_tgt = GetImage("vendor", input_tmp) 1198 system_src = GetImage("system", source_tmp) 1199 system_tgt = GetImage("system", input_tmp) 1200 1201 update_system = system_src.TotalSha1() != system_tgt.TotalSha1() 1202 update_vendor = vendor_src.TotalSha1() != vendor_tgt.TotalSha1() 1203 1204 input_zip.close() 1205 source_zip.close() 1206 1207 target_zip = zipfile.ZipFile(target_file, "r") 1208 AddCompatibilityArchive(target_zip, output_zip, update_system, 1209 update_vendor) 1210 common.ZipClose(target_zip) 1211 1212 # Write the current metadata entry with placeholders. 1213 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata( 1214 output_zip, reserve_space=True) 1215 WriteMetadata(metadata, output_zip) 1216 common.ZipClose(output_zip) 1217 1218 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the 1219 # zip entries, as well as padding the entry headers. We do a preliminary 1220 # signing (with an incomplete metadata entry) to allow that to happen. Then 1221 # compute the zip entry offsets, write back the final metadata and do the 1222 # final signing. 1223 prelim_signing = tempfile.NamedTemporaryFile() 1224 SignOutput(temp_zip_file.name, prelim_signing.name) 1225 common.ZipClose(temp_zip_file) 1226 1227 # Open the signed zip. Compute the final metadata that's needed for streaming. 1228 prelim_zip = zipfile.ZipFile(prelim_signing, "r", 1229 compression=zipfile.ZIP_DEFLATED) 1230 expected_length = len(metadata['ota-streaming-property-files']) 1231 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata( 1232 prelim_zip, reserve_space=False, expected_length=expected_length) 1233 1234 # Copy the zip entries, as we cannot update / delete entries with zipfile. 1235 final_signing = tempfile.NamedTemporaryFile() 1236 output_zip = zipfile.ZipFile(final_signing, "w", 1237 compression=zipfile.ZIP_DEFLATED) 1238 for item in prelim_zip.infolist(): 1239 if item.filename == METADATA_NAME: 1240 continue 1241 1242 data = prelim_zip.read(item.filename) 1243 out_info = copy.copy(item) 1244 common.ZipWriteStr(output_zip, out_info, data) 1245 1246 # Now write the final metadata entry. 1247 WriteMetadata(metadata, output_zip) 1248 common.ZipClose(prelim_zip) 1249 common.ZipClose(output_zip) 1250 1251 # Re-sign the package after updating the metadata entry. 1252 SignOutput(final_signing.name, output_file) 1253 final_signing.close() 1254 1255 # Reopen the final signed zip to double check the streaming metadata. 1256 output_zip = zipfile.ZipFile(output_file, "r") 1257 actual = metadata['ota-streaming-property-files'].strip() 1258 expected = ComputeStreamingMetadata(output_zip) 1259 assert actual == expected, \ 1260 "Mismatching streaming metadata: %s vs %s." % (actual, expected) 1261 common.ZipClose(output_zip) 1262 1263 1264 def main(argv): 1265 1266 def option_handler(o, a): 1267 if o == "--board_config": 1268 pass # deprecated 1269 elif o in ("-k", "--package_key"): 1270 OPTIONS.package_key = a 1271 elif o in ("-i", "--incremental_from"): 1272 OPTIONS.incremental_source = a 1273 elif o == "--full_radio": 1274 OPTIONS.full_radio = True 1275 elif o == "--full_bootloader": 1276 OPTIONS.full_bootloader = True 1277 elif o in ("-w", "--wipe_user_data"): 1278 OPTIONS.wipe_user_data = True 1279 elif o == "--downgrade": 1280 OPTIONS.downgrade = True 1281 OPTIONS.wipe_user_data = True 1282 elif o == "--override_timestamp": 1283 OPTIONS.timestamp = True 1284 elif o in ("-o", "--oem_settings"): 1285 OPTIONS.oem_source = a.split(',') 1286 elif o == "--oem_no_mount": 1287 OPTIONS.oem_no_mount = True 1288 elif o in ("-e", "--extra_script"): 1289 OPTIONS.extra_script = a 1290 elif o in ("-t", "--worker_threads"): 1291 if a.isdigit(): 1292 OPTIONS.worker_threads = int(a) 1293 else: 1294 raise ValueError("Cannot parse value %r for option %r - only " 1295 "integers are allowed." % (a, o)) 1296 elif o in ("-2", "--two_step"): 1297 OPTIONS.two_step = True 1298 elif o == "--no_signing": 1299 OPTIONS.no_signing = True 1300 elif o == "--verify": 1301 OPTIONS.verify = True 1302 elif o == "--block": 1303 OPTIONS.block_based = True 1304 elif o in ("-b", "--binary"): 1305 OPTIONS.updater_binary = a 1306 elif o in ("--no_fallback_to_full",): 1307 OPTIONS.fallback_to_full = False 1308 elif o == "--stash_threshold": 1309 try: 1310 OPTIONS.stash_threshold = float(a) 1311 except ValueError: 1312 raise ValueError("Cannot parse value %r for option %r - expecting " 1313 "a float" % (a, o)) 1314 elif o == "--gen_verify": 1315 OPTIONS.gen_verify = True 1316 elif o == "--log_diff": 1317 OPTIONS.log_diff = a 1318 elif o == "--payload_signer": 1319 OPTIONS.payload_signer = a 1320 elif o == "--payload_signer_args": 1321 OPTIONS.payload_signer_args = shlex.split(a) 1322 elif o == "--extracted_input_target_files": 1323 OPTIONS.extracted_input = a 1324 else: 1325 return False 1326 return True 1327 1328 args = common.ParseOptions(argv, __doc__, 1329 extra_opts="b:k:i:d:we:t:2o:", 1330 extra_long_opts=[ 1331 "board_config=", 1332 "package_key=", 1333 "incremental_from=", 1334 "full_radio", 1335 "full_bootloader", 1336 "wipe_user_data", 1337 "downgrade", 1338 "override_timestamp", 1339 "extra_script=", 1340 "worker_threads=", 1341 "two_step", 1342 "no_signing", 1343 "block", 1344 "binary=", 1345 "oem_settings=", 1346 "oem_no_mount", 1347 "verify", 1348 "no_fallback_to_full", 1349 "stash_threshold=", 1350 "gen_verify", 1351 "log_diff=", 1352 "payload_signer=", 1353 "payload_signer_args=", 1354 "extracted_input_target_files=", 1355 ], extra_option_handler=option_handler) 1356 1357 if len(args) != 2: 1358 common.Usage(__doc__) 1359 sys.exit(1) 1360 1361 if OPTIONS.downgrade: 1362 # Sanity check to enforce a data wipe. 1363 if not OPTIONS.wipe_user_data: 1364 raise ValueError("Cannot downgrade without a data wipe") 1365 1366 # We should only allow downgrading incrementals (as opposed to full). 1367 # Otherwise the device may go back from arbitrary build with this full 1368 # OTA package. 1369 if OPTIONS.incremental_source is None: 1370 raise ValueError("Cannot generate downgradable full OTAs") 1371 1372 assert not (OPTIONS.downgrade and OPTIONS.timestamp), \ 1373 "Cannot have --downgrade AND --override_timestamp both" 1374 1375 # Load the dict file from the zip directly to have a peek at the OTA type. 1376 # For packages using A/B update, unzipping is not needed. 1377 if OPTIONS.extracted_input is not None: 1378 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input, OPTIONS.extracted_input) 1379 else: 1380 input_zip = zipfile.ZipFile(args[0], "r") 1381 OPTIONS.info_dict = common.LoadInfoDict(input_zip) 1382 common.ZipClose(input_zip) 1383 1384 ab_update = OPTIONS.info_dict.get("ab_update") == "true" 1385 1386 if ab_update: 1387 if OPTIONS.incremental_source is not None: 1388 OPTIONS.target_info_dict = OPTIONS.info_dict 1389 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r") 1390 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip) 1391 common.ZipClose(source_zip) 1392 1393 if OPTIONS.verbose: 1394 print("--- target info ---") 1395 common.DumpInfoDict(OPTIONS.info_dict) 1396 1397 if OPTIONS.incremental_source is not None: 1398 print("--- source info ---") 1399 common.DumpInfoDict(OPTIONS.source_info_dict) 1400 1401 WriteABOTAPackageWithBrilloScript( 1402 target_file=args[0], 1403 output_file=args[1], 1404 source_file=OPTIONS.incremental_source) 1405 1406 print("done.") 1407 return 1408 1409 if OPTIONS.extra_script is not None: 1410 OPTIONS.extra_script = open(OPTIONS.extra_script).read() 1411 1412 if OPTIONS.extracted_input is not None: 1413 OPTIONS.input_tmp = OPTIONS.extracted_input 1414 OPTIONS.target_tmp = OPTIONS.input_tmp 1415 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.input_tmp, OPTIONS.input_tmp) 1416 input_zip = zipfile.ZipFile(args[0], "r") 1417 else: 1418 print("unzipping target target-files...") 1419 OPTIONS.input_tmp, input_zip = common.UnzipTemp( 1420 args[0], UNZIP_PATTERN) 1421 1422 OPTIONS.target_tmp = OPTIONS.input_tmp 1423 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp) 1424 1425 if OPTIONS.verbose: 1426 print("--- target info ---") 1427 common.DumpInfoDict(OPTIONS.info_dict) 1428 1429 # If the caller explicitly specified the device-specific extensions 1430 # path via -s/--device_specific, use that. Otherwise, use 1431 # META/releasetools.py if it is present in the target target_files. 1432 # Otherwise, take the path of the file from 'tool_extensions' in the 1433 # info dict and look for that in the local filesystem, relative to 1434 # the current directory. 1435 1436 if OPTIONS.device_specific is None: 1437 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py") 1438 if os.path.exists(from_input): 1439 print("(using device-specific extensions from target_files)") 1440 OPTIONS.device_specific = from_input 1441 else: 1442 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None) 1443 1444 if OPTIONS.device_specific is not None: 1445 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific) 1446 1447 if OPTIONS.info_dict.get("no_recovery") == "true": 1448 raise common.ExternalError( 1449 "--- target build has specified no recovery ---") 1450 1451 # Use the default key to sign the package if not specified with package_key. 1452 if not OPTIONS.no_signing: 1453 if OPTIONS.package_key is None: 1454 OPTIONS.package_key = OPTIONS.info_dict.get( 1455 "default_system_dev_certificate", 1456 "build/target/product/security/testkey") 1457 1458 # Set up the output zip. Create a temporary zip file if signing is needed. 1459 if OPTIONS.no_signing: 1460 if os.path.exists(args[1]): 1461 os.unlink(args[1]) 1462 output_zip = zipfile.ZipFile(args[1], "w", 1463 compression=zipfile.ZIP_DEFLATED) 1464 else: 1465 temp_zip_file = tempfile.NamedTemporaryFile() 1466 output_zip = zipfile.ZipFile(temp_zip_file, "w", 1467 compression=zipfile.ZIP_DEFLATED) 1468 1469 # Non A/B OTAs rely on /cache partition to store temporary files. 1470 cache_size = OPTIONS.info_dict.get("cache_size", None) 1471 if cache_size is None: 1472 print("--- can't determine the cache partition size ---") 1473 OPTIONS.cache_size = cache_size 1474 1475 # Generate a verify package. 1476 if OPTIONS.gen_verify: 1477 WriteVerifyPackage(input_zip, output_zip) 1478 1479 # Generate a full OTA. 1480 elif OPTIONS.incremental_source is None: 1481 WriteFullOTAPackage(input_zip, output_zip) 1482 1483 # Generate an incremental OTA. It will fall back to generate a full OTA on 1484 # failure unless no_fallback_to_full is specified. 1485 else: 1486 print("unzipping source target-files...") 1487 OPTIONS.source_tmp, source_zip = common.UnzipTemp( 1488 OPTIONS.incremental_source, 1489 UNZIP_PATTERN) 1490 OPTIONS.target_info_dict = OPTIONS.info_dict 1491 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip, 1492 OPTIONS.source_tmp) 1493 if OPTIONS.verbose: 1494 print("--- source info ---") 1495 common.DumpInfoDict(OPTIONS.source_info_dict) 1496 try: 1497 WriteBlockIncrementalOTAPackage(input_zip, source_zip, output_zip) 1498 if OPTIONS.log_diff: 1499 out_file = open(OPTIONS.log_diff, 'w') 1500 import target_files_diff 1501 target_files_diff.recursiveDiff('', 1502 OPTIONS.source_tmp, 1503 OPTIONS.input_tmp, 1504 out_file) 1505 out_file.close() 1506 except ValueError: 1507 if not OPTIONS.fallback_to_full: 1508 raise 1509 print("--- failed to build incremental; falling back to full ---") 1510 OPTIONS.incremental_source = None 1511 WriteFullOTAPackage(input_zip, output_zip) 1512 1513 common.ZipClose(output_zip) 1514 1515 # Sign the generated zip package unless no_signing is specified. 1516 if not OPTIONS.no_signing: 1517 SignOutput(temp_zip_file.name, args[1]) 1518 temp_zip_file.close() 1519 1520 print("done.") 1521 1522 1523 if __name__ == '__main__': 1524 try: 1525 common.CloseInheritedPipes() 1526 main(sys.argv[1:]) 1527 except common.ExternalError as e: 1528 print("\n ERROR: %s\n" % (e,)) 1529 sys.exit(1) 1530 finally: 1531 common.Cleanup() 1532