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