blob: 667891c65e4c70193231654846b9707db250cd36 [file] [log] [blame]
Kelvin Zhangcff4d762020-07-29 16:37:51 -04001# 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
Abhishek Nigam1dfca462023-11-08 02:21:39 +000022import verity_utils
Kelvin Zhangcff4d762020-07-29 16:37:51 -040023from check_target_files_vintf import CheckVintfIfTrebleEnabled, HasPartition
Abhishek Nigam1dfca462023-11-08 02:21:39 +000024from common import OPTIONS
Kelvin Zhangcff4d762020-07-29 16:37:51 -040025from ota_utils import UNZIP_PATTERN, FinalizeMetadata, GetPackageMetadata, PropertyFiles
Abhishek Nigam1dfca462023-11-08 02:21:39 +000026import subprocess
Kelvin Zhangcff4d762020-07-29 16:37:51 -040027
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
Kelvin Zhangcff4d762020-07-29 16:37:51 -040044 partition_tgt = common.GetUserImage(name, OPTIONS.target_tmp, target_zip,
45 info_dict=target_info,
hungweichencc9c05d2022-08-23 05:45:42 +000046 allow_shared_blocks=allow_shared_blocks)
Kelvin Zhangcff4d762020-07-29 16:37:51 -040047
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"
Kelvin Zhange8ce3842023-01-20 10:21:29 -080052 # Disable imgdiff because it relies on zlib to produce stable output
53 # across different versions, which is often not the case.
Abhishek Nigam1dfca462023-11-08 02:21:39 +000054 return common.BlockDifference(name, partition_tgt, partition_src,
Abhishek Nigamb148ac22023-11-08 02:19:31 +000055 check_first_block,
56 version=blockimgdiff_version,
57 disable_imgdiff=True)
Kelvin Zhangcff4d762020-07-29 16:37:51 -040058
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",
Ramji Jiyani7ecb0ec2022-02-09 02:53:07 +000070 "vendor_dlkm", "odm_dlkm", "system_dlkm"]
Kelvin Zhangcff4d762020-07-29 16:37:51 -040071 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)
Abhishek Nigam1dfca462023-11-08 02:21:39 +000079 block_diff_dict[partition] = common.BlockDifference(partition, tgt,
Abhishek Nigamb148ac22023-11-08 02:19:31 +000080 src=None)
Kelvin Zhangcff4d762020-07-29 16:37:51 -040081 # 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:
Abhishek Nigam1dfca462023-11-08 02:21:39 +000098 assert all(isinstance(diff, common.BlockDifference)
Kelvin Zhangcff4d762020-07-29 16:37:51 -040099 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
Abhishek Nigam1dfca462023-11-08 02:21:39 +0000134 device_specific = common.DeviceSpecificParams(
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400135 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.
Abhishek Nigam1dfca462023-11-08 02:21:39 +0000220 dynamic_partitions_diff = common.DynamicPartitionsDifference(
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400221 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)
Tianjiea2076132020-08-19 17:25:32 -0700272 metadata.required_cache = script.required_cache
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400273
274 # We haven't written the metadata entry, which will be done in
275 # FinalizeMetadata.
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000276 common.ZipClose(output_zip)
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400277
278 needed_property_files = (
279 NonAbOtaPropertyFiles(),
280 )
Kelvin Zhangb0360072023-05-09 20:30:53 -0700281 FinalizeMetadata(metadata, staging_file, output_file,
282 needed_property_files, package_key=OPTIONS.package_key)
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400283
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
Abhishek Nigam1dfca462023-11-08 02:21:39 +0000312 device_specific = common.DeviceSpecificParams(
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400313 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:
Abhishek Nigam1dfca462023-11-08 02:21:39 +0000407 boot_type, boot_device_expr = common.GetTypeAndDeviceExpr("/boot",
Abhishek Nigamb148ac22023-11-08 02:19:31 +0000408 source_info)
Abhishek Nigam1dfca462023-11-08 02:21:39 +0000409 d = common.Difference(target_boot, source_boot, "bsdiff")
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400410 _, _, 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")
Abhishek Nigam1dfca462023-11-08 02:21:39 +0000464 dynamic_partitions_diff = common.DynamicPartitionsDifference(
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400465 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)
Tianjiea2076132020-08-19 17:25:32 -0700527 metadata.required_cache = script.required_cache
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400528
529 # We haven't written the metadata entry yet, which will be handled in
530 # FinalizeMetadata().
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000531 common.ZipClose(output_zip)
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400532
533 # Sign the generated zip package unless no_signing is specified.
534 needed_property_files = (
535 NonAbOtaPropertyFiles(),
536 )
Kelvin Zhangb0360072023-05-09 20:30:53 -0700537 FinalizeMetadata(metadata, staging_file, output_file,
538 needed_property_files, package_key=OPTIONS.package_key)
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400539
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:
Kelvin Zhangb0360072023-05-09 20:30:53 -0700561 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
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400573 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