blob: c4fd80944732907761e4bb80cd11f6aa58aeab3c [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
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
Kelvin Zhangcff4d762020-07-29 16:37:51 -040043 partition_tgt = common.GetUserImage(name, OPTIONS.target_tmp, target_zip,
44 info_dict=target_info,
hungweichencc9c05d2022-08-23 05:45:42 +000045 allow_shared_blocks=allow_shared_blocks)
Kelvin Zhangcff4d762020-07-29 16:37:51 -040046
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"
Kelvin Zhange8ce3842023-01-20 10:21:29 -080051 # Disable imgdiff because it relies on zlib to produce stable output
52 # across different versions, which is often not the case.
Kelvin Zhangcff4d762020-07-29 16:37:51 -040053 return common.BlockDifference(name, partition_tgt, partition_src,
54 check_first_block,
55 version=blockimgdiff_version,
Kelvin Zhange8ce3842023-01-20 10:21:29 -080056 disable_imgdiff=True)
Kelvin Zhangcff4d762020-07-29 16:37:51 -040057
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",
Ramji Jiyani7ecb0ec2022-02-09 02:53:07 +000069 "vendor_dlkm", "odm_dlkm", "system_dlkm"]
Kelvin Zhangcff4d762020-07-29 16:37:51 -040070 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)
Tianjiea2076132020-08-19 17:25:32 -0700271 metadata.required_cache = script.required_cache
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400272
273 # We haven't written the metadata entry, which will be done in
274 # FinalizeMetadata.
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000275 common.ZipClose(output_zip)
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400276
277 needed_property_files = (
278 NonAbOtaPropertyFiles(),
279 )
Kelvin Zhangf80e8862023-01-20 10:18:11 -0800280 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files, package_key=OPTIONS.package_key)
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400281
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)
Kelvin Zhange8ce3842023-01-20 10:21:29 -0800407 d = common.Difference(target_boot, source_boot, "bsdiff")
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400408 _, _, 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)
Tianjiea2076132020-08-19 17:25:32 -0700525 metadata.required_cache = script.required_cache
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400526
527 # We haven't written the metadata entry yet, which will be handled in
528 # FinalizeMetadata().
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000529 common.ZipClose(output_zip)
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400530
531 # Sign the generated zip package unless no_signing is specified.
532 needed_property_files = (
533 NonAbOtaPropertyFiles(),
534 )
Kelvin Zhangf80e8862023-01-20 10:18:11 -0800535 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files, package_key=OPTIONS.package_key)
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400536
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