xref: /aosp_15_r20/build/make/tools/releasetools/non_ab_ota.py (revision 9e94795a3d4ef5c1d47486f9a02bb378756cea8a)
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