1# Copyright (C) 2020 The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15import collections 16import logging 17import os 18import zipfile 19 20import common 21import edify_generator 22import verity_utils 23from check_target_files_vintf import CheckVintfIfTrebleEnabled, HasPartition 24from common import OPTIONS 25from ota_utils import UNZIP_PATTERN, FinalizeMetadata, GetPackageMetadata, PropertyFiles 26import subprocess 27 28logger = logging.getLogger(__name__) 29 30 31def GetBlockDifferences(target_zip, source_zip, target_info, source_info, 32 device_specific): 33 """Returns a ordered dict of block differences with partition name as key.""" 34 35 def GetIncrementalBlockDifferenceForPartition(name): 36 if not HasPartition(source_zip, name): 37 raise RuntimeError( 38 "can't generate incremental that adds {}".format(name)) 39 40 partition_src = common.GetUserImage(name, OPTIONS.source_tmp, source_zip, 41 info_dict=source_info, 42 allow_shared_blocks=allow_shared_blocks) 43 44 partition_tgt = common.GetUserImage(name, OPTIONS.target_tmp, target_zip, 45 info_dict=target_info, 46 allow_shared_blocks=allow_shared_blocks) 47 48 # Check the first block of the source system partition for remount R/W only 49 # if the filesystem is ext4. 50 partition_source_info = source_info["fstab"]["/" + name] 51 check_first_block = partition_source_info.fs_type == "ext4" 52 # Disable imgdiff because it relies on zlib to produce stable output 53 # across different versions, which is often not the case. 54 return common.BlockDifference(name, partition_tgt, partition_src, 55 check_first_block, 56 version=blockimgdiff_version, 57 disable_imgdiff=True) 58 59 if source_zip: 60 # See notes in common.GetUserImage() 61 allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or 62 target_info.get('ext4_share_dup_blocks') == "true") 63 blockimgdiff_version = max( 64 int(i) for i in target_info.get( 65 "blockimgdiff_versions", "1").split(",")) 66 assert blockimgdiff_version >= 3 67 68 block_diff_dict = collections.OrderedDict() 69 partition_names = ["system", "vendor", "product", "odm", "system_ext", 70 "vendor_dlkm", "odm_dlkm", "system_dlkm"] 71 for partition in partition_names: 72 if not HasPartition(target_zip, partition): 73 continue 74 # Full OTA update. 75 if not source_zip: 76 tgt = common.GetUserImage(partition, OPTIONS.input_tmp, target_zip, 77 info_dict=target_info, 78 reset_file_map=True) 79 block_diff_dict[partition] = common.BlockDifference(partition, tgt, 80 src=None) 81 # Incremental OTA update. 82 else: 83 block_diff_dict[partition] = GetIncrementalBlockDifferenceForPartition( 84 partition) 85 assert "system" in block_diff_dict 86 87 # Get the block diffs from the device specific script. If there is a 88 # duplicate block diff for a partition, ignore the diff in the generic script 89 # and use the one in the device specific script instead. 90 if source_zip: 91 device_specific_diffs = device_specific.IncrementalOTA_GetBlockDifferences() 92 function_name = "IncrementalOTA_GetBlockDifferences" 93 else: 94 device_specific_diffs = device_specific.FullOTA_GetBlockDifferences() 95 function_name = "FullOTA_GetBlockDifferences" 96 97 if device_specific_diffs: 98 assert all(isinstance(diff, common.BlockDifference) 99 for diff in device_specific_diffs), \ 100 "{} is not returning a list of BlockDifference objects".format( 101 function_name) 102 for diff in device_specific_diffs: 103 if diff.partition in block_diff_dict: 104 logger.warning("Duplicate block difference found. Device specific block" 105 " diff for partition '%s' overrides the one in generic" 106 " script.", diff.partition) 107 block_diff_dict[diff.partition] = diff 108 109 return block_diff_dict 110 111 112def WriteFullOTAPackage(input_zip, output_file): 113 target_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts) 114 115 # We don't know what version it will be installed on top of. We expect the API 116 # just won't change very often. Similarly for fstab, it might have changed in 117 # the target build. 118 target_api_version = target_info["recovery_api_version"] 119 script = edify_generator.EdifyGenerator(target_api_version, target_info) 120 121 if target_info.oem_props and not OPTIONS.oem_no_mount: 122 target_info.WriteMountOemScript(script) 123 124 metadata = GetPackageMetadata(target_info) 125 126 if not OPTIONS.no_signing: 127 staging_file = common.MakeTempFile(suffix='.zip') 128 else: 129 staging_file = output_file 130 131 output_zip = zipfile.ZipFile( 132 staging_file, "w", compression=zipfile.ZIP_DEFLATED) 133 134 device_specific = common.DeviceSpecificParams( 135 input_zip=input_zip, 136 input_version=target_api_version, 137 output_zip=output_zip, 138 script=script, 139 input_tmp=OPTIONS.input_tmp, 140 metadata=metadata, 141 info_dict=OPTIONS.info_dict) 142 143 assert HasRecoveryPatch(input_zip, info_dict=OPTIONS.info_dict) 144 145 # Assertions (e.g. downgrade check, device properties check). 146 ts = target_info.GetBuildProp("ro.build.date.utc") 147 ts_text = target_info.GetBuildProp("ro.build.date") 148 script.AssertOlderBuild(ts, ts_text) 149 150 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount) 151 device_specific.FullOTA_Assertions() 152 153 block_diff_dict = GetBlockDifferences(target_zip=input_zip, source_zip=None, 154 target_info=target_info, 155 source_info=None, 156 device_specific=device_specific) 157 158 # Two-step package strategy (in chronological order, which is *not* 159 # the order in which the generated script has things): 160 # 161 # if stage is not "2/3" or "3/3": 162 # write recovery image to boot partition 163 # set stage to "2/3" 164 # reboot to boot partition and restart recovery 165 # else if stage is "2/3": 166 # write recovery image to recovery partition 167 # set stage to "3/3" 168 # reboot to recovery partition and restart recovery 169 # else: 170 # (stage must be "3/3") 171 # set stage to "" 172 # do normal full package installation: 173 # wipe and install system, boot image, etc. 174 # set up system to update recovery partition on first boot 175 # complete script normally 176 # (allow recovery to mark itself finished and reboot) 177 178 recovery_img = common.GetBootableImage("recovery.img", "recovery.img", 179 OPTIONS.input_tmp, "RECOVERY") 180 if OPTIONS.two_step: 181 if not target_info.get("multistage_support"): 182 assert False, "two-step packages not supported by this build" 183 fs = target_info["fstab"]["/misc"] 184 assert fs.fs_type.upper() == "EMMC", \ 185 "two-step packages only supported on devices with EMMC /misc partitions" 186 bcb_dev = {"bcb_dev": fs.device} 187 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data) 188 script.AppendExtra(""" 189if get_stage("%(bcb_dev)s") == "2/3" then 190""" % bcb_dev) 191 192 # Stage 2/3: Write recovery image to /recovery (currently running /boot). 193 script.Comment("Stage 2/3") 194 script.WriteRawImage("/recovery", "recovery.img") 195 script.AppendExtra(""" 196set_stage("%(bcb_dev)s", "3/3"); 197reboot_now("%(bcb_dev)s", "recovery"); 198else if get_stage("%(bcb_dev)s") == "3/3" then 199""" % bcb_dev) 200 201 # Stage 3/3: Make changes. 202 script.Comment("Stage 3/3") 203 204 # Dump fingerprints 205 script.Print("Target: {}".format(target_info.fingerprint)) 206 207 device_specific.FullOTA_InstallBegin() 208 209 # All other partitions as well as the data wipe use 10% of the progress, and 210 # the update of the system partition takes the remaining progress. 211 system_progress = 0.9 - (len(block_diff_dict) - 1) * 0.1 212 if OPTIONS.wipe_user_data: 213 system_progress -= 0.1 214 progress_dict = {partition: 0.1 for partition in block_diff_dict} 215 progress_dict["system"] = system_progress 216 217 if target_info.get('use_dynamic_partitions') == "true": 218 # Use empty source_info_dict to indicate that all partitions / groups must 219 # be re-added. 220 dynamic_partitions_diff = common.DynamicPartitionsDifference( 221 info_dict=OPTIONS.info_dict, 222 block_diffs=block_diff_dict.values(), 223 progress_dict=progress_dict) 224 dynamic_partitions_diff.WriteScript(script, output_zip, 225 write_verify_script=OPTIONS.verify) 226 else: 227 for block_diff in block_diff_dict.values(): 228 block_diff.WriteScript(script, output_zip, 229 progress=progress_dict.get(block_diff.partition), 230 write_verify_script=OPTIONS.verify) 231 232 CheckVintfIfTrebleEnabled(OPTIONS.input_tmp, target_info) 233 234 boot_img = common.GetBootableImage( 235 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT") 236 common.CheckSize(boot_img.data, "boot.img", target_info) 237 common.ZipWriteStr(output_zip, "boot.img", boot_img.data) 238 239 script.WriteRawImage("/boot", "boot.img") 240 241 script.ShowProgress(0.1, 10) 242 device_specific.FullOTA_InstallEnd() 243 244 if OPTIONS.extra_script is not None: 245 script.AppendExtra(OPTIONS.extra_script) 246 247 script.UnmountAll() 248 249 if OPTIONS.wipe_user_data: 250 script.ShowProgress(0.1, 10) 251 script.FormatPartition("/data") 252 253 if OPTIONS.two_step: 254 script.AppendExtra(""" 255set_stage("%(bcb_dev)s", ""); 256""" % bcb_dev) 257 script.AppendExtra("else\n") 258 259 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot. 260 script.Comment("Stage 1/3") 261 _WriteRecoveryImageToBoot(script, output_zip) 262 263 script.AppendExtra(""" 264set_stage("%(bcb_dev)s", "2/3"); 265reboot_now("%(bcb_dev)s", ""); 266endif; 267endif; 268""" % bcb_dev) 269 270 script.SetProgress(1) 271 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary) 272 metadata.required_cache = script.required_cache 273 274 # We haven't written the metadata entry, which will be done in 275 # FinalizeMetadata. 276 common.ZipClose(output_zip) 277 278 needed_property_files = ( 279 NonAbOtaPropertyFiles(), 280 ) 281 FinalizeMetadata(metadata, staging_file, output_file, 282 needed_property_files, package_key=OPTIONS.package_key) 283 284 285def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file): 286 target_info = common.BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts) 287 source_info = common.BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts) 288 289 target_api_version = target_info["recovery_api_version"] 290 source_api_version = source_info["recovery_api_version"] 291 if source_api_version == 0: 292 logger.warning( 293 "Generating edify script for a source that can't install it.") 294 295 script = edify_generator.EdifyGenerator( 296 source_api_version, target_info, fstab=source_info["fstab"]) 297 298 if target_info.oem_props or source_info.oem_props: 299 if not OPTIONS.oem_no_mount: 300 source_info.WriteMountOemScript(script) 301 302 metadata = GetPackageMetadata(target_info, source_info) 303 304 if not OPTIONS.no_signing: 305 staging_file = common.MakeTempFile(suffix='.zip') 306 else: 307 staging_file = output_file 308 309 output_zip = zipfile.ZipFile( 310 staging_file, "w", compression=zipfile.ZIP_DEFLATED) 311 312 device_specific = common.DeviceSpecificParams( 313 source_zip=source_zip, 314 source_version=source_api_version, 315 source_tmp=OPTIONS.source_tmp, 316 target_zip=target_zip, 317 target_version=target_api_version, 318 target_tmp=OPTIONS.target_tmp, 319 output_zip=output_zip, 320 script=script, 321 metadata=metadata, 322 info_dict=source_info) 323 324 source_boot = common.GetBootableImage( 325 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info) 326 target_boot = common.GetBootableImage( 327 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info) 328 updating_boot = (not OPTIONS.two_step and 329 (source_boot.data != target_boot.data)) 330 331 target_recovery = common.GetBootableImage( 332 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY") 333 334 block_diff_dict = GetBlockDifferences(target_zip=target_zip, 335 source_zip=source_zip, 336 target_info=target_info, 337 source_info=source_info, 338 device_specific=device_specific) 339 340 CheckVintfIfTrebleEnabled(OPTIONS.target_tmp, target_info) 341 342 # Assertions (e.g. device properties check). 343 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount) 344 device_specific.IncrementalOTA_Assertions() 345 346 # Two-step incremental package strategy (in chronological order, 347 # which is *not* the order in which the generated script has 348 # things): 349 # 350 # if stage is not "2/3" or "3/3": 351 # do verification on current system 352 # write recovery image to boot partition 353 # set stage to "2/3" 354 # reboot to boot partition and restart recovery 355 # else if stage is "2/3": 356 # write recovery image to recovery partition 357 # set stage to "3/3" 358 # reboot to recovery partition and restart recovery 359 # else: 360 # (stage must be "3/3") 361 # perform update: 362 # patch system files, etc. 363 # force full install of new boot image 364 # set up system to update recovery partition on first boot 365 # complete script normally 366 # (allow recovery to mark itself finished and reboot) 367 368 if OPTIONS.two_step: 369 if not source_info.get("multistage_support"): 370 assert False, "two-step packages not supported by this build" 371 fs = source_info["fstab"]["/misc"] 372 assert fs.fs_type.upper() == "EMMC", \ 373 "two-step packages only supported on devices with EMMC /misc partitions" 374 bcb_dev = {"bcb_dev": fs.device} 375 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data) 376 script.AppendExtra(""" 377if get_stage("%(bcb_dev)s") == "2/3" then 378""" % bcb_dev) 379 380 # Stage 2/3: Write recovery image to /recovery (currently running /boot). 381 script.Comment("Stage 2/3") 382 script.AppendExtra("sleep(20);\n") 383 script.WriteRawImage("/recovery", "recovery.img") 384 script.AppendExtra(""" 385set_stage("%(bcb_dev)s", "3/3"); 386reboot_now("%(bcb_dev)s", "recovery"); 387else if get_stage("%(bcb_dev)s") != "3/3" then 388""" % bcb_dev) 389 390 # Stage 1/3: (a) Verify the current system. 391 script.Comment("Stage 1/3") 392 393 # Dump fingerprints 394 script.Print("Source: {}".format(source_info.fingerprint)) 395 script.Print("Target: {}".format(target_info.fingerprint)) 396 397 script.Print("Verifying current system...") 398 399 device_specific.IncrementalOTA_VerifyBegin() 400 401 WriteFingerprintAssertion(script, target_info, source_info) 402 403 # Check the required cache size (i.e. stashed blocks). 404 required_cache_sizes = [diff.required_cache for diff in 405 block_diff_dict.values()] 406 if updating_boot: 407 boot_type, boot_device_expr = common.GetTypeAndDeviceExpr("/boot", 408 source_info) 409 d = common.Difference(target_boot, source_boot, "bsdiff") 410 _, _, d = d.ComputePatch() 411 if d is None: 412 include_full_boot = True 413 common.ZipWriteStr(output_zip, "boot.img", target_boot.data) 414 else: 415 include_full_boot = False 416 417 logger.info( 418 "boot target: %d source: %d diff: %d", target_boot.size, 419 source_boot.size, len(d)) 420 421 common.ZipWriteStr(output_zip, "boot.img.p", d) 422 423 target_expr = 'concat("{}:",{},":{}:{}")'.format( 424 boot_type, boot_device_expr, target_boot.size, target_boot.sha1) 425 source_expr = 'concat("{}:",{},":{}:{}")'.format( 426 boot_type, boot_device_expr, source_boot.size, source_boot.sha1) 427 script.PatchPartitionExprCheck(target_expr, source_expr) 428 429 required_cache_sizes.append(target_boot.size) 430 431 if required_cache_sizes: 432 script.CacheFreeSpaceCheck(max(required_cache_sizes)) 433 434 # Verify the existing partitions. 435 for diff in block_diff_dict.values(): 436 diff.WriteVerifyScript(script, touched_blocks_only=True) 437 438 device_specific.IncrementalOTA_VerifyEnd() 439 440 if OPTIONS.two_step: 441 # Stage 1/3: (b) Write recovery image to /boot. 442 _WriteRecoveryImageToBoot(script, output_zip) 443 444 script.AppendExtra(""" 445set_stage("%(bcb_dev)s", "2/3"); 446reboot_now("%(bcb_dev)s", ""); 447else 448""" % bcb_dev) 449 450 # Stage 3/3: Make changes. 451 script.Comment("Stage 3/3") 452 453 script.Comment("---- start making changes here ----") 454 455 device_specific.IncrementalOTA_InstallBegin() 456 457 progress_dict = {partition: 0.1 for partition in block_diff_dict} 458 progress_dict["system"] = 1 - len(block_diff_dict) * 0.1 459 460 if OPTIONS.source_info_dict.get("use_dynamic_partitions") == "true": 461 if OPTIONS.target_info_dict.get("use_dynamic_partitions") != "true": 462 raise RuntimeError( 463 "can't generate incremental that disables dynamic partitions") 464 dynamic_partitions_diff = common.DynamicPartitionsDifference( 465 info_dict=OPTIONS.target_info_dict, 466 source_info_dict=OPTIONS.source_info_dict, 467 block_diffs=block_diff_dict.values(), 468 progress_dict=progress_dict) 469 dynamic_partitions_diff.WriteScript( 470 script, output_zip, write_verify_script=OPTIONS.verify) 471 else: 472 for block_diff in block_diff_dict.values(): 473 block_diff.WriteScript(script, output_zip, 474 progress=progress_dict.get(block_diff.partition), 475 write_verify_script=OPTIONS.verify) 476 477 if OPTIONS.two_step: 478 common.ZipWriteStr(output_zip, "boot.img", target_boot.data) 479 script.WriteRawImage("/boot", "boot.img") 480 logger.info("writing full boot image (forced by two-step mode)") 481 482 if not OPTIONS.two_step: 483 if updating_boot: 484 if include_full_boot: 485 logger.info("boot image changed; including full.") 486 script.Print("Installing boot image...") 487 script.WriteRawImage("/boot", "boot.img") 488 else: 489 # Produce the boot image by applying a patch to the current 490 # contents of the boot partition, and write it back to the 491 # partition. 492 logger.info("boot image changed; including patch.") 493 script.Print("Patching boot image...") 494 script.ShowProgress(0.1, 10) 495 target_expr = 'concat("{}:",{},":{}:{}")'.format( 496 boot_type, boot_device_expr, target_boot.size, target_boot.sha1) 497 source_expr = 'concat("{}:",{},":{}:{}")'.format( 498 boot_type, boot_device_expr, source_boot.size, source_boot.sha1) 499 script.PatchPartitionExpr(target_expr, source_expr, '"boot.img.p"') 500 else: 501 logger.info("boot image unchanged; skipping.") 502 503 # Do device-specific installation (eg, write radio image). 504 device_specific.IncrementalOTA_InstallEnd() 505 506 if OPTIONS.extra_script is not None: 507 script.AppendExtra(OPTIONS.extra_script) 508 509 if OPTIONS.wipe_user_data: 510 script.Print("Erasing user data...") 511 script.FormatPartition("/data") 512 513 if OPTIONS.two_step: 514 script.AppendExtra(""" 515set_stage("%(bcb_dev)s", ""); 516endif; 517endif; 518""" % bcb_dev) 519 520 script.SetProgress(1) 521 # For downgrade OTAs, we prefer to use the update-binary in the source 522 # build that is actually newer than the one in the target build. 523 if OPTIONS.downgrade: 524 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary) 525 else: 526 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary) 527 metadata.required_cache = script.required_cache 528 529 # We haven't written the metadata entry yet, which will be handled in 530 # FinalizeMetadata(). 531 common.ZipClose(output_zip) 532 533 # Sign the generated zip package unless no_signing is specified. 534 needed_property_files = ( 535 NonAbOtaPropertyFiles(), 536 ) 537 FinalizeMetadata(metadata, staging_file, output_file, 538 needed_property_files, package_key=OPTIONS.package_key) 539 540 541def GenerateNonAbOtaPackage(target_file, output_file, source_file=None): 542 """Generates a non-A/B OTA package.""" 543 # Check the loaded info dicts first. 544 if OPTIONS.info_dict.get("no_recovery") == "true": 545 raise common.ExternalError( 546 "--- target build has specified no recovery ---") 547 548 # Non-A/B OTAs rely on /cache partition to store temporary files. 549 cache_size = OPTIONS.info_dict.get("cache_size") 550 if cache_size is None: 551 logger.warning("--- can't determine the cache partition size ---") 552 OPTIONS.cache_size = cache_size 553 554 if OPTIONS.extra_script is not None: 555 with open(OPTIONS.extra_script) as fp: 556 OPTIONS.extra_script = fp.read() 557 558 if OPTIONS.extracted_input is not None: 559 OPTIONS.input_tmp = OPTIONS.extracted_input 560 else: 561 if not os.path.isdir(target_file): 562 logger.info("unzipping target target-files...") 563 OPTIONS.input_tmp = common.UnzipTemp(target_file, UNZIP_PATTERN) 564 else: 565 OPTIONS.input_tmp = target_file 566 tmpfile = common.MakeTempFile(suffix=".zip") 567 os.unlink(tmpfile) 568 common.RunAndCheckOutput( 569 ["zip", tmpfile, "-r", ".", "-0"], cwd=target_file) 570 assert zipfile.is_zipfile(tmpfile) 571 target_file = tmpfile 572 573 OPTIONS.target_tmp = OPTIONS.input_tmp 574 575 # If the caller explicitly specified the device-specific extensions path via 576 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it 577 # is present in the target target_files. Otherwise, take the path of the file 578 # from 'tool_extensions' in the info dict and look for that in the local 579 # filesystem, relative to the current directory. 580 if OPTIONS.device_specific is None: 581 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py") 582 if os.path.exists(from_input): 583 logger.info("(using device-specific extensions from target_files)") 584 OPTIONS.device_specific = from_input 585 else: 586 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions") 587 588 if OPTIONS.device_specific is not None: 589 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific) 590 591 # Generate a full OTA. 592 if source_file is None: 593 with zipfile.ZipFile(target_file) as input_zip: 594 WriteFullOTAPackage( 595 input_zip, 596 output_file) 597 598 # Generate an incremental OTA. 599 else: 600 logger.info("unzipping source target-files...") 601 OPTIONS.source_tmp = common.UnzipTemp( 602 OPTIONS.incremental_source, UNZIP_PATTERN) 603 with zipfile.ZipFile(target_file) as input_zip, \ 604 zipfile.ZipFile(source_file) as source_zip: 605 WriteBlockIncrementalOTAPackage( 606 input_zip, 607 source_zip, 608 output_file) 609 610 611def WriteFingerprintAssertion(script, target_info, source_info): 612 source_oem_props = source_info.oem_props 613 target_oem_props = target_info.oem_props 614 615 if source_oem_props is None and target_oem_props is None: 616 script.AssertSomeFingerprint( 617 source_info.fingerprint, target_info.fingerprint) 618 elif source_oem_props is not None and target_oem_props is not None: 619 script.AssertSomeThumbprint( 620 target_info.GetBuildProp("ro.build.thumbprint"), 621 source_info.GetBuildProp("ro.build.thumbprint")) 622 elif source_oem_props is None and target_oem_props is not None: 623 script.AssertFingerprintOrThumbprint( 624 source_info.fingerprint, 625 target_info.GetBuildProp("ro.build.thumbprint")) 626 else: 627 script.AssertFingerprintOrThumbprint( 628 target_info.fingerprint, 629 source_info.GetBuildProp("ro.build.thumbprint")) 630 631 632class NonAbOtaPropertyFiles(PropertyFiles): 633 """The property-files for non-A/B OTA. 634 635 For non-A/B OTA, the property-files string contains the info for METADATA 636 entry, with which a system updater can be fetched the package metadata prior 637 to downloading the entire package. 638 """ 639 640 def __init__(self): 641 super(NonAbOtaPropertyFiles, self).__init__() 642 self.name = 'ota-property-files' 643 644 645def _WriteRecoveryImageToBoot(script, output_zip): 646 """Find and write recovery image to /boot in two-step OTA. 647 648 In two-step OTAs, we write recovery image to /boot as the first step so that 649 we can reboot to there and install a new recovery image to /recovery. 650 A special "recovery-two-step.img" will be preferred, which encodes the correct 651 path of "/boot". Otherwise the device may show "device is corrupt" message 652 when booting into /boot. 653 654 Fall back to using the regular recovery.img if the two-step recovery image 655 doesn't exist. Note that rebuilding the special image at this point may be 656 infeasible, because we don't have the desired boot signer and keys when 657 calling ota_from_target_files.py. 658 """ 659 660 recovery_two_step_img_name = "recovery-two-step.img" 661 recovery_two_step_img_path = os.path.join( 662 OPTIONS.input_tmp, "OTA", recovery_two_step_img_name) 663 if os.path.exists(recovery_two_step_img_path): 664 common.ZipWrite( 665 output_zip, 666 recovery_two_step_img_path, 667 arcname=recovery_two_step_img_name) 668 logger.info( 669 "two-step package: using %s in stage 1/3", recovery_two_step_img_name) 670 script.WriteRawImage("/boot", recovery_two_step_img_name) 671 else: 672 logger.info("two-step package: using recovery.img in stage 1/3") 673 # The "recovery.img" entry has been written into package earlier. 674 script.WriteRawImage("/boot", "recovery.img") 675 676 677def HasRecoveryPatch(target_files_zip, info_dict): 678 board_uses_vendorimage = info_dict.get("board_uses_vendorimage") == "true" 679 680 if board_uses_vendorimage: 681 target_files_dir = "VENDOR" 682 else: 683 target_files_dir = "SYSTEM/vendor" 684 685 patch = "%s/recovery-from-boot.p" % target_files_dir 686 img = "%s/etc/recovery.img" % target_files_dir 687 688 namelist = target_files_zip.namelist() 689 return patch in namelist or img in namelist 690