blob: b75341410437d920aee8a47d9c363749a60ed5ca [file] [log] [blame]
Doug Zongkereef39442009-04-02 12:14:19 -07001#!/usr/bin/env python
2#
3# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""
Tao Bao30df8b42018-04-23 15:32:53 -070018Given a target-files zipfile, produces an OTA package that installs that build.
19An incremental OTA is produced if -i is given, otherwise a full OTA is produced.
Doug Zongkereef39442009-04-02 12:14:19 -070020
Tao Bao30df8b42018-04-23 15:32:53 -070021Usage: ota_from_target_files [options] input_target_files output_ota_package
Doug Zongkereef39442009-04-02 12:14:19 -070022
Tao Bao30df8b42018-04-23 15:32:53 -070023Common options that apply to both of non-A/B and A/B OTAs
24
25 --downgrade
26 Intentionally generate an incremental OTA that updates from a newer build
Tao Baofaa8e0b2018-04-12 14:31:43 -070027 to an older one (e.g. downgrading from P preview back to O MR1).
28 "ota-downgrade=yes" will be set in the package metadata file. A data wipe
29 will always be enforced when using this flag, so "ota-wipe=yes" will also
30 be included in the metadata file. The update-binary in the source build
31 will be used in the OTA package, unless --binary flag is specified. Please
32 also check the comment for --override_timestamp below.
Tao Bao30df8b42018-04-23 15:32:53 -070033
34 -i (--incremental_from) <file>
35 Generate an incremental OTA using the given target-files zip as the
36 starting build.
37
38 -k (--package_key) <key>
39 Key to use to sign the package (default is the value of
40 default_system_dev_certificate from the input target-files's
Tao Bao59cf0c52019-06-25 10:04:24 -070041 META/misc_info.txt, or "build/make/target/product/security/testkey" if
42 that value is not specified).
Doug Zongkerafb32ea2011-09-22 10:28:04 -070043
44 For incremental OTAs, the default value is based on the source
45 target-file, not the target build.
Doug Zongkereef39442009-04-02 12:14:19 -070046
Tao Bao30df8b42018-04-23 15:32:53 -070047 --override_timestamp
48 Intentionally generate an incremental OTA that updates from a newer build
Tao Baofaa8e0b2018-04-12 14:31:43 -070049 to an older one (based on timestamp comparison), by setting the downgrade
50 flag in the package metadata. This differs from --downgrade flag, as we
51 don't enforce a data wipe with this flag. Because we know for sure this is
52 NOT an actual downgrade case, but two builds happen to be cut in a reverse
53 order (e.g. from two branches). A legit use case is that we cut a new
54 build C (after having A and B), but want to enfore an update path of A ->
55 C -> B. Specifying --downgrade may not help since that would enforce a
56 data wipe for C -> B update.
57
58 We used to set a fake timestamp in the package metadata for this flow. But
59 now we consolidate the two cases (i.e. an actual downgrade, or a downgrade
60 based on timestamp) with the same "ota-downgrade=yes" flag, with the
61 difference being whether "ota-wipe=yes" is set.
Doug Zongkereef39442009-04-02 12:14:19 -070062
Tao Bao30df8b42018-04-23 15:32:53 -070063 --wipe_user_data
64 Generate an OTA package that will wipe the user data partition when
65 installed.
66
Yifan Hong50e79542018-11-08 17:44:12 -080067 --retrofit_dynamic_partitions
68 Generates an OTA package that updates a device to support dynamic
69 partitions (default False). This flag is implied when generating
70 an incremental OTA where the base build does not support dynamic
71 partitions but the target build does. For A/B, when this flag is set,
72 --skip_postinstall is implied.
73
xunchangabfa2652019-02-19 16:27:10 -080074 --skip_compatibility_check
Yifan Hong9276cf02019-08-21 16:37:04 -070075 Skip checking compatibility of the input target files package.
xunchangabfa2652019-02-19 16:27:10 -080076
xunchang1cfe2512019-02-19 14:14:48 -080077 --output_metadata_path
78 Write a copy of the metadata to a separate file. Therefore, users can
79 read the post build fingerprint without extracting the OTA package.
80
Yifan Hong65afc072020-04-17 10:08:10 -070081 --force_non_ab
82 This flag can only be set on an A/B device that also supports non-A/B
83 updates. Implies --two_step.
84 If set, generate that non-A/B update package.
85 If not set, generates A/B package for A/B device and non-A/B package for
86 non-A/B device.
87
Tao Bao30df8b42018-04-23 15:32:53 -070088Non-A/B OTA specific options
89
90 -b (--binary) <file>
91 Use the given binary as the update-binary in the output package, instead
92 of the binary in the build's target_files. Use for development only.
93
94 --block
95 Generate a block-based OTA for non-A/B device. We have deprecated the
96 support for file-based OTA since O. Block-based OTA will be used by
97 default for all non-A/B devices. Keeping this flag here to not break
98 existing callers.
99
100 -e (--extra_script) <file>
101 Insert the contents of file at the end of the update script.
Tao Bao43078aa2015-04-21 14:32:35 -0700102
leozwangaa6c1a12015-08-14 10:57:58 -0700103 --full_bootloader
104 Similar to --full_radio. When generating an incremental OTA, always
105 include a full copy of bootloader image.
106
Tao Bao30df8b42018-04-23 15:32:53 -0700107 --full_radio
108 When generating an incremental OTA, always include a full copy of radio
109 image. This option is only meaningful when -i is specified, because a full
110 radio is always included in a full OTA if applicable.
Michael Runge63f01de2014-10-28 19:24:19 -0700111
Tao Bao30df8b42018-04-23 15:32:53 -0700112 --log_diff <file>
113 Generate a log file that shows the differences in the source and target
114 builds for an incremental package. This option is only meaningful when -i
115 is specified.
116
117 -o (--oem_settings) <main_file[,additional_files...]>
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800118 Comma seperated list of files used to specify the expected OEM-specific
Tao Bao481bab82017-12-21 11:23:09 -0800119 properties on the OEM partition of the intended device. Multiple expected
120 values can be used by providing multiple files. Only the first dict will
121 be used to compute fingerprint, while the rest will be used to assert
122 OEM-specific properties.
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800123
Tao Bao8608cde2016-02-25 19:49:55 -0800124 --oem_no_mount
Tao Bao30df8b42018-04-23 15:32:53 -0700125 For devices with OEM-specific properties but without an OEM partition, do
126 not mount the OEM partition in the updater-script. This should be very
127 rarely used, since it's expected to have a dedicated OEM partition for
128 OEM-specific properties. Only meaningful when -o is specified.
Tao Bao8608cde2016-02-25 19:49:55 -0800129
Tao Bao30df8b42018-04-23 15:32:53 -0700130 --stash_threshold <float>
131 Specify the threshold that will be used to compute the maximum allowed
132 stash size (defaults to 0.8).
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700133
Tao Bao30df8b42018-04-23 15:32:53 -0700134 -t (--worker_threads) <int>
135 Specify the number of worker-threads that will be used when generating
136 patches for incremental updates (defaults to 3).
Tao Bao3e6161a2017-02-28 11:48:48 -0800137
Tao Bao30df8b42018-04-23 15:32:53 -0700138 --verify
139 Verify the checksums of the updated system and vendor (if any) partitions.
140 Non-A/B incremental OTAs only.
Doug Zongker1c390a22009-05-14 19:06:36 -0700141
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800142 -2 (--two_step)
Tao Bao30df8b42018-04-23 15:32:53 -0700143 Generate a 'two-step' OTA package, where recovery is updated first, so
144 that any changes made to the system partition are done using the new
145 recovery (new kernel, etc.).
146
147A/B OTA specific options
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800148
Tianjie Xu1b079832019-08-28 12:19:23 -0700149 --disable_fec_computation
150 Disable the on device FEC data computation for incremental updates.
151
Tao Baof7140c02018-01-30 17:09:24 -0800152 --include_secondary
153 Additionally include the payload for secondary slot images (default:
154 False). Only meaningful when generating A/B OTAs.
155
156 By default, an A/B OTA package doesn't contain the images for the
157 secondary slot (e.g. system_other.img). Specifying this flag allows
158 generating a separate payload that will install secondary slot images.
159
160 Such a package needs to be applied in a two-stage manner, with a reboot
161 in-between. During the first stage, the updater applies the primary
162 payload only. Upon finishing, it reboots the device into the newly updated
163 slot. It then continues to install the secondary payload to the inactive
164 slot, but without switching the active slot at the end (needs the matching
165 support in update_engine, i.e. SWITCH_SLOT_ON_REBOOT flag).
166
167 Due to the special install procedure, the secondary payload will be always
168 generated as a full payload.
169
Tao Baodea0f8b2016-06-20 17:55:06 -0700170 --payload_signer <signer>
171 Specify the signer when signing the payload and metadata for A/B OTAs.
172 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
173 with the package private key. If the private key cannot be accessed
174 directly, a payload signer that knows how to do that should be specified.
175 The signer will be supplied with "-inkey <path_to_key>",
176 "-in <input_file>" and "-out <output_file>" parameters.
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700177
178 --payload_signer_args <args>
179 Specify the arguments needed for payload signer.
Tao Bao15a146a2018-02-21 16:06:59 -0800180
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700181 --payload_signer_maximum_signature_size <signature_size>
182 The maximum signature size (in bytes) that would be generated by the given
183 payload signer. Only meaningful when custom payload signer is specified
184 via '--payload_signer'.
185 If the signer uses a RSA key, this should be the number of bytes to
186 represent the modulus. If it uses an EC key, this is the size of a
187 DER-encoded ECDSA signature.
188
xunchang376cc7c2019-04-08 23:04:58 -0700189 --payload_signer_key_size <key_size>
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700190 Deprecated. Use the '--payload_signer_maximum_signature_size' instead.
xunchang376cc7c2019-04-08 23:04:58 -0700191
Tianjied6867162020-05-10 14:30:13 -0700192 --boot_variable_file <path>
193 A file that contains the possible values of ro.boot.* properties. It's
194 used to calculate the possible runtime fingerprints when some
195 ro.product.* properties are overridden by the 'import' statement.
196 The file expects one property per line, and each line has the following
197 format: 'prop_name=value1,value2'. e.g. 'ro.boot.product.sku=std,pro'
198
Tao Bao15a146a2018-02-21 16:06:59 -0800199 --skip_postinstall
200 Skip the postinstall hooks when generating an A/B OTA package (default:
201 False). Note that this discards ALL the hooks, including non-optional
202 ones. Should only be used if caller knows it's safe to do so (e.g. all the
203 postinstall work is to dexopt apps and a data wipe will happen immediately
204 after). Only meaningful when generating A/B OTAs.
Doug Zongkereef39442009-04-02 12:14:19 -0700205"""
206
Tao Bao89fbb0f2017-01-10 10:47:58 -0800207from __future__ import print_function
208
Tianjie Xuf67dd802019-05-20 17:50:36 -0700209import collections
Tianjie Xu9afb2212020-05-10 21:48:15 +0000210import copy
211import itertools
Tao Bao32fcdab2018-10-12 10:30:39 -0700212import logging
Doug Zongkerfc44a512014-08-26 13:10:25 -0700213import multiprocessing
Tao Bao2dd1c482017-02-03 16:49:39 -0800214import os.path
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700215import shlex
Tao Bao15a146a2018-02-21 16:06:59 -0800216import shutil
Tao Bao85f16982018-03-08 16:28:33 -0800217import struct
Tao Bao481bab82017-12-21 11:23:09 -0800218import sys
Doug Zongkereef39442009-04-02 12:14:19 -0700219import zipfile
220
Yifan Hong9276cf02019-08-21 16:37:04 -0700221import check_target_files_vintf
Doug Zongkereef39442009-04-02 12:14:19 -0700222import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700223import edify_generator
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700224import verity_utils
Doug Zongkereef39442009-04-02 12:14:19 -0700225
Tao Bao481bab82017-12-21 11:23:09 -0800226if sys.hexversion < 0x02070000:
227 print("Python 2.7 or newer is required.", file=sys.stderr)
228 sys.exit(1)
229
Tao Bao32fcdab2018-10-12 10:30:39 -0700230logger = logging.getLogger(__name__)
Tao Bao481bab82017-12-21 11:23:09 -0800231
Doug Zongkereef39442009-04-02 12:14:19 -0700232OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700233OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700234OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700235OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700236OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700237OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800238OPTIONS.downgrade = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700239OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700240OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
241if OPTIONS.worker_threads == 0:
242 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800243OPTIONS.two_step = False
Tao Baof7140c02018-01-30 17:09:24 -0800244OPTIONS.include_secondary = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900245OPTIONS.no_signing = False
Tao Bao457cbf62017-03-06 09:56:01 -0800246OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -0800247OPTIONS.updater_binary = None
Tianjie Xu9afb2212020-05-10 21:48:15 +0000248OPTIONS.oem_dicts = None
Michael Runge6e836112014-04-15 17:40:21 -0700249OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800250OPTIONS.oem_no_mount = False
Tao Bao43078aa2015-04-21 14:32:35 -0700251OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700252OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700253# Stash size cannot exceed cache_size * threshold.
254OPTIONS.cache_size = None
255OPTIONS.stash_threshold = 0.8
Tao Baod62c6032015-11-30 09:40:20 -0800256OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700257OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700258OPTIONS.payload_signer_args = []
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700259OPTIONS.payload_signer_maximum_signature_size = None
Tao Bao5f8ff932017-03-21 22:35:00 -0700260OPTIONS.extracted_input = None
Christian Oderf63e2cd2017-05-01 22:30:15 +0200261OPTIONS.key_passwords = []
Tao Bao15a146a2018-02-21 16:06:59 -0800262OPTIONS.skip_postinstall = False
Yifan Hong50e79542018-11-08 17:44:12 -0800263OPTIONS.retrofit_dynamic_partitions = False
xunchangabfa2652019-02-19 16:27:10 -0800264OPTIONS.skip_compatibility_check = False
xunchang1cfe2512019-02-19 14:14:48 -0800265OPTIONS.output_metadata_path = None
Tianjie Xu1b079832019-08-28 12:19:23 -0700266OPTIONS.disable_fec_computation = False
Yifan Hong65afc072020-04-17 10:08:10 -0700267OPTIONS.force_non_ab = False
Tianjied6867162020-05-10 14:30:13 -0700268OPTIONS.boot_variable_file = None
Tao Bao15a146a2018-02-21 16:06:59 -0800269
Tao Bao8dcf7382015-05-21 14:09:49 -0700270
Tao Bao2dd1c482017-02-03 16:49:39 -0800271METADATA_NAME = 'META-INF/com/android/metadata'
Tao Bao15a146a2018-02-21 16:06:59 -0800272POSTINSTALL_CONFIG = 'META/postinstall_config.txt'
Yifan Hong50e79542018-11-08 17:44:12 -0800273DYNAMIC_PARTITION_INFO = 'META/dynamic_partitions_info.txt'
Yifan Hongb433eba2019-03-06 12:42:53 -0800274AB_PARTITIONS = 'META/ab_partitions.txt'
Tao Bao04808502019-07-25 23:11:41 -0700275UNZIP_PATTERN = ['IMAGES/*', 'META/*', 'OTA/*', 'RADIO/*']
Tao Baof0c4aa22018-04-30 20:29:30 -0700276# Files to be unzipped for target diffing purpose.
277TARGET_DIFFING_UNZIP_PATTERN = ['BOOT', 'RECOVERY', 'SYSTEM/*', 'VENDOR/*',
278 'PRODUCT/*', 'SYSTEM_EXT/*', 'ODM/*']
Yifan Hongb433eba2019-03-06 12:42:53 -0800279RETROFIT_DAP_UNZIP_PATTERN = ['OTA/super_*.img', AB_PARTITIONS]
Tao Bao3e759462019-09-17 22:43:11 -0700280
281# Images to be excluded from secondary payload. We essentially only keep
282# 'system_other' and bootloader partitions.
283SECONDARY_PAYLOAD_SKIPPED_IMAGES = [
284 'boot', 'dtbo', 'modem', 'odm', 'product', 'radio', 'recovery',
Tianjiec3850642020-05-13 14:47:31 -0700285 'system_ext', 'vbmeta', 'vbmeta_system', 'vbmeta_vendor', 'vendor',
286 'vendor_boot']
Tao Bao6b0b2f92017-03-05 11:38:11 -0800287
Tao Bao2dd1c482017-02-03 16:49:39 -0800288
Tao Baofabe0832018-01-17 15:52:28 -0800289class PayloadSigner(object):
290 """A class that wraps the payload signing works.
291
292 When generating a Payload, hashes of the payload and metadata files will be
293 signed with the device key, either by calling an external payload signer or
294 by calling openssl with the package key. This class provides a unified
295 interface, so that callers can just call PayloadSigner.Sign().
296
297 If an external payload signer has been specified (OPTIONS.payload_signer), it
298 calls the signer with the provided args (OPTIONS.payload_signer_args). Note
299 that the signing key should be provided as part of the payload_signer_args.
300 Otherwise without an external signer, it uses the package key
301 (OPTIONS.package_key) and calls openssl for the signing works.
302 """
303
304 def __init__(self):
305 if OPTIONS.payload_signer is None:
306 # Prepare the payload signing key.
307 private_key = OPTIONS.package_key + OPTIONS.private_key_suffix
308 pw = OPTIONS.key_passwords[OPTIONS.package_key]
309
310 cmd = ["openssl", "pkcs8", "-in", private_key, "-inform", "DER"]
311 cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"])
312 signing_key = common.MakeTempFile(prefix="key-", suffix=".key")
313 cmd.extend(["-out", signing_key])
Tao Baobec89c12018-10-15 11:53:28 -0700314 common.RunAndCheckOutput(cmd, verbose=False)
Tao Baofabe0832018-01-17 15:52:28 -0800315
316 self.signer = "openssl"
317 self.signer_args = ["pkeyutl", "-sign", "-inkey", signing_key,
318 "-pkeyopt", "digest:sha256"]
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700319 self.maximum_signature_size = self._GetMaximumSignatureSizeInBytes(
320 signing_key)
Tao Baofabe0832018-01-17 15:52:28 -0800321 else:
322 self.signer = OPTIONS.payload_signer
323 self.signer_args = OPTIONS.payload_signer_args
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700324 if OPTIONS.payload_signer_maximum_signature_size:
325 self.maximum_signature_size = int(
326 OPTIONS.payload_signer_maximum_signature_size)
xunchang376cc7c2019-04-08 23:04:58 -0700327 else:
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700328 # The legacy config uses RSA2048 keys.
329 logger.warning("The maximum signature size for payload signer is not"
330 " set, default to 256 bytes.")
331 self.maximum_signature_size = 256
xunchang376cc7c2019-04-08 23:04:58 -0700332
333 @staticmethod
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700334 def _GetMaximumSignatureSizeInBytes(signing_key):
335 out_signature_size_file = common.MakeTempFile("signature_size")
336 cmd = ["delta_generator", "--out_maximum_signature_size_file={}".format(
337 out_signature_size_file), "--private_key={}".format(signing_key)]
338 common.RunAndCheckOutput(cmd)
339 with open(out_signature_size_file) as f:
340 signature_size = f.read().rstrip()
Luca Stefani88e1a142020-03-27 14:05:12 +0100341 logger.info("%s outputs the maximum signature size: %s", cmd[0],
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700342 signature_size)
343 return int(signature_size)
Tao Baofabe0832018-01-17 15:52:28 -0800344
345 def Sign(self, in_file):
346 """Signs the given input file. Returns the output filename."""
347 out_file = common.MakeTempFile(prefix="signed-", suffix=".bin")
348 cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file]
Tao Bao718faed2019-08-02 13:24:19 -0700349 common.RunAndCheckOutput(cmd)
Tao Baofabe0832018-01-17 15:52:28 -0800350 return out_file
351
352
Tao Bao40b18822018-01-30 18:19:04 -0800353class Payload(object):
354 """Manages the creation and the signing of an A/B OTA Payload."""
355
356 PAYLOAD_BIN = 'payload.bin'
357 PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
Tao Baof7140c02018-01-30 17:09:24 -0800358 SECONDARY_PAYLOAD_BIN = 'secondary/payload.bin'
359 SECONDARY_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt'
Tao Bao40b18822018-01-30 18:19:04 -0800360
Tao Bao667ff572018-02-10 00:02:40 -0800361 def __init__(self, secondary=False):
362 """Initializes a Payload instance.
363
364 Args:
365 secondary: Whether it's generating a secondary payload (default: False).
366 """
Tao Bao40b18822018-01-30 18:19:04 -0800367 self.payload_file = None
368 self.payload_properties = None
Tao Bao667ff572018-02-10 00:02:40 -0800369 self.secondary = secondary
Tao Bao40b18822018-01-30 18:19:04 -0800370
Tao Baof0c4aa22018-04-30 20:29:30 -0700371 def _Run(self, cmd): # pylint: disable=no-self-use
Tao Bao718faed2019-08-02 13:24:19 -0700372 # Don't pipe (buffer) the output if verbose is set. Let
373 # brillo_update_payload write to stdout/stderr directly, so its progress can
374 # be monitored.
375 if OPTIONS.verbose:
376 common.RunAndCheckOutput(cmd, stdout=None, stderr=None)
377 else:
378 common.RunAndCheckOutput(cmd)
379
Tao Bao40b18822018-01-30 18:19:04 -0800380 def Generate(self, target_file, source_file=None, additional_args=None):
381 """Generates a payload from the given target-files zip(s).
382
383 Args:
384 target_file: The filename of the target build target-files zip.
385 source_file: The filename of the source build target-files zip; or None if
386 generating a full OTA.
387 additional_args: A list of additional args that should be passed to
388 brillo_update_payload script; or None.
389 """
390 if additional_args is None:
391 additional_args = []
392
393 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
394 cmd = ["brillo_update_payload", "generate",
395 "--payload", payload_file,
396 "--target_image", target_file]
397 if source_file is not None:
398 cmd.extend(["--source_image", source_file])
Tianjie Xu1b079832019-08-28 12:19:23 -0700399 if OPTIONS.disable_fec_computation:
400 cmd.extend(["--disable_fec_computation", "true"])
Tao Bao40b18822018-01-30 18:19:04 -0800401 cmd.extend(additional_args)
Tao Bao718faed2019-08-02 13:24:19 -0700402 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800403
404 self.payload_file = payload_file
405 self.payload_properties = None
406
407 def Sign(self, payload_signer):
408 """Generates and signs the hashes of the payload and metadata.
409
410 Args:
411 payload_signer: A PayloadSigner() instance that serves the signing work.
412
413 Raises:
414 AssertionError: On any failure when calling brillo_update_payload script.
415 """
416 assert isinstance(payload_signer, PayloadSigner)
417
418 # 1. Generate hashes of the payload and metadata files.
419 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
420 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
421 cmd = ["brillo_update_payload", "hash",
422 "--unsigned_payload", self.payload_file,
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700423 "--signature_size", str(payload_signer.maximum_signature_size),
Tao Bao40b18822018-01-30 18:19:04 -0800424 "--metadata_hash_file", metadata_sig_file,
425 "--payload_hash_file", payload_sig_file]
Tao Bao718faed2019-08-02 13:24:19 -0700426 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800427
428 # 2. Sign the hashes.
429 signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
430 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
431
432 # 3. Insert the signatures back into the payload file.
433 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
434 suffix=".bin")
435 cmd = ["brillo_update_payload", "sign",
436 "--unsigned_payload", self.payload_file,
437 "--payload", signed_payload_file,
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700438 "--signature_size", str(payload_signer.maximum_signature_size),
Tao Bao40b18822018-01-30 18:19:04 -0800439 "--metadata_signature_file", signed_metadata_sig_file,
440 "--payload_signature_file", signed_payload_sig_file]
Tao Bao718faed2019-08-02 13:24:19 -0700441 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800442
443 # 4. Dump the signed payload properties.
444 properties_file = common.MakeTempFile(prefix="payload-properties-",
445 suffix=".txt")
446 cmd = ["brillo_update_payload", "properties",
447 "--payload", signed_payload_file,
448 "--properties_file", properties_file]
Tao Bao718faed2019-08-02 13:24:19 -0700449 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800450
Tao Bao667ff572018-02-10 00:02:40 -0800451 if self.secondary:
452 with open(properties_file, "a") as f:
453 f.write("SWITCH_SLOT_ON_REBOOT=0\n")
454
Tao Bao40b18822018-01-30 18:19:04 -0800455 if OPTIONS.wipe_user_data:
456 with open(properties_file, "a") as f:
457 f.write("POWERWASH=1\n")
458
459 self.payload_file = signed_payload_file
460 self.payload_properties = properties_file
461
Tao Bao667ff572018-02-10 00:02:40 -0800462 def WriteToZip(self, output_zip):
Tao Bao40b18822018-01-30 18:19:04 -0800463 """Writes the payload to the given zip.
464
465 Args:
466 output_zip: The output ZipFile instance.
467 """
468 assert self.payload_file is not None
469 assert self.payload_properties is not None
470
Tao Bao667ff572018-02-10 00:02:40 -0800471 if self.secondary:
Tao Baof7140c02018-01-30 17:09:24 -0800472 payload_arcname = Payload.SECONDARY_PAYLOAD_BIN
473 payload_properties_arcname = Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT
474 else:
475 payload_arcname = Payload.PAYLOAD_BIN
476 payload_properties_arcname = Payload.PAYLOAD_PROPERTIES_TXT
477
Tao Bao40b18822018-01-30 18:19:04 -0800478 # Add the signed payload file and properties into the zip. In order to
479 # support streaming, we pack them as ZIP_STORED. So these entries can be
480 # read directly with the offset and length pairs.
Tao Baof7140c02018-01-30 17:09:24 -0800481 common.ZipWrite(output_zip, self.payload_file, arcname=payload_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800482 compress_type=zipfile.ZIP_STORED)
483 common.ZipWrite(output_zip, self.payload_properties,
Tao Baof7140c02018-01-30 17:09:24 -0800484 arcname=payload_properties_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800485 compress_type=zipfile.ZIP_STORED)
486
487
Doug Zongkereef39442009-04-02 12:14:19 -0700488def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200489 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700490
Doug Zongker951495f2009-08-14 12:44:19 -0700491 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
492 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700493
494
Tao Bao481bab82017-12-21 11:23:09 -0800495def _LoadOemDicts(oem_source):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800496 """Returns the list of loaded OEM properties dict."""
Tao Bao481bab82017-12-21 11:23:09 -0800497 if not oem_source:
498 return None
499
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800500 oem_dicts = []
Tao Bao481bab82017-12-21 11:23:09 -0800501 for oem_file in oem_source:
502 with open(oem_file) as fp:
503 oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines()))
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800504 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700505
Doug Zongkereef39442009-04-02 12:14:19 -0700506
Tao Baod42e97e2016-11-30 12:11:57 -0800507def _WriteRecoveryImageToBoot(script, output_zip):
508 """Find and write recovery image to /boot in two-step OTA.
509
510 In two-step OTAs, we write recovery image to /boot as the first step so that
511 we can reboot to there and install a new recovery image to /recovery.
512 A special "recovery-two-step.img" will be preferred, which encodes the correct
513 path of "/boot". Otherwise the device may show "device is corrupt" message
514 when booting into /boot.
515
516 Fall back to using the regular recovery.img if the two-step recovery image
517 doesn't exist. Note that rebuilding the special image at this point may be
518 infeasible, because we don't have the desired boot signer and keys when
519 calling ota_from_target_files.py.
520 """
521
522 recovery_two_step_img_name = "recovery-two-step.img"
523 recovery_two_step_img_path = os.path.join(
Tao Bao04808502019-07-25 23:11:41 -0700524 OPTIONS.input_tmp, "OTA", recovery_two_step_img_name)
Tao Baod42e97e2016-11-30 12:11:57 -0800525 if os.path.exists(recovery_two_step_img_path):
Tao Bao04808502019-07-25 23:11:41 -0700526 common.ZipWrite(
527 output_zip,
528 recovery_two_step_img_path,
529 arcname=recovery_two_step_img_name)
Tao Bao32fcdab2018-10-12 10:30:39 -0700530 logger.info(
531 "two-step package: using %s in stage 1/3", recovery_two_step_img_name)
Tao Baod42e97e2016-11-30 12:11:57 -0800532 script.WriteRawImage("/boot", recovery_two_step_img_name)
533 else:
Tao Bao32fcdab2018-10-12 10:30:39 -0700534 logger.info("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800535 # The "recovery.img" entry has been written into package earlier.
536 script.WriteRawImage("/boot", "recovery.img")
537
538
Bill Peckhame868aec2019-09-17 17:06:47 -0700539def HasRecoveryPatch(target_files_zip, info_dict):
540 board_uses_vendorimage = info_dict.get("board_uses_vendorimage") == "true"
541
542 if board_uses_vendorimage:
543 target_files_dir = "VENDOR"
544 else:
545 target_files_dir = "SYSTEM/vendor"
546
547 patch = "%s/recovery-from-boot.p" % target_files_dir
548 img = "%s/etc/recovery.img" %target_files_dir
549
Tao Baof2cffbd2015-07-22 12:33:18 -0700550 namelist = [name for name in target_files_zip.namelist()]
Bill Peckhame868aec2019-09-17 17:06:47 -0700551 return (patch in namelist or img in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700552
Tao Bao457cbf62017-03-06 09:56:01 -0800553
Yifan Hong51d37562019-04-23 17:06:46 -0700554def HasPartition(target_files_zip, partition):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700555 try:
Yifan Hong51d37562019-04-23 17:06:46 -0700556 target_files_zip.getinfo(partition.upper() + "/")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700557 return True
558 except KeyError:
559 return False
560
Tao Bao457cbf62017-03-06 09:56:01 -0800561
Yifan Hong9276cf02019-08-21 16:37:04 -0700562def HasTrebleEnabled(target_files, target_info):
563 def HasVendorPartition(target_files):
564 if os.path.isdir(target_files):
565 return os.path.isdir(os.path.join(target_files, "VENDOR"))
566 if zipfile.is_zipfile(target_files):
567 return HasPartition(zipfile.ZipFile(target_files), "vendor")
568 raise ValueError("Unknown target_files argument")
Yifan Hong51d37562019-04-23 17:06:46 -0700569
Yifan Hong9276cf02019-08-21 16:37:04 -0700570 return (HasVendorPartition(target_files) and
Tao Bao481bab82017-12-21 11:23:09 -0800571 target_info.GetBuildProp("ro.treble.enabled") == "true")
Tao Baobcd1d162017-08-26 13:10:26 -0700572
573
Tao Bao481bab82017-12-21 11:23:09 -0800574def WriteFingerprintAssertion(script, target_info, source_info):
575 source_oem_props = source_info.oem_props
576 target_oem_props = target_info.oem_props
Michael Runge6e836112014-04-15 17:40:21 -0700577
Tao Bao481bab82017-12-21 11:23:09 -0800578 if source_oem_props is None and target_oem_props is None:
579 script.AssertSomeFingerprint(
580 source_info.fingerprint, target_info.fingerprint)
581 elif source_oem_props is not None and target_oem_props is not None:
582 script.AssertSomeThumbprint(
583 target_info.GetBuildProp("ro.build.thumbprint"),
584 source_info.GetBuildProp("ro.build.thumbprint"))
585 elif source_oem_props is None and target_oem_props is not None:
586 script.AssertFingerprintOrThumbprint(
587 source_info.fingerprint,
588 target_info.GetBuildProp("ro.build.thumbprint"))
589 else:
590 script.AssertFingerprintOrThumbprint(
591 target_info.fingerprint,
592 source_info.GetBuildProp("ro.build.thumbprint"))
Doug Zongker73ef8252009-07-23 15:12:53 -0700593
Doug Zongkerfc44a512014-08-26 13:10:25 -0700594
Yifan Hong9276cf02019-08-21 16:37:04 -0700595def CheckVintfIfTrebleEnabled(target_files, target_info):
596 """Checks compatibility info of the input target files.
Tao Bao21803d32017-04-19 10:16:09 -0700597
Yifan Hong9276cf02019-08-21 16:37:04 -0700598 Metadata used for compatibility verification is retrieved from target_zip.
Tao Bao21803d32017-04-19 10:16:09 -0700599
Yifan Hong9276cf02019-08-21 16:37:04 -0700600 Compatibility should only be checked for devices that have enabled
Tao Baobcd1d162017-08-26 13:10:26 -0700601 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700602
603 Args:
Yifan Hong9276cf02019-08-21 16:37:04 -0700604 target_files: Path to zip file containing the source files to be included
605 for OTA. Can also be the path to extracted directory.
Tao Bao481bab82017-12-21 11:23:09 -0800606 target_info: The BuildInfo instance that holds the target build info.
Tao Bao21803d32017-04-19 10:16:09 -0700607 """
608
Tao Baobcd1d162017-08-26 13:10:26 -0700609 # Will only proceed if the target has enabled the Treble support (as well as
610 # having a /vendor partition).
Yifan Hong9276cf02019-08-21 16:37:04 -0700611 if not HasTrebleEnabled(target_files, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700612 return
613
xunchangabfa2652019-02-19 16:27:10 -0800614 # Skip adding the compatibility package as a workaround for b/114240221. The
615 # compatibility will always fail on devices without qualified kernels.
616 if OPTIONS.skip_compatibility_check:
617 return
618
Yifan Hong9276cf02019-08-21 16:37:04 -0700619 if not check_target_files_vintf.CheckVintf(target_files, target_info):
620 raise RuntimeError("VINTF compatibility check failed")
Tao Bao21803d32017-04-19 10:16:09 -0700621
622
Tianjie Xuf67dd802019-05-20 17:50:36 -0700623def GetBlockDifferences(target_zip, source_zip, target_info, source_info,
624 device_specific):
625 """Returns a ordered dict of block differences with partition name as key."""
626
627 def GetIncrementalBlockDifferenceForPartition(name):
628 if not HasPartition(source_zip, name):
629 raise RuntimeError("can't generate incremental that adds {}".format(name))
630
631 partition_src = common.GetUserImage(name, OPTIONS.source_tmp, source_zip,
632 info_dict=source_info,
633 allow_shared_blocks=allow_shared_blocks)
634
635 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
636 name, 4096, target_info)
637 partition_tgt = common.GetUserImage(name, OPTIONS.target_tmp, target_zip,
638 info_dict=target_info,
639 allow_shared_blocks=allow_shared_blocks,
640 hashtree_info_generator=
641 hashtree_info_generator)
642
643 # Check the first block of the source system partition for remount R/W only
644 # if the filesystem is ext4.
645 partition_source_info = source_info["fstab"]["/" + name]
646 check_first_block = partition_source_info.fs_type == "ext4"
647 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
648 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
649 # b) the blocks listed in block map may not contain all the bytes for a
650 # given file (because they're rounded to be 4K-aligned).
651 partition_target_info = target_info["fstab"]["/" + name]
652 disable_imgdiff = (partition_source_info.fs_type == "squashfs" or
653 partition_target_info.fs_type == "squashfs")
Xindong Xu2a7aaa62020-03-13 15:59:22 +0800654 return common.BlockDifference(name, partition_tgt, partition_src,
Tianjie Xuf67dd802019-05-20 17:50:36 -0700655 check_first_block,
656 version=blockimgdiff_version,
657 disable_imgdiff=disable_imgdiff)
658
659 if source_zip:
660 # See notes in common.GetUserImage()
661 allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or
662 target_info.get('ext4_share_dup_blocks') == "true")
663 blockimgdiff_version = max(
664 int(i) for i in target_info.get(
665 "blockimgdiff_versions", "1").split(","))
666 assert blockimgdiff_version >= 3
667
668 block_diff_dict = collections.OrderedDict()
669 partition_names = ["system", "vendor", "product", "odm", "system_ext"]
670 for partition in partition_names:
671 if not HasPartition(target_zip, partition):
672 continue
673 # Full OTA update.
674 if not source_zip:
675 tgt = common.GetUserImage(partition, OPTIONS.input_tmp, target_zip,
676 info_dict=target_info,
677 reset_file_map=True)
678 block_diff_dict[partition] = common.BlockDifference(partition, tgt,
679 src=None)
680 # Incremental OTA update.
681 else:
682 block_diff_dict[partition] = GetIncrementalBlockDifferenceForPartition(
683 partition)
684 assert "system" in block_diff_dict
685
686 # Get the block diffs from the device specific script. If there is a
687 # duplicate block diff for a partition, ignore the diff in the generic script
688 # and use the one in the device specific script instead.
689 if source_zip:
690 device_specific_diffs = device_specific.IncrementalOTA_GetBlockDifferences()
691 function_name = "IncrementalOTA_GetBlockDifferences"
692 else:
693 device_specific_diffs = device_specific.FullOTA_GetBlockDifferences()
694 function_name = "FullOTA_GetBlockDifferences"
695
696 if device_specific_diffs:
697 assert all(isinstance(diff, common.BlockDifference)
698 for diff in device_specific_diffs), \
699 "{} is not returning a list of BlockDifference objects".format(
700 function_name)
701 for diff in device_specific_diffs:
702 if diff.partition in block_diff_dict:
703 logger.warning("Duplicate block difference found. Device specific block"
704 " diff for partition '%s' overrides the one in generic"
705 " script.", diff.partition)
706 block_diff_dict[diff.partition] = diff
707
708 return block_diff_dict
709
710
Tao Bao491d7e22018-02-21 13:17:22 -0800711def WriteFullOTAPackage(input_zip, output_file):
Tao Bao1c320f82019-10-04 23:25:12 -0700712 target_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700713
Tao Bao481bab82017-12-21 11:23:09 -0800714 # We don't know what version it will be installed on top of. We expect the API
715 # just won't change very often. Similarly for fstab, it might have changed in
716 # the target build.
717 target_api_version = target_info["recovery_api_version"]
718 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700719
Tao Bao481bab82017-12-21 11:23:09 -0800720 if target_info.oem_props and not OPTIONS.oem_no_mount:
721 target_info.WriteMountOemScript(script)
722
Tao Baodf3a48b2018-01-10 16:30:43 -0800723 metadata = GetPackageMetadata(target_info)
Doug Zongker2ea21062010-04-28 16:05:21 -0700724
Tao Bao491d7e22018-02-21 13:17:22 -0800725 if not OPTIONS.no_signing:
726 staging_file = common.MakeTempFile(suffix='.zip')
727 else:
728 staging_file = output_file
729
730 output_zip = zipfile.ZipFile(
731 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
732
Doug Zongker05d3dea2009-06-22 11:32:31 -0700733 device_specific = common.DeviceSpecificParams(
734 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800735 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700736 output_zip=output_zip,
737 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700738 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700739 metadata=metadata,
740 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700741
Bill Peckhame868aec2019-09-17 17:06:47 -0700742 assert HasRecoveryPatch(input_zip, info_dict=OPTIONS.info_dict)
Doug Zongkerc9253822014-02-04 12:17:58 -0800743
Tao Bao481bab82017-12-21 11:23:09 -0800744 # Assertions (e.g. downgrade check, device properties check).
745 ts = target_info.GetBuildProp("ro.build.date.utc")
746 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700747 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700748
Tao Bao481bab82017-12-21 11:23:09 -0800749 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700750 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800751
Tianjie Xuf67dd802019-05-20 17:50:36 -0700752 block_diff_dict = GetBlockDifferences(target_zip=input_zip, source_zip=None,
753 target_info=target_info,
754 source_info=None,
755 device_specific=device_specific)
756
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800757 # Two-step package strategy (in chronological order, which is *not*
758 # the order in which the generated script has things):
759 #
760 # if stage is not "2/3" or "3/3":
761 # write recovery image to boot partition
762 # set stage to "2/3"
763 # reboot to boot partition and restart recovery
764 # else if stage is "2/3":
765 # write recovery image to recovery partition
766 # set stage to "3/3"
767 # reboot to recovery partition and restart recovery
768 # else:
769 # (stage must be "3/3")
770 # set stage to ""
771 # do normal full package installation:
772 # wipe and install system, boot image, etc.
773 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700774 # complete script normally
775 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800776
777 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
778 OPTIONS.input_tmp, "RECOVERY")
779 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800780 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800781 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800782 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800783 assert fs.fs_type.upper() == "EMMC", \
784 "two-step packages only supported on devices with EMMC /misc partitions"
785 bcb_dev = {"bcb_dev": fs.device}
786 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
787 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700788if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800789""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800790
791 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
792 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800793 script.WriteRawImage("/recovery", "recovery.img")
794 script.AppendExtra("""
795set_stage("%(bcb_dev)s", "3/3");
796reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700797else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800798""" % bcb_dev)
799
Tao Baod42e97e2016-11-30 12:11:57 -0800800 # Stage 3/3: Make changes.
801 script.Comment("Stage 3/3")
802
Tao Bao6c55a8a2015-04-08 15:30:27 -0700803 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800804 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700805
Doug Zongkere5ff5902012-01-17 10:55:37 -0800806 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700807
Tianjie Xuf67dd802019-05-20 17:50:36 -0700808 # All other partitions as well as the data wipe use 10% of the progress, and
809 # the update of the system partition takes the remaining progress.
810 system_progress = 0.9 - (len(block_diff_dict) - 1) * 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700811 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800812 system_progress -= 0.1
Tianjie Xuf67dd802019-05-20 17:50:36 -0700813 progress_dict = {partition: 0.1 for partition in block_diff_dict}
814 progress_dict["system"] = system_progress
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700815
Yifan Hong10c530d2018-12-27 17:34:18 -0800816 if target_info.get('use_dynamic_partitions') == "true":
817 # Use empty source_info_dict to indicate that all partitions / groups must
818 # be re-added.
819 dynamic_partitions_diff = common.DynamicPartitionsDifference(
820 info_dict=OPTIONS.info_dict,
Tianjie Xuf67dd802019-05-20 17:50:36 -0700821 block_diffs=block_diff_dict.values(),
Yifan Hong10c530d2018-12-27 17:34:18 -0800822 progress_dict=progress_dict)
823 dynamic_partitions_diff.WriteScript(script, output_zip,
824 write_verify_script=OPTIONS.verify)
825 else:
Tianjie Xuf67dd802019-05-20 17:50:36 -0700826 for block_diff in block_diff_dict.values():
Yifan Hong10c530d2018-12-27 17:34:18 -0800827 block_diff.WriteScript(script, output_zip,
828 progress=progress_dict.get(block_diff.partition),
829 write_verify_script=OPTIONS.verify)
Doug Zongker73ef8252009-07-23 15:12:53 -0700830
Yifan Hong9276cf02019-08-21 16:37:04 -0700831 CheckVintfIfTrebleEnabled(OPTIONS.input_tmp, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700832
Yifan Hong10c530d2018-12-27 17:34:18 -0800833 boot_img = common.GetBootableImage(
834 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Tao Bao481bab82017-12-21 11:23:09 -0800835 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700836 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700837
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700838 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700839
Tianjie Xuf67dd802019-05-20 17:50:36 -0700840 script.ShowProgress(0.1, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700841 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700842
Doug Zongker1c390a22009-05-14 19:06:36 -0700843 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700844 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700845
Doug Zongker14833602010-02-02 13:12:04 -0800846 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800847
Doug Zongker922206e2014-03-04 13:16:24 -0800848 if OPTIONS.wipe_user_data:
849 script.ShowProgress(0.1, 10)
850 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700851
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800852 if OPTIONS.two_step:
853 script.AppendExtra("""
854set_stage("%(bcb_dev)s", "");
855""" % bcb_dev)
856 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800857
858 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
859 script.Comment("Stage 1/3")
860 _WriteRecoveryImageToBoot(script, output_zip)
861
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800862 script.AppendExtra("""
863set_stage("%(bcb_dev)s", "2/3");
864reboot_now("%(bcb_dev)s", "");
865endif;
866endif;
867""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800868
Tao Bao5d182562016-02-23 11:38:39 -0800869 script.SetProgress(1)
870 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800871 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -0800872
873 # We haven't written the metadata entry, which will be done in
874 # FinalizeMetadata.
875 common.ZipClose(output_zip)
876
877 needed_property_files = (
878 NonAbOtaPropertyFiles(),
879 )
880 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Doug Zongker2ea21062010-04-28 16:05:21 -0700881
Doug Zongkerfc44a512014-08-26 13:10:25 -0700882
xunchang1cfe2512019-02-19 14:14:48 -0800883def WriteMetadata(metadata, output):
884 """Writes the metadata to the zip archive or a file.
885
886 Args:
887 metadata: The metadata dict for the package.
888 output: A ZipFile object or a string of the output file path.
889 """
890
Tao Bao59cf0c52019-06-25 10:04:24 -0700891 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.items())])
xunchang1cfe2512019-02-19 14:14:48 -0800892 if isinstance(output, zipfile.ZipFile):
893 common.ZipWriteStr(output, METADATA_NAME, value,
894 compress_type=zipfile.ZIP_STORED)
895 return
896
897 with open(output, 'w') as f:
898 f.write(value)
Doug Zongkereef39442009-04-02 12:14:19 -0700899
Doug Zongkerfc44a512014-08-26 13:10:25 -0700900
Tao Bao481bab82017-12-21 11:23:09 -0800901def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -0800902 # Only incremental OTAs are allowed to reach here.
903 assert OPTIONS.incremental_source is not None
904
Tao Bao481bab82017-12-21 11:23:09 -0800905 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
906 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Bao59cf0c52019-06-25 10:04:24 -0700907 is_downgrade = int(post_timestamp) < int(pre_timestamp)
Tao Baob31892e2017-02-07 11:21:17 -0800908
909 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800910 if not is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700911 raise RuntimeError(
912 "--downgrade or --override_timestamp specified but no downgrade "
913 "detected: pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800914 metadata["ota-downgrade"] = "yes"
Tao Baob31892e2017-02-07 11:21:17 -0800915 else:
916 if is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700917 raise RuntimeError(
918 "Downgrade detected based on timestamp check: pre: %s, post: %s. "
919 "Need to specify --override_timestamp OR --downgrade to allow "
920 "building the incremental." % (pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800921
922
Tao Baodf3a48b2018-01-10 16:30:43 -0800923def GetPackageMetadata(target_info, source_info=None):
924 """Generates and returns the metadata dict.
925
926 It generates a dict() that contains the info to be written into an OTA
927 package (META-INF/com/android/metadata). It also handles the detection of
Tao Baofaa8e0b2018-04-12 14:31:43 -0700928 downgrade / data wipe based on the global options.
Tao Baodf3a48b2018-01-10 16:30:43 -0800929
930 Args:
931 target_info: The BuildInfo instance that holds the target build info.
932 source_info: The BuildInfo instance that holds the source build info, or
933 None if generating full OTA.
934
935 Returns:
936 A dict to be written into package metadata entry.
937 """
Tao Bao1c320f82019-10-04 23:25:12 -0700938 assert isinstance(target_info, common.BuildInfo)
939 assert source_info is None or isinstance(source_info, common.BuildInfo)
Tao Baodf3a48b2018-01-10 16:30:43 -0800940
Tianjied6867162020-05-10 14:30:13 -0700941 separator = '|'
942
943 boot_variable_values = {}
944 if OPTIONS.boot_variable_file:
945 d = common.LoadDictionaryFromFile(OPTIONS.boot_variable_file)
946 for key, values in d.items():
947 boot_variable_values[key] = [val.strip() for val in values.split(',')]
948
949 post_build_devices, post_build_fingerprints = \
950 CalculateRuntimeDevicesAndFingerprints(target_info, boot_variable_values)
Tao Baodf3a48b2018-01-10 16:30:43 -0800951 metadata = {
Tianjied6867162020-05-10 14:30:13 -0700952 'post-build': separator.join(sorted(post_build_fingerprints)),
953 'post-build-incremental': target_info.GetBuildProp(
Tao Baodf3a48b2018-01-10 16:30:43 -0800954 'ro.build.version.incremental'),
Tianjied6867162020-05-10 14:30:13 -0700955 'post-sdk-level': target_info.GetBuildProp(
Tao Bao35dc2552018-02-01 13:18:00 -0800956 'ro.build.version.sdk'),
Tianjied6867162020-05-10 14:30:13 -0700957 'post-security-patch-level': target_info.GetBuildProp(
Tao Bao35dc2552018-02-01 13:18:00 -0800958 'ro.build.version.security_patch'),
Tao Baodf3a48b2018-01-10 16:30:43 -0800959 }
960
Yifan Hong65afc072020-04-17 10:08:10 -0700961 if target_info.is_ab and not OPTIONS.force_non_ab:
Tao Baodf3a48b2018-01-10 16:30:43 -0800962 metadata['ota-type'] = 'AB'
963 metadata['ota-required-cache'] = '0'
964 else:
965 metadata['ota-type'] = 'BLOCK'
966
967 if OPTIONS.wipe_user_data:
968 metadata['ota-wipe'] = 'yes'
969
Tao Bao393eeb42019-03-06 16:00:38 -0800970 if OPTIONS.retrofit_dynamic_partitions:
971 metadata['ota-retrofit-dynamic-partitions'] = 'yes'
972
Tao Baodf3a48b2018-01-10 16:30:43 -0800973 is_incremental = source_info is not None
974 if is_incremental:
Tianjied6867162020-05-10 14:30:13 -0700975 pre_build_devices, pre_build_fingerprints = \
976 CalculateRuntimeDevicesAndFingerprints(source_info,
977 boot_variable_values)
978 metadata['pre-build'] = separator.join(sorted(pre_build_fingerprints))
Tao Baodf3a48b2018-01-10 16:30:43 -0800979 metadata['pre-build-incremental'] = source_info.GetBuildProp(
980 'ro.build.version.incremental')
Tianjied6867162020-05-10 14:30:13 -0700981 metadata['pre-device'] = separator.join(sorted(pre_build_devices))
Tao Baodf3a48b2018-01-10 16:30:43 -0800982 else:
Tianjied6867162020-05-10 14:30:13 -0700983 metadata['pre-device'] = separator.join(sorted(post_build_devices))
Tao Baodf3a48b2018-01-10 16:30:43 -0800984
Tao Baofaa8e0b2018-04-12 14:31:43 -0700985 # Use the actual post-timestamp, even for a downgrade case.
986 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
987
988 # Detect downgrades and set up downgrade flags accordingly.
Tao Baodf3a48b2018-01-10 16:30:43 -0800989 if is_incremental:
990 HandleDowngradeMetadata(metadata, target_info, source_info)
Tao Baodf3a48b2018-01-10 16:30:43 -0800991
992 return metadata
993
994
Tao Baod3fc38a2018-03-08 16:09:01 -0800995class PropertyFiles(object):
996 """A class that computes the property-files string for an OTA package.
997
998 A property-files string is a comma-separated string that contains the
999 offset/size info for an OTA package. The entries, which must be ZIP_STORED,
1000 can be fetched directly with the package URL along with the offset/size info.
1001 These strings can be used for streaming A/B OTAs, or allowing an updater to
1002 download package metadata entry directly, without paying the cost of
1003 downloading entire package.
Tao Baofe5b69a2018-03-02 09:47:43 -08001004
Tao Baocc8e2662018-03-01 19:30:00 -08001005 Computing the final property-files string requires two passes. Because doing
1006 the whole package signing (with signapk.jar) will possibly reorder the ZIP
1007 entries, which may in turn invalidate earlier computed ZIP entry offset/size
1008 values.
1009
1010 This class provides functions to be called for each pass. The general flow is
1011 as follows.
1012
Tao Baod3fc38a2018-03-08 16:09:01 -08001013 property_files = PropertyFiles()
Tao Baocc8e2662018-03-01 19:30:00 -08001014 # The first pass, which writes placeholders before doing initial signing.
1015 property_files.Compute()
1016 SignOutput()
1017
1018 # The second pass, by replacing the placeholders with actual data.
1019 property_files.Finalize()
1020 SignOutput()
1021
1022 And the caller can additionally verify the final result.
1023
1024 property_files.Verify()
Tao Baofe5b69a2018-03-02 09:47:43 -08001025 """
1026
Tao Baocc8e2662018-03-01 19:30:00 -08001027 def __init__(self):
Tao Baod3fc38a2018-03-08 16:09:01 -08001028 self.name = None
1029 self.required = ()
1030 self.optional = ()
Tao Baofe5b69a2018-03-02 09:47:43 -08001031
Tao Baocc8e2662018-03-01 19:30:00 -08001032 def Compute(self, input_zip):
1033 """Computes and returns a property-files string with placeholders.
Tao Baofe5b69a2018-03-02 09:47:43 -08001034
Tao Baocc8e2662018-03-01 19:30:00 -08001035 We reserve extra space for the offset and size of the metadata entry itself,
1036 although we don't know the final values until the package gets signed.
Tao Baofe5b69a2018-03-02 09:47:43 -08001037
Tao Baocc8e2662018-03-01 19:30:00 -08001038 Args:
1039 input_zip: The input ZIP file.
Tao Baofe5b69a2018-03-02 09:47:43 -08001040
Tao Baocc8e2662018-03-01 19:30:00 -08001041 Returns:
1042 A string with placeholders for the metadata offset/size info, e.g.
1043 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1044 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001045 return self.GetPropertyFilesString(input_zip, reserve_space=True)
Tao Baofe5b69a2018-03-02 09:47:43 -08001046
Tao Baod2ce2ed2018-03-16 12:59:42 -07001047 class InsufficientSpaceException(Exception):
1048 pass
1049
Tao Baocc8e2662018-03-01 19:30:00 -08001050 def Finalize(self, input_zip, reserved_length):
1051 """Finalizes a property-files string with actual METADATA offset/size info.
1052
1053 The input ZIP file has been signed, with the ZIP entries in the desired
1054 place (signapk.jar will possibly reorder the ZIP entries). Now we compute
1055 the ZIP entry offsets and construct the property-files string with actual
1056 data. Note that during this process, we must pad the property-files string
1057 to the reserved length, so that the METADATA entry size remains the same.
1058 Otherwise the entries' offsets and sizes may change again.
1059
1060 Args:
1061 input_zip: The input ZIP file.
1062 reserved_length: The reserved length of the property-files string during
1063 the call to Compute(). The final string must be no more than this
1064 size.
1065
1066 Returns:
1067 A property-files string including the metadata offset/size info, e.g.
1068 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379 ".
1069
1070 Raises:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001071 InsufficientSpaceException: If the reserved length is insufficient to hold
1072 the final string.
Tao Baocc8e2662018-03-01 19:30:00 -08001073 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001074 result = self.GetPropertyFilesString(input_zip, reserve_space=False)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001075 if len(result) > reserved_length:
1076 raise self.InsufficientSpaceException(
1077 'Insufficient reserved space: reserved={}, actual={}'.format(
1078 reserved_length, len(result)))
1079
Tao Baocc8e2662018-03-01 19:30:00 -08001080 result += ' ' * (reserved_length - len(result))
1081 return result
1082
1083 def Verify(self, input_zip, expected):
1084 """Verifies the input ZIP file contains the expected property-files string.
1085
1086 Args:
1087 input_zip: The input ZIP file.
1088 expected: The property-files string that's computed from Finalize().
1089
1090 Raises:
1091 AssertionError: On finding a mismatch.
1092 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001093 actual = self.GetPropertyFilesString(input_zip)
Tao Baocc8e2662018-03-01 19:30:00 -08001094 assert actual == expected, \
1095 "Mismatching streaming metadata: {} vs {}.".format(actual, expected)
1096
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001097 def GetPropertyFilesString(self, zip_file, reserve_space=False):
1098 """
1099 Constructs the property-files string per request.
1100
1101 Args:
1102 zip_file: The input ZIP file.
1103 reserved_length: The reserved length of the property-files string.
1104
1105 Returns:
1106 A property-files string including the metadata offset/size info, e.g.
1107 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1108 """
Tao Baocc8e2662018-03-01 19:30:00 -08001109
1110 def ComputeEntryOffsetSize(name):
1111 """Computes the zip entry offset and size."""
1112 info = zip_file.getinfo(name)
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001113 offset = info.header_offset
1114 offset += zipfile.sizeFileHeader
1115 offset += len(info.extra) + len(info.filename)
Tao Baocc8e2662018-03-01 19:30:00 -08001116 size = info.file_size
1117 return '%s:%d:%d' % (os.path.basename(name), offset, size)
1118
1119 tokens = []
Tao Bao85f16982018-03-08 16:28:33 -08001120 tokens.extend(self._GetPrecomputed(zip_file))
Tao Baocc8e2662018-03-01 19:30:00 -08001121 for entry in self.required:
1122 tokens.append(ComputeEntryOffsetSize(entry))
1123 for entry in self.optional:
1124 if entry in zip_file.namelist():
1125 tokens.append(ComputeEntryOffsetSize(entry))
1126
1127 # 'META-INF/com/android/metadata' is required. We don't know its actual
1128 # offset and length (as well as the values for other entries). So we reserve
Tao Baod2ce2ed2018-03-16 12:59:42 -07001129 # 15-byte as a placeholder ('offset:length'), which is sufficient to cover
1130 # the space for metadata entry. Because 'offset' allows a max of 10-digit
1131 # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the
1132 # reserved space serves the metadata entry only.
Tao Baocc8e2662018-03-01 19:30:00 -08001133 if reserve_space:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001134 tokens.append('metadata:' + ' ' * 15)
Tao Baocc8e2662018-03-01 19:30:00 -08001135 else:
1136 tokens.append(ComputeEntryOffsetSize(METADATA_NAME))
1137
1138 return ','.join(tokens)
Tao Baofe5b69a2018-03-02 09:47:43 -08001139
Tao Bao85f16982018-03-08 16:28:33 -08001140 def _GetPrecomputed(self, input_zip):
1141 """Computes the additional tokens to be included into the property-files.
1142
1143 This applies to tokens without actual ZIP entries, such as
1144 payload_metadadata.bin. We want to expose the offset/size to updaters, so
1145 that they can download the payload metadata directly with the info.
1146
1147 Args:
1148 input_zip: The input zip file.
1149
1150 Returns:
1151 A list of strings (tokens) to be added to the property-files string.
1152 """
1153 # pylint: disable=no-self-use
1154 # pylint: disable=unused-argument
1155 return []
1156
Tao Baofe5b69a2018-03-02 09:47:43 -08001157
Tao Baod3fc38a2018-03-08 16:09:01 -08001158class StreamingPropertyFiles(PropertyFiles):
1159 """A subclass for computing the property-files for streaming A/B OTAs."""
1160
1161 def __init__(self):
1162 super(StreamingPropertyFiles, self).__init__()
1163 self.name = 'ota-streaming-property-files'
1164 self.required = (
1165 # payload.bin and payload_properties.txt must exist.
1166 'payload.bin',
1167 'payload_properties.txt',
1168 )
1169 self.optional = (
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001170 # care_map is available only if dm-verity is enabled.
1171 'care_map.pb',
Tao Baod3fc38a2018-03-08 16:09:01 -08001172 'care_map.txt',
1173 # compatibility.zip is available only if target supports Treble.
1174 'compatibility.zip',
1175 )
1176
1177
Tao Bao85f16982018-03-08 16:28:33 -08001178class AbOtaPropertyFiles(StreamingPropertyFiles):
1179 """The property-files for A/B OTA that includes payload_metadata.bin info.
1180
1181 Since P, we expose one more token (aka property-file), in addition to the ones
1182 for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'.
1183 'payload_metadata.bin' is the header part of a payload ('payload.bin'), which
1184 doesn't exist as a separate ZIP entry, but can be used to verify if the
1185 payload can be applied on the given device.
1186
1187 For backward compatibility, we keep both of the 'ota-streaming-property-files'
1188 and the newly added 'ota-property-files' in P. The new token will only be
1189 available in 'ota-property-files'.
1190 """
1191
1192 def __init__(self):
1193 super(AbOtaPropertyFiles, self).__init__()
1194 self.name = 'ota-property-files'
1195
1196 def _GetPrecomputed(self, input_zip):
1197 offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip)
1198 return ['payload_metadata.bin:{}:{}'.format(offset, size)]
1199
1200 @staticmethod
1201 def _GetPayloadMetadataOffsetAndSize(input_zip):
1202 """Computes the offset and size of the payload metadata for a given package.
1203
1204 (From system/update_engine/update_metadata.proto)
1205 A delta update file contains all the deltas needed to update a system from
1206 one specific version to another specific version. The update format is
1207 represented by this struct pseudocode:
1208
1209 struct delta_update_file {
1210 char magic[4] = "CrAU";
1211 uint64 file_format_version;
1212 uint64 manifest_size; // Size of protobuf DeltaArchiveManifest
1213
1214 // Only present if format_version > 1:
1215 uint32 metadata_signature_size;
1216
1217 // The Bzip2 compressed DeltaArchiveManifest
1218 char manifest[metadata_signature_size];
1219
1220 // The signature of the metadata (from the beginning of the payload up to
1221 // this location, not including the signature itself). This is a
1222 // serialized Signatures message.
1223 char medatada_signature_message[metadata_signature_size];
1224
1225 // Data blobs for files, no specific format. The specific offset
1226 // and length of each data blob is recorded in the DeltaArchiveManifest.
1227 struct {
1228 char data[];
1229 } blobs[];
1230
1231 // These two are not signed:
1232 uint64 payload_signatures_message_size;
1233 char payload_signatures_message[];
1234 };
1235
1236 'payload-metadata.bin' contains all the bytes from the beginning of the
1237 payload, till the end of 'medatada_signature_message'.
1238 """
1239 payload_info = input_zip.getinfo('payload.bin')
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001240 payload_offset = payload_info.header_offset
1241 payload_offset += zipfile.sizeFileHeader
1242 payload_offset += len(payload_info.extra) + len(payload_info.filename)
Tao Bao85f16982018-03-08 16:28:33 -08001243 payload_size = payload_info.file_size
1244
Tao Bao59cf0c52019-06-25 10:04:24 -07001245 with input_zip.open('payload.bin') as payload_fp:
Tao Bao85f16982018-03-08 16:28:33 -08001246 header_bin = payload_fp.read(24)
1247
1248 # network byte order (big-endian)
1249 header = struct.unpack("!IQQL", header_bin)
1250
1251 # 'CrAU'
1252 magic = header[0]
1253 assert magic == 0x43724155, "Invalid magic: {:x}".format(magic)
1254
1255 manifest_size = header[2]
1256 metadata_signature_size = header[3]
1257 metadata_total = 24 + manifest_size + metadata_signature_size
1258 assert metadata_total < payload_size
1259
1260 return (payload_offset, metadata_total)
1261
1262
Tao Bao491d7e22018-02-21 13:17:22 -08001263class NonAbOtaPropertyFiles(PropertyFiles):
1264 """The property-files for non-A/B OTA.
1265
1266 For non-A/B OTA, the property-files string contains the info for METADATA
1267 entry, with which a system updater can be fetched the package metadata prior
1268 to downloading the entire package.
1269 """
1270
1271 def __init__(self):
1272 super(NonAbOtaPropertyFiles, self).__init__()
1273 self.name = 'ota-property-files'
1274
1275
Tao Baod3fc38a2018-03-08 16:09:01 -08001276def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
Tao Baofe5b69a2018-03-02 09:47:43 -08001277 """Finalizes the metadata and signs an A/B OTA package.
1278
1279 In order to stream an A/B OTA package, we need 'ota-streaming-property-files'
1280 that contains the offsets and sizes for the ZIP entries. An example
1281 property-files string is as follows.
1282
1283 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379"
1284
1285 OTA server can pass down this string, in addition to the package URL, to the
1286 system update client. System update client can then fetch individual ZIP
1287 entries (ZIP_STORED) directly at the given offset of the URL.
1288
1289 Args:
1290 metadata: The metadata dict for the package.
1291 input_file: The input ZIP filename that doesn't contain the package METADATA
1292 entry yet.
1293 output_file: The final output ZIP filename.
Tao Baod3fc38a2018-03-08 16:09:01 -08001294 needed_property_files: The list of PropertyFiles' to be generated.
Tao Baofe5b69a2018-03-02 09:47:43 -08001295 """
Tao Baofe5b69a2018-03-02 09:47:43 -08001296
Tao Baod2ce2ed2018-03-16 12:59:42 -07001297 def ComputeAllPropertyFiles(input_file, needed_property_files):
1298 # Write the current metadata entry with placeholders.
1299 with zipfile.ZipFile(input_file) as input_zip:
1300 for property_files in needed_property_files:
1301 metadata[property_files.name] = property_files.Compute(input_zip)
1302 namelist = input_zip.namelist()
Tao Baofe5b69a2018-03-02 09:47:43 -08001303
Tao Baod2ce2ed2018-03-16 12:59:42 -07001304 if METADATA_NAME in namelist:
1305 common.ZipDelete(input_file, METADATA_NAME)
1306 output_zip = zipfile.ZipFile(input_file, 'a')
1307 WriteMetadata(metadata, output_zip)
1308 common.ZipClose(output_zip)
1309
1310 if OPTIONS.no_signing:
1311 return input_file
1312
Tao Bao491d7e22018-02-21 13:17:22 -08001313 prelim_signing = common.MakeTempFile(suffix='.zip')
1314 SignOutput(input_file, prelim_signing)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001315 return prelim_signing
Tao Baofe5b69a2018-03-02 09:47:43 -08001316
Tao Baod2ce2ed2018-03-16 12:59:42 -07001317 def FinalizeAllPropertyFiles(prelim_signing, needed_property_files):
1318 with zipfile.ZipFile(prelim_signing) as prelim_signing_zip:
1319 for property_files in needed_property_files:
1320 metadata[property_files.name] = property_files.Finalize(
1321 prelim_signing_zip, len(metadata[property_files.name]))
1322
1323 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP
1324 # entries, as well as padding the entry headers. We do a preliminary signing
1325 # (with an incomplete metadata entry) to allow that to happen. Then compute
1326 # the ZIP entry offsets, write back the final metadata and do the final
1327 # signing.
1328 prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files)
1329 try:
1330 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
1331 except PropertyFiles.InsufficientSpaceException:
1332 # Even with the preliminary signing, the entry orders may change
1333 # dramatically, which leads to insufficiently reserved space during the
1334 # first call to ComputeAllPropertyFiles(). In that case, we redo all the
1335 # preliminary signing works, based on the already ordered ZIP entries, to
1336 # address the issue.
1337 prelim_signing = ComputeAllPropertyFiles(
1338 prelim_signing, needed_property_files)
1339 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
Tao Baofe5b69a2018-03-02 09:47:43 -08001340
1341 # Replace the METADATA entry.
1342 common.ZipDelete(prelim_signing, METADATA_NAME)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001343 output_zip = zipfile.ZipFile(prelim_signing, 'a')
Tao Baofe5b69a2018-03-02 09:47:43 -08001344 WriteMetadata(metadata, output_zip)
1345 common.ZipClose(output_zip)
1346
1347 # Re-sign the package after updating the metadata entry.
Tao Bao491d7e22018-02-21 13:17:22 -08001348 if OPTIONS.no_signing:
1349 output_file = prelim_signing
1350 else:
1351 SignOutput(prelim_signing, output_file)
Tao Baofe5b69a2018-03-02 09:47:43 -08001352
1353 # Reopen the final signed zip to double check the streaming metadata.
Tao Baod2ce2ed2018-03-16 12:59:42 -07001354 with zipfile.ZipFile(output_file) as output_zip:
Tao Baod3fc38a2018-03-08 16:09:01 -08001355 for property_files in needed_property_files:
1356 property_files.Verify(output_zip, metadata[property_files.name].strip())
Tao Baofe5b69a2018-03-02 09:47:43 -08001357
xunchang1cfe2512019-02-19 14:14:48 -08001358 # If requested, dump the metadata to a separate file.
1359 output_metadata_path = OPTIONS.output_metadata_path
1360 if output_metadata_path:
1361 WriteMetadata(metadata, output_metadata_path)
1362
Tao Baofe5b69a2018-03-02 09:47:43 -08001363
Tao Bao491d7e22018-02-21 13:17:22 -08001364def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
Tao Bao1c320f82019-10-04 23:25:12 -07001365 target_info = common.BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1366 source_info = common.BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -08001367
Tao Bao481bab82017-12-21 11:23:09 -08001368 target_api_version = target_info["recovery_api_version"]
1369 source_api_version = source_info["recovery_api_version"]
1370 if source_api_version == 0:
Tao Bao32fcdab2018-10-12 10:30:39 -07001371 logger.warning(
1372 "Generating edify script for a source that can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001373
Tao Bao481bab82017-12-21 11:23:09 -08001374 script = edify_generator.EdifyGenerator(
1375 source_api_version, target_info, fstab=source_info["fstab"])
1376
1377 if target_info.oem_props or source_info.oem_props:
1378 if not OPTIONS.oem_no_mount:
1379 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -07001380
Tao Baodf3a48b2018-01-10 16:30:43 -08001381 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -08001382
Tao Bao491d7e22018-02-21 13:17:22 -08001383 if not OPTIONS.no_signing:
1384 staging_file = common.MakeTempFile(suffix='.zip')
1385 else:
1386 staging_file = output_file
1387
1388 output_zip = zipfile.ZipFile(
1389 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
1390
Geremy Condra36bd3652014-02-06 19:45:10 -08001391 device_specific = common.DeviceSpecificParams(
1392 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001393 source_version=source_api_version,
Yifan Hong8a66a712019-04-04 15:37:57 -07001394 source_tmp=OPTIONS.source_tmp,
Geremy Condra36bd3652014-02-06 19:45:10 -08001395 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001396 target_version=target_api_version,
Yifan Hong8a66a712019-04-04 15:37:57 -07001397 target_tmp=OPTIONS.target_tmp,
Geremy Condra36bd3652014-02-06 19:45:10 -08001398 output_zip=output_zip,
1399 script=script,
1400 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -08001401 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001402
Geremy Condra36bd3652014-02-06 19:45:10 -08001403 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001404 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001405 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001406 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001407 updating_boot = (not OPTIONS.two_step and
1408 (source_boot.data != target_boot.data))
1409
Geremy Condra36bd3652014-02-06 19:45:10 -08001410 target_recovery = common.GetBootableImage(
1411 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -08001412
Tianjie Xuf67dd802019-05-20 17:50:36 -07001413 block_diff_dict = GetBlockDifferences(target_zip=target_zip,
1414 source_zip=source_zip,
1415 target_info=target_info,
1416 source_info=source_info,
1417 device_specific=device_specific)
Geremy Condra36bd3652014-02-06 19:45:10 -08001418
Yifan Hong9276cf02019-08-21 16:37:04 -07001419 CheckVintfIfTrebleEnabled(OPTIONS.target_tmp, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001420
Tao Bao481bab82017-12-21 11:23:09 -08001421 # Assertions (e.g. device properties check).
1422 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -08001423 device_specific.IncrementalOTA_Assertions()
1424
1425 # Two-step incremental package strategy (in chronological order,
1426 # which is *not* the order in which the generated script has
1427 # things):
1428 #
1429 # if stage is not "2/3" or "3/3":
1430 # do verification on current system
1431 # write recovery image to boot partition
1432 # set stage to "2/3"
1433 # reboot to boot partition and restart recovery
1434 # else if stage is "2/3":
1435 # write recovery image to recovery partition
1436 # set stage to "3/3"
1437 # reboot to recovery partition and restart recovery
1438 # else:
1439 # (stage must be "3/3")
1440 # perform update:
1441 # patch system files, etc.
1442 # force full install of new boot image
1443 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001444 # complete script normally
1445 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001446
1447 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001448 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001449 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001450 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001451 assert fs.fs_type.upper() == "EMMC", \
1452 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001453 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001454 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1455 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001456if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001457""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001458
1459 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1460 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001461 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001462 script.WriteRawImage("/recovery", "recovery.img")
1463 script.AppendExtra("""
1464set_stage("%(bcb_dev)s", "3/3");
1465reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001466else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001467""" % bcb_dev)
1468
Tao Baod42e97e2016-11-30 12:11:57 -08001469 # Stage 1/3: (a) Verify the current system.
1470 script.Comment("Stage 1/3")
1471
Tao Bao6c55a8a2015-04-08 15:30:27 -07001472 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001473 script.Print("Source: {}".format(source_info.fingerprint))
1474 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001475
Geremy Condra36bd3652014-02-06 19:45:10 -08001476 script.Print("Verifying current system...")
1477
1478 device_specific.IncrementalOTA_VerifyBegin()
1479
Tao Bao481bab82017-12-21 11:23:09 -08001480 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001481
Tao Baod8d14be2016-02-04 14:26:02 -08001482 # Check the required cache size (i.e. stashed blocks).
Tianjie Xuf67dd802019-05-20 17:50:36 -07001483 required_cache_sizes = [diff.required_cache for diff in
1484 block_diff_dict.values()]
Geremy Condra36bd3652014-02-06 19:45:10 -08001485 if updating_boot:
Yifan Hongbdb32012020-05-07 12:38:53 -07001486 boot_type, boot_device_expr = common.GetTypeAndDeviceExpr("/boot",
1487 source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001488 d = common.Difference(target_boot, source_boot)
1489 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001490 if d is None:
1491 include_full_boot = True
1492 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1493 else:
1494 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001495
Tao Bao32fcdab2018-10-12 10:30:39 -07001496 logger.info(
1497 "boot target: %d source: %d diff: %d", target_boot.size,
1498 source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -08001499
Tao Bao51216552018-08-26 11:53:15 -07001500 common.ZipWriteStr(output_zip, "boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001501
Yifan Hongbdb32012020-05-07 12:38:53 -07001502 target_expr = 'concat("{}:",{},":{}:{}")'.format(
1503 boot_type, boot_device_expr, target_boot.size, target_boot.sha1)
1504 source_expr = 'concat("{}:",{},":{}:{}")'.format(
1505 boot_type, boot_device_expr, source_boot.size, source_boot.sha1)
1506 script.PatchPartitionExprCheck(target_expr, source_expr)
Tao Bao51216552018-08-26 11:53:15 -07001507
Tianjie Xuf67dd802019-05-20 17:50:36 -07001508 required_cache_sizes.append(target_boot.size)
Tao Baod8d14be2016-02-04 14:26:02 -08001509
Tianjie Xuf67dd802019-05-20 17:50:36 -07001510 if required_cache_sizes:
1511 script.CacheFreeSpaceCheck(max(required_cache_sizes))
1512
1513 # Verify the existing partitions.
1514 for diff in block_diff_dict.values():
1515 diff.WriteVerifyScript(script, touched_blocks_only=True)
Geremy Condra36bd3652014-02-06 19:45:10 -08001516
1517 device_specific.IncrementalOTA_VerifyEnd()
1518
1519 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001520 # Stage 1/3: (b) Write recovery image to /boot.
1521 _WriteRecoveryImageToBoot(script, output_zip)
1522
Geremy Condra36bd3652014-02-06 19:45:10 -08001523 script.AppendExtra("""
1524set_stage("%(bcb_dev)s", "2/3");
1525reboot_now("%(bcb_dev)s", "");
1526else
1527""" % bcb_dev)
1528
Tao Baod42e97e2016-11-30 12:11:57 -08001529 # Stage 3/3: Make changes.
1530 script.Comment("Stage 3/3")
1531
Geremy Condra36bd3652014-02-06 19:45:10 -08001532 script.Comment("---- start making changes here ----")
1533
1534 device_specific.IncrementalOTA_InstallBegin()
1535
Tianjie Xuf67dd802019-05-20 17:50:36 -07001536 progress_dict = {partition: 0.1 for partition in block_diff_dict}
1537 progress_dict["system"] = 1 - len(block_diff_dict) * 0.1
Yifan Hong10c530d2018-12-27 17:34:18 -08001538
1539 if OPTIONS.source_info_dict.get("use_dynamic_partitions") == "true":
1540 if OPTIONS.target_info_dict.get("use_dynamic_partitions") != "true":
1541 raise RuntimeError(
1542 "can't generate incremental that disables dynamic partitions")
1543 dynamic_partitions_diff = common.DynamicPartitionsDifference(
1544 info_dict=OPTIONS.target_info_dict,
1545 source_info_dict=OPTIONS.source_info_dict,
Tianjie Xuf67dd802019-05-20 17:50:36 -07001546 block_diffs=block_diff_dict.values(),
Yifan Hong10c530d2018-12-27 17:34:18 -08001547 progress_dict=progress_dict)
1548 dynamic_partitions_diff.WriteScript(
1549 script, output_zip, write_verify_script=OPTIONS.verify)
1550 else:
Tianjie Xuf67dd802019-05-20 17:50:36 -07001551 for block_diff in block_diff_dict.values():
Yifan Hong10c530d2018-12-27 17:34:18 -08001552 block_diff.WriteScript(script, output_zip,
1553 progress=progress_dict.get(block_diff.partition),
1554 write_verify_script=OPTIONS.verify)
Geremy Condra36bd3652014-02-06 19:45:10 -08001555
1556 if OPTIONS.two_step:
1557 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1558 script.WriteRawImage("/boot", "boot.img")
Tao Bao32fcdab2018-10-12 10:30:39 -07001559 logger.info("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001560
1561 if not OPTIONS.two_step:
1562 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001563 if include_full_boot:
Tao Bao32fcdab2018-10-12 10:30:39 -07001564 logger.info("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001565 script.Print("Installing boot image...")
1566 script.WriteRawImage("/boot", "boot.img")
1567 else:
1568 # Produce the boot image by applying a patch to the current
1569 # contents of the boot partition, and write it back to the
1570 # partition.
Tao Bao32fcdab2018-10-12 10:30:39 -07001571 logger.info("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001572 script.Print("Patching boot image...")
1573 script.ShowProgress(0.1, 10)
Yifan Hongbdb32012020-05-07 12:38:53 -07001574 target_expr = 'concat("{}:",{},":{}:{}")'.format(
1575 boot_type, boot_device_expr, target_boot.size, target_boot.sha1)
1576 source_expr = 'concat("{}:",{},":{}:{}")'.format(
1577 boot_type, boot_device_expr, source_boot.size, source_boot.sha1)
1578 script.PatchPartitionExpr(target_expr, source_expr, '"boot.img.p"')
Geremy Condra36bd3652014-02-06 19:45:10 -08001579 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001580 logger.info("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001581
1582 # Do device-specific installation (eg, write radio image).
1583 device_specific.IncrementalOTA_InstallEnd()
1584
1585 if OPTIONS.extra_script is not None:
1586 script.AppendExtra(OPTIONS.extra_script)
1587
Doug Zongker922206e2014-03-04 13:16:24 -08001588 if OPTIONS.wipe_user_data:
1589 script.Print("Erasing user data...")
1590 script.FormatPartition("/data")
1591
Geremy Condra36bd3652014-02-06 19:45:10 -08001592 if OPTIONS.two_step:
1593 script.AppendExtra("""
1594set_stage("%(bcb_dev)s", "");
1595endif;
1596endif;
1597""" % bcb_dev)
1598
1599 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001600 # For downgrade OTAs, we prefer to use the update-binary in the source
1601 # build that is actually newer than the one in the target build.
1602 if OPTIONS.downgrade:
1603 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1604 else:
1605 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001606 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001607
1608 # We haven't written the metadata entry yet, which will be handled in
1609 # FinalizeMetadata().
1610 common.ZipClose(output_zip)
1611
1612 # Sign the generated zip package unless no_signing is specified.
1613 needed_property_files = (
1614 NonAbOtaPropertyFiles(),
1615 )
1616 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Geremy Condra36bd3652014-02-06 19:45:10 -08001617
Doug Zongker32b527d2014-03-04 10:03:02 -08001618
Tao Bao15a146a2018-02-21 16:06:59 -08001619def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
Tao Baof7140c02018-01-30 17:09:24 -08001620 """Returns a target-files.zip file for generating secondary payload.
1621
1622 Although the original target-files.zip already contains secondary slot
1623 images (i.e. IMAGES/system_other.img), we need to rename the files to the
1624 ones without _other suffix. Note that we cannot instead modify the names in
1625 META/ab_partitions.txt, because there are no matching partitions on device.
1626
1627 For the partitions that don't have secondary images, the ones for primary
1628 slot will be used. This is to ensure that we always have valid boot, vbmeta,
1629 bootloader images in the inactive slot.
1630
1631 Args:
1632 input_file: The input target-files.zip file.
Tao Bao15a146a2018-02-21 16:06:59 -08001633 skip_postinstall: Whether to skip copying the postinstall config file.
Tao Baof7140c02018-01-30 17:09:24 -08001634
1635 Returns:
1636 The filename of the target-files.zip for generating secondary payload.
1637 """
Tianjie Xu1c808002019-09-11 00:29:26 -07001638
1639 def GetInfoForSecondaryImages(info_file):
1640 """Updates info file for secondary payload generation.
1641
1642 Scan each line in the info file, and remove the unwanted partitions from
1643 the dynamic partition list in the related properties. e.g.
1644 "super_google_dynamic_partitions_partition_list=system vendor product"
1645 will become "super_google_dynamic_partitions_partition_list=system".
1646
1647 Args:
1648 info_file: The input info file. e.g. misc_info.txt.
1649
1650 Returns:
1651 A string of the updated info content.
1652 """
1653
1654 output_list = []
1655 with open(info_file) as f:
1656 lines = f.read().splitlines()
1657
1658 # The suffix in partition_list variables that follows the name of the
1659 # partition group.
1660 LIST_SUFFIX = 'partition_list'
1661 for line in lines:
1662 if line.startswith('#') or '=' not in line:
1663 output_list.append(line)
1664 continue
1665 key, value = line.strip().split('=', 1)
1666 if key == 'dynamic_partition_list' or key.endswith(LIST_SUFFIX):
1667 partitions = value.split()
1668 partitions = [partition for partition in partitions if partition
Tao Bao3e759462019-09-17 22:43:11 -07001669 not in SECONDARY_PAYLOAD_SKIPPED_IMAGES]
Tianjie Xu1c808002019-09-11 00:29:26 -07001670 output_list.append('{}={}'.format(key, ' '.join(partitions)))
Yifan Hongfe073432019-11-01 12:28:31 -07001671 elif key == 'virtual_ab' or key == "virtual_ab_retrofit":
1672 # Remove virtual_ab flag from secondary payload so that OTA client
1673 # don't use snapshots for secondary update
1674 pass
Tianjie Xu1c808002019-09-11 00:29:26 -07001675 else:
1676 output_list.append(line)
1677 return '\n'.join(output_list)
1678
Tao Baof7140c02018-01-30 17:09:24 -08001679 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1680 target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
1681
Tao Baodba59ee2018-01-09 13:21:02 -08001682 with zipfile.ZipFile(input_file, 'r') as input_zip:
1683 infolist = input_zip.infolist()
Tao Bao12489802018-07-12 14:47:38 -07001684
Tao Bao0ff15de2019-03-20 11:26:06 -07001685 input_tmp = common.UnzipTemp(input_file, UNZIP_PATTERN)
Tao Baodba59ee2018-01-09 13:21:02 -08001686 for info in infolist:
Tao Baof7140c02018-01-30 17:09:24 -08001687 unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
1688 if info.filename == 'IMAGES/system_other.img':
1689 common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
1690
1691 # Primary images and friends need to be skipped explicitly.
1692 elif info.filename in ('IMAGES/system.img',
1693 'IMAGES/system.map'):
1694 pass
Tao Bao3e759462019-09-17 22:43:11 -07001695
1696 # Copy images that are not in SECONDARY_PAYLOAD_SKIPPED_IMAGES.
1697 elif info.filename.startswith(('IMAGES/', 'RADIO/')):
1698 image_name = os.path.basename(info.filename)
1699 if image_name not in ['{}.img'.format(partition) for partition in
1700 SECONDARY_PAYLOAD_SKIPPED_IMAGES]:
1701 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
Tao Baof7140c02018-01-30 17:09:24 -08001702
Tao Bao15a146a2018-02-21 16:06:59 -08001703 # Skip copying the postinstall config if requested.
1704 elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
1705 pass
1706
Tianjie Xu1c808002019-09-11 00:29:26 -07001707 elif info.filename.startswith('META/'):
1708 # Remove the unnecessary partitions for secondary images from the
1709 # ab_partitions file.
1710 if info.filename == AB_PARTITIONS:
1711 with open(unzipped_file) as f:
1712 partition_list = f.read().splitlines()
1713 partition_list = [partition for partition in partition_list if partition
Tao Bao3e759462019-09-17 22:43:11 -07001714 and partition not in SECONDARY_PAYLOAD_SKIPPED_IMAGES]
Tianjie Xu1c808002019-09-11 00:29:26 -07001715 common.ZipWriteStr(target_zip, info.filename, '\n'.join(partition_list))
1716 # Remove the unnecessary partitions from the dynamic partitions list.
1717 elif (info.filename == 'META/misc_info.txt' or
1718 info.filename == DYNAMIC_PARTITION_INFO):
1719 modified_info = GetInfoForSecondaryImages(unzipped_file)
1720 common.ZipWriteStr(target_zip, info.filename, modified_info)
1721 else:
1722 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
Tao Baof7140c02018-01-30 17:09:24 -08001723
Tao Baof7140c02018-01-30 17:09:24 -08001724 common.ZipClose(target_zip)
1725
1726 return target_file
1727
1728
Tao Bao15a146a2018-02-21 16:06:59 -08001729def GetTargetFilesZipWithoutPostinstallConfig(input_file):
1730 """Returns a target-files.zip that's not containing postinstall_config.txt.
1731
1732 This allows brillo_update_payload script to skip writing all the postinstall
1733 hooks in the generated payload. The input target-files.zip file will be
1734 duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
1735 contain the postinstall_config.txt entry, the input file will be returned.
1736
1737 Args:
1738 input_file: The input target-files.zip filename.
1739
1740 Returns:
1741 The filename of target-files.zip that doesn't contain postinstall config.
1742 """
1743 # We should only make a copy if postinstall_config entry exists.
1744 with zipfile.ZipFile(input_file, 'r') as input_zip:
1745 if POSTINSTALL_CONFIG not in input_zip.namelist():
1746 return input_file
1747
1748 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1749 shutil.copyfile(input_file, target_file)
1750 common.ZipDelete(target_file, POSTINSTALL_CONFIG)
1751 return target_file
1752
1753
Yifan Hong50e79542018-11-08 17:44:12 -08001754def GetTargetFilesZipForRetrofitDynamicPartitions(input_file,
Yifan Hongb433eba2019-03-06 12:42:53 -08001755 super_block_devices,
1756 dynamic_partition_list):
Yifan Hong50e79542018-11-08 17:44:12 -08001757 """Returns a target-files.zip for retrofitting dynamic partitions.
1758
1759 This allows brillo_update_payload to generate an OTA based on the exact
1760 bits on the block devices. Postinstall is disabled.
1761
1762 Args:
1763 input_file: The input target-files.zip filename.
1764 super_block_devices: The list of super block devices
Yifan Hongb433eba2019-03-06 12:42:53 -08001765 dynamic_partition_list: The list of dynamic partitions
Yifan Hong50e79542018-11-08 17:44:12 -08001766
1767 Returns:
1768 The filename of target-files.zip with *.img replaced with super_*.img for
1769 each block device in super_block_devices.
1770 """
1771 assert super_block_devices, "No super_block_devices are specified."
1772
1773 replace = {'OTA/super_{}.img'.format(dev): 'IMAGES/{}.img'.format(dev)
Tao Bao03fecb62018-11-28 10:59:23 -08001774 for dev in super_block_devices}
Yifan Hong50e79542018-11-08 17:44:12 -08001775
1776 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1777 shutil.copyfile(input_file, target_file)
1778
Tao Baoa3705452019-06-24 15:33:41 -07001779 with zipfile.ZipFile(input_file) as input_zip:
Yifan Hong50e79542018-11-08 17:44:12 -08001780 namelist = input_zip.namelist()
1781
Yifan Hongb433eba2019-03-06 12:42:53 -08001782 input_tmp = common.UnzipTemp(input_file, RETROFIT_DAP_UNZIP_PATTERN)
1783
1784 # Remove partitions from META/ab_partitions.txt that is in
1785 # dynamic_partition_list but not in super_block_devices so that
1786 # brillo_update_payload won't generate update for those logical partitions.
1787 ab_partitions_file = os.path.join(input_tmp, *AB_PARTITIONS.split('/'))
1788 with open(ab_partitions_file) as f:
1789 ab_partitions_lines = f.readlines()
1790 ab_partitions = [line.strip() for line in ab_partitions_lines]
1791 # Assert that all super_block_devices are in ab_partitions
1792 super_device_not_updated = [partition for partition in super_block_devices
1793 if partition not in ab_partitions]
1794 assert not super_device_not_updated, \
1795 "{} is in super_block_devices but not in {}".format(
1796 super_device_not_updated, AB_PARTITIONS)
1797 # ab_partitions -= (dynamic_partition_list - super_block_devices)
1798 new_ab_partitions = common.MakeTempFile(prefix="ab_partitions", suffix=".txt")
1799 with open(new_ab_partitions, 'w') as f:
1800 for partition in ab_partitions:
1801 if (partition in dynamic_partition_list and
1802 partition not in super_block_devices):
Tao Bao59cf0c52019-06-25 10:04:24 -07001803 logger.info("Dropping %s from ab_partitions.txt", partition)
1804 continue
Yifan Hongb433eba2019-03-06 12:42:53 -08001805 f.write(partition + "\n")
1806 to_delete = [AB_PARTITIONS]
1807
Yifan Hong50e79542018-11-08 17:44:12 -08001808 # Always skip postinstall for a retrofit update.
Yifan Hongb433eba2019-03-06 12:42:53 -08001809 to_delete += [POSTINSTALL_CONFIG]
Yifan Hong50e79542018-11-08 17:44:12 -08001810
1811 # Delete dynamic_partitions_info.txt so that brillo_update_payload thinks this
1812 # is a regular update on devices without dynamic partitions support.
1813 to_delete += [DYNAMIC_PARTITION_INFO]
1814
Tao Bao03fecb62018-11-28 10:59:23 -08001815 # Remove the existing partition images as well as the map files.
Tao Bao59cf0c52019-06-25 10:04:24 -07001816 to_delete += list(replace.values())
Tao Bao03fecb62018-11-28 10:59:23 -08001817 to_delete += ['IMAGES/{}.map'.format(dev) for dev in super_block_devices]
Yifan Hong50e79542018-11-08 17:44:12 -08001818
1819 common.ZipDelete(target_file, to_delete)
1820
Yifan Hong50e79542018-11-08 17:44:12 -08001821 target_zip = zipfile.ZipFile(target_file, 'a', allowZip64=True)
1822
1823 # Write super_{foo}.img as {foo}.img.
1824 for src, dst in replace.items():
1825 assert src in namelist, \
Tao Bao59cf0c52019-06-25 10:04:24 -07001826 'Missing {} in {}; {} cannot be written'.format(src, input_file, dst)
Yifan Hong50e79542018-11-08 17:44:12 -08001827 unzipped_file = os.path.join(input_tmp, *src.split('/'))
1828 common.ZipWrite(target_zip, unzipped_file, arcname=dst)
1829
Yifan Hongb433eba2019-03-06 12:42:53 -08001830 # Write new ab_partitions.txt file
1831 common.ZipWrite(target_zip, new_ab_partitions, arcname=AB_PARTITIONS)
1832
Yifan Hong50e79542018-11-08 17:44:12 -08001833 common.ZipClose(target_zip)
1834
1835 return target_file
1836
1837
Tao Baof0c4aa22018-04-30 20:29:30 -07001838def GenerateAbOtaPackage(target_file, output_file, source_file=None):
Tao Baofe5b69a2018-03-02 09:47:43 -08001839 """Generates an Android OTA package that has A/B update payload."""
Tao Baodea0f8b2016-06-20 17:55:06 -07001840 # Stage the output zip package for package signing.
Tao Bao491d7e22018-02-21 13:17:22 -08001841 if not OPTIONS.no_signing:
1842 staging_file = common.MakeTempFile(suffix='.zip')
1843 else:
1844 staging_file = output_file
Tao Baoa652c002018-03-01 19:31:38 -08001845 output_zip = zipfile.ZipFile(staging_file, "w",
Tao Baoc098e9e2016-01-07 13:03:56 -08001846 compression=zipfile.ZIP_DEFLATED)
1847
Tao Bao481bab82017-12-21 11:23:09 -08001848 if source_file is not None:
Tao Bao1c320f82019-10-04 23:25:12 -07001849 target_info = common.BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1850 source_info = common.BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Tao Bao481bab82017-12-21 11:23:09 -08001851 else:
Tao Bao1c320f82019-10-04 23:25:12 -07001852 target_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Tao Bao481bab82017-12-21 11:23:09 -08001853 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001854
Tao Bao481bab82017-12-21 11:23:09 -08001855 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001856 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001857
Yifan Hong50e79542018-11-08 17:44:12 -08001858 if OPTIONS.retrofit_dynamic_partitions:
1859 target_file = GetTargetFilesZipForRetrofitDynamicPartitions(
Yifan Hongb433eba2019-03-06 12:42:53 -08001860 target_file, target_info.get("super_block_devices").strip().split(),
1861 target_info.get("dynamic_partition_list").strip().split())
Yifan Hong50e79542018-11-08 17:44:12 -08001862 elif OPTIONS.skip_postinstall:
Tao Bao15a146a2018-02-21 16:06:59 -08001863 target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
1864
Tao Bao40b18822018-01-30 18:19:04 -08001865 # Generate payload.
1866 payload = Payload()
1867
1868 # Enforce a max timestamp this payload can be applied on top of.
Tao Baoff1b86e2017-10-03 14:17:57 -07001869 if OPTIONS.downgrade:
Tao Bao2a12ed72018-01-22 11:35:00 -08001870 max_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baoff1b86e2017-10-03 14:17:57 -07001871 else:
1872 max_timestamp = metadata["post-timestamp"]
Tao Bao40b18822018-01-30 18:19:04 -08001873 additional_args = ["--max_timestamp", max_timestamp]
Tao Baoc098e9e2016-01-07 13:03:56 -08001874
Tao Bao40b18822018-01-30 18:19:04 -08001875 payload.Generate(target_file, source_file, additional_args)
Tao Baoc098e9e2016-01-07 13:03:56 -08001876
Tao Bao40b18822018-01-30 18:19:04 -08001877 # Sign the payload.
Tao Baof7140c02018-01-30 17:09:24 -08001878 payload_signer = PayloadSigner()
1879 payload.Sign(payload_signer)
Tao Baoc098e9e2016-01-07 13:03:56 -08001880
Tao Bao40b18822018-01-30 18:19:04 -08001881 # Write the payload into output zip.
1882 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001883
Tao Baof7140c02018-01-30 17:09:24 -08001884 # Generate and include the secondary payload that installs secondary images
1885 # (e.g. system_other.img).
1886 if OPTIONS.include_secondary:
1887 # We always include a full payload for the secondary slot, even when
1888 # building an incremental OTA. See the comments for "--include_secondary".
Tao Bao15a146a2018-02-21 16:06:59 -08001889 secondary_target_file = GetTargetFilesZipForSecondaryImages(
1890 target_file, OPTIONS.skip_postinstall)
Tao Bao667ff572018-02-10 00:02:40 -08001891 secondary_payload = Payload(secondary=True)
Tao Baodb1fe412018-02-09 23:15:05 -08001892 secondary_payload.Generate(secondary_target_file,
1893 additional_args=additional_args)
Tao Baof7140c02018-01-30 17:09:24 -08001894 secondary_payload.Sign(payload_signer)
Tao Bao667ff572018-02-10 00:02:40 -08001895 secondary_payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08001896
Tianjie Xucfa86222016-03-07 16:31:19 -08001897 # If dm-verity is supported for the device, copy contents of care_map
1898 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001899 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08001900 if (target_info.get("verity") == "true" or
1901 target_info.get("avb_enable") == "true"):
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001902 care_map_list = [x for x in ["care_map.pb", "care_map.txt"] if
1903 "META/" + x in target_zip.namelist()]
1904
1905 # Adds care_map if either the protobuf format or the plain text one exists.
1906 if care_map_list:
1907 care_map_name = care_map_list[0]
1908 care_map_data = target_zip.read("META/" + care_map_name)
1909 # In order to support streaming, care_map needs to be packed as
Tao Bao40b18822018-01-30 18:19:04 -08001910 # ZIP_STORED.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001911 common.ZipWriteStr(output_zip, care_map_name, care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08001912 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001913 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001914 logger.warning("Cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07001915
Tao Bao21803d32017-04-19 10:16:09 -07001916 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001917
Yifan Hong9276cf02019-08-21 16:37:04 -07001918 CheckVintfIfTrebleEnabled(target_file, target_info)
1919
Tao Baofe5b69a2018-03-02 09:47:43 -08001920 # We haven't written the metadata entry yet, which will be handled in
1921 # FinalizeMetadata().
Tao Baoc96316c2017-01-24 22:10:49 -08001922 common.ZipClose(output_zip)
1923
Tao Bao85f16982018-03-08 16:28:33 -08001924 # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers
1925 # all the info of the latter. However, system updaters and OTA servers need to
1926 # take time to switch to the new flag. We keep both of the flags for
1927 # P-timeframe, and will remove StreamingPropertyFiles in later release.
Tao Baod3fc38a2018-03-08 16:09:01 -08001928 needed_property_files = (
Tao Bao85f16982018-03-08 16:28:33 -08001929 AbOtaPropertyFiles(),
Tao Baod3fc38a2018-03-08 16:09:01 -08001930 StreamingPropertyFiles(),
1931 )
1932 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Tao Baoc96316c2017-01-24 22:10:49 -08001933
Tao Baoc098e9e2016-01-07 13:03:56 -08001934
Tao Baof0c4aa22018-04-30 20:29:30 -07001935def GenerateNonAbOtaPackage(target_file, output_file, source_file=None):
1936 """Generates a non-A/B OTA package."""
1937 # Sanity check the loaded info dicts first.
1938 if OPTIONS.info_dict.get("no_recovery") == "true":
1939 raise common.ExternalError(
1940 "--- target build has specified no recovery ---")
1941
1942 # Non-A/B OTAs rely on /cache partition to store temporary files.
1943 cache_size = OPTIONS.info_dict.get("cache_size")
1944 if cache_size is None:
1945 logger.warning("--- can't determine the cache partition size ---")
1946 OPTIONS.cache_size = cache_size
1947
1948 if OPTIONS.extra_script is not None:
1949 with open(OPTIONS.extra_script) as fp:
1950 OPTIONS.extra_script = fp.read()
1951
1952 if OPTIONS.extracted_input is not None:
1953 OPTIONS.input_tmp = OPTIONS.extracted_input
1954 else:
1955 logger.info("unzipping target target-files...")
1956 OPTIONS.input_tmp = common.UnzipTemp(target_file, UNZIP_PATTERN)
1957 OPTIONS.target_tmp = OPTIONS.input_tmp
1958
1959 # If the caller explicitly specified the device-specific extensions path via
1960 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
1961 # is present in the target target_files. Otherwise, take the path of the file
1962 # from 'tool_extensions' in the info dict and look for that in the local
1963 # filesystem, relative to the current directory.
1964 if OPTIONS.device_specific is None:
1965 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1966 if os.path.exists(from_input):
1967 logger.info("(using device-specific extensions from target_files)")
1968 OPTIONS.device_specific = from_input
1969 else:
1970 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
1971
1972 if OPTIONS.device_specific is not None:
1973 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
1974
1975 # Generate a full OTA.
1976 if source_file is None:
1977 with zipfile.ZipFile(target_file) as input_zip:
1978 WriteFullOTAPackage(
1979 input_zip,
1980 output_file)
1981
1982 # Generate an incremental OTA.
1983 else:
1984 logger.info("unzipping source target-files...")
1985 OPTIONS.source_tmp = common.UnzipTemp(
1986 OPTIONS.incremental_source, UNZIP_PATTERN)
1987 with zipfile.ZipFile(target_file) as input_zip, \
1988 zipfile.ZipFile(source_file) as source_zip:
1989 WriteBlockIncrementalOTAPackage(
1990 input_zip,
1991 source_zip,
1992 output_file)
1993
1994
Tianjied6867162020-05-10 14:30:13 -07001995def CalculateRuntimeDevicesAndFingerprints(build_info, boot_variable_values):
1996 """Returns a tuple of sets for runtime devices and fingerprints"""
Tianjie Xu9afb2212020-05-10 21:48:15 +00001997
Tianjied6867162020-05-10 14:30:13 -07001998 device_names = {build_info.device}
Tianjie Xu9afb2212020-05-10 21:48:15 +00001999 fingerprints = {build_info.fingerprint}
2000
Tianjied6867162020-05-10 14:30:13 -07002001 if not boot_variable_values:
2002 return device_names, fingerprints
Tianjie Xu9afb2212020-05-10 21:48:15 +00002003
2004 # Calculate all possible combinations of the values for the boot variables.
Tianjied6867162020-05-10 14:30:13 -07002005 keys = boot_variable_values.keys()
2006 value_list = boot_variable_values.values()
Tianjie Xu9afb2212020-05-10 21:48:15 +00002007 combinations = [dict(zip(keys, values))
2008 for values in itertools.product(*value_list)]
2009 for placeholder_values in combinations:
2010 # Reload the info_dict as some build properties may change their values
2011 # based on the value of ro.boot* properties.
Tianjied6867162020-05-10 14:30:13 -07002012 info_dict = copy.deepcopy(build_info.info_dict)
Tianjie Xu9afb2212020-05-10 21:48:15 +00002013 for partition in common.PARTITIONS_WITH_CARE_MAP:
2014 partition_prop_key = "{}.build.prop".format(partition)
2015 old_props = info_dict[partition_prop_key]
2016 info_dict[partition_prop_key] = common.PartitionBuildProps.FromInputFile(
2017 old_props.input_file, partition, placeholder_values)
2018 info_dict["build.prop"] = info_dict["system.build.prop"]
2019
Tianjied6867162020-05-10 14:30:13 -07002020 new_build_info = common.BuildInfo(info_dict, build_info.oem_dicts)
2021 device_names.add(new_build_info.device)
2022 fingerprints.add(new_build_info.fingerprint)
2023 return device_names, fingerprints
Tianjie Xu9afb2212020-05-10 21:48:15 +00002024
2025
Doug Zongkereef39442009-04-02 12:14:19 -07002026def main(argv):
2027
2028 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07002029 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07002030 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07002031 elif o in ("-i", "--incremental_from"):
2032 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07002033 elif o == "--full_radio":
2034 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07002035 elif o == "--full_bootloader":
2036 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08002037 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002038 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08002039 elif o == "--downgrade":
2040 OPTIONS.downgrade = True
2041 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08002042 elif o == "--override_timestamp":
Tao Baofaa8e0b2018-04-12 14:31:43 -07002043 OPTIONS.downgrade = True
Michael Runge6e836112014-04-15 17:40:21 -07002044 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08002045 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08002046 elif o == "--oem_no_mount":
2047 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07002048 elif o in ("-e", "--extra_script"):
2049 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02002050 elif o in ("-t", "--worker_threads"):
2051 if a.isdigit():
2052 OPTIONS.worker_threads = int(a)
2053 else:
2054 raise ValueError("Cannot parse value %r for option %r - only "
2055 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002056 elif o in ("-2", "--two_step"):
2057 OPTIONS.two_step = True
Tao Baof7140c02018-01-30 17:09:24 -08002058 elif o == "--include_secondary":
2059 OPTIONS.include_secondary = True
Doug Zongker26e66192014-02-20 13:22:07 -08002060 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002061 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07002062 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07002063 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08002064 elif o == "--block":
2065 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002066 elif o in ("-b", "--binary"):
2067 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07002068 elif o == "--stash_threshold":
2069 try:
2070 OPTIONS.stash_threshold = float(a)
2071 except ValueError:
2072 raise ValueError("Cannot parse value %r for option %r - expecting "
2073 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08002074 elif o == "--log_diff":
2075 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002076 elif o == "--payload_signer":
2077 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002078 elif o == "--payload_signer_args":
2079 OPTIONS.payload_signer_args = shlex.split(a)
Tianjie Xu21e6deb2019-10-07 18:01:00 -07002080 elif o == "--payload_signer_maximum_signature_size":
2081 OPTIONS.payload_signer_maximum_signature_size = a
xunchang376cc7c2019-04-08 23:04:58 -07002082 elif o == "--payload_signer_key_size":
Tianjie Xu21e6deb2019-10-07 18:01:00 -07002083 # TODO(Xunchang) remove this option after cleaning up the callers.
2084 logger.warning("The option '--payload_signer_key_size' is deprecated."
2085 " Use '--payload_signer_maximum_signature_size' instead.")
2086 OPTIONS.payload_signer_maximum_signature_size = a
Dan Willemsencea5cd22017-03-21 14:44:27 -07002087 elif o == "--extracted_input_target_files":
2088 OPTIONS.extracted_input = a
Tao Bao15a146a2018-02-21 16:06:59 -08002089 elif o == "--skip_postinstall":
2090 OPTIONS.skip_postinstall = True
Yifan Hong50e79542018-11-08 17:44:12 -08002091 elif o == "--retrofit_dynamic_partitions":
2092 OPTIONS.retrofit_dynamic_partitions = True
xunchangabfa2652019-02-19 16:27:10 -08002093 elif o == "--skip_compatibility_check":
2094 OPTIONS.skip_compatibility_check = True
xunchang1cfe2512019-02-19 14:14:48 -08002095 elif o == "--output_metadata_path":
2096 OPTIONS.output_metadata_path = a
Tianjie Xu1b079832019-08-28 12:19:23 -07002097 elif o == "--disable_fec_computation":
2098 OPTIONS.disable_fec_computation = True
Yifan Hong65afc072020-04-17 10:08:10 -07002099 elif o == "--force_non_ab":
2100 OPTIONS.force_non_ab = True
Tianjied6867162020-05-10 14:30:13 -07002101 elif o == "--boot_variable_file":
2102 OPTIONS.boot_variable_file = a
Doug Zongkereef39442009-04-02 12:14:19 -07002103 else:
2104 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002105 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002106
2107 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08002108 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002109 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07002110 "package_key=",
2111 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002112 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002113 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002114 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08002115 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08002116 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07002117 "extra_script=",
2118 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002119 "two_step",
Tao Baof7140c02018-01-30 17:09:24 -08002120 "include_secondary",
Dan Albert8b72aef2015-03-23 19:13:21 -07002121 "no_signing",
2122 "block",
2123 "binary=",
2124 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08002125 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002126 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07002127 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002128 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002129 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002130 "payload_signer_args=",
Tianjie Xu21e6deb2019-10-07 18:01:00 -07002131 "payload_signer_maximum_signature_size=",
xunchang376cc7c2019-04-08 23:04:58 -07002132 "payload_signer_key_size=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07002133 "extracted_input_target_files=",
Tao Bao15a146a2018-02-21 16:06:59 -08002134 "skip_postinstall",
Yifan Hong50e79542018-11-08 17:44:12 -08002135 "retrofit_dynamic_partitions",
xunchangabfa2652019-02-19 16:27:10 -08002136 "skip_compatibility_check",
xunchang1cfe2512019-02-19 14:14:48 -08002137 "output_metadata_path=",
Tianjie Xu1b079832019-08-28 12:19:23 -07002138 "disable_fec_computation",
Yifan Hong65afc072020-04-17 10:08:10 -07002139 "force_non_ab",
Tianjied6867162020-05-10 14:30:13 -07002140 "boot_variable_file=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002141 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002142
2143 if len(args) != 2:
2144 common.Usage(__doc__)
2145 sys.exit(1)
2146
Tao Bao32fcdab2018-10-12 10:30:39 -07002147 common.InitLogging()
2148
Tao Bao5d182562016-02-23 11:38:39 -08002149 if OPTIONS.downgrade:
Tao Bao5d182562016-02-23 11:38:39 -08002150 # We should only allow downgrading incrementals (as opposed to full).
2151 # Otherwise the device may go back from arbitrary build with this full
2152 # OTA package.
2153 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002154 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08002155
Tao Bao2db13852018-01-08 22:28:57 -08002156 # Load the build info dicts from the zip directly or the extracted input
2157 # directory. We don't need to unzip the entire target-files zips, because they
2158 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
2159 # When loading the info dicts, we don't need to provide the second parameter
2160 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
2161 # some properties with their actual paths, such as 'selinux_fc',
2162 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07002163 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08002164 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07002165 else:
Tao Bao2db13852018-01-08 22:28:57 -08002166 with zipfile.ZipFile(args[0], 'r') as input_zip:
2167 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08002168
Tao Bao32fcdab2018-10-12 10:30:39 -07002169 logger.info("--- target info ---")
2170 common.DumpInfoDict(OPTIONS.info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002171
2172 # Load the source build dict if applicable.
2173 if OPTIONS.incremental_source is not None:
2174 OPTIONS.target_info_dict = OPTIONS.info_dict
2175 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
2176 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2177
Tao Bao32fcdab2018-10-12 10:30:39 -07002178 logger.info("--- source info ---")
2179 common.DumpInfoDict(OPTIONS.source_info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002180
2181 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08002182 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
2183
Yifan Hong50e79542018-11-08 17:44:12 -08002184 # Assume retrofitting dynamic partitions when base build does not set
Yifan Hong50611032018-11-20 14:27:38 -08002185 # use_dynamic_partitions but target build does.
Yifan Hong50e79542018-11-08 17:44:12 -08002186 if (OPTIONS.source_info_dict and
Yifan Hong50611032018-11-20 14:27:38 -08002187 OPTIONS.source_info_dict.get("use_dynamic_partitions") != "true" and
2188 OPTIONS.target_info_dict.get("use_dynamic_partitions") == "true"):
Yifan Hong50e79542018-11-08 17:44:12 -08002189 if OPTIONS.target_info_dict.get("dynamic_partition_retrofit") != "true":
2190 raise common.ExternalError(
2191 "Expect to generate incremental OTA for retrofitting dynamic "
2192 "partitions, but dynamic_partition_retrofit is not set in target "
2193 "build.")
2194 logger.info("Implicitly generating retrofit incremental OTA.")
2195 OPTIONS.retrofit_dynamic_partitions = True
2196
2197 # Skip postinstall for retrofitting dynamic partitions.
2198 if OPTIONS.retrofit_dynamic_partitions:
2199 OPTIONS.skip_postinstall = True
2200
Tao Baoc098e9e2016-01-07 13:03:56 -08002201 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
Yifan Hong65afc072020-04-17 10:08:10 -07002202 allow_non_ab = OPTIONS.info_dict.get("allow_non_ab") == "true"
2203 if OPTIONS.force_non_ab:
2204 assert allow_non_ab, "--force_non_ab only allowed on devices that supports non-A/B"
2205 assert ab_update, "--force_non_ab only allowed on A/B devices"
2206
2207 generate_ab = not OPTIONS.force_non_ab and ab_update
Tao Baoc098e9e2016-01-07 13:03:56 -08002208
Christian Oderf63e2cd2017-05-01 22:30:15 +02002209 # Use the default key to sign the package if not specified with package_key.
2210 # package_keys are needed on ab_updates, so always define them if an
Yifan Hong65afc072020-04-17 10:08:10 -07002211 # A/B update is getting created.
2212 if not OPTIONS.no_signing or generate_ab:
Christian Oderf63e2cd2017-05-01 22:30:15 +02002213 if OPTIONS.package_key is None:
2214 OPTIONS.package_key = OPTIONS.info_dict.get(
2215 "default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -07002216 "build/make/target/product/security/testkey")
Christian Oderf63e2cd2017-05-01 22:30:15 +02002217 # Get signing keys
2218 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
2219
Yifan Hong65afc072020-04-17 10:08:10 -07002220 if generate_ab:
Tao Baof0c4aa22018-04-30 20:29:30 -07002221 GenerateAbOtaPackage(
Tao Baoc098e9e2016-01-07 13:03:56 -08002222 target_file=args[0],
2223 output_file=args[1],
2224 source_file=OPTIONS.incremental_source)
2225
Dan Willemsencea5cd22017-03-21 14:44:27 -07002226 else:
Tao Baof0c4aa22018-04-30 20:29:30 -07002227 GenerateNonAbOtaPackage(
2228 target_file=args[0],
2229 output_file=args[1],
2230 source_file=OPTIONS.incremental_source)
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002231
Tao Baof0c4aa22018-04-30 20:29:30 -07002232 # Post OTA generation works.
2233 if OPTIONS.incremental_source is not None and OPTIONS.log_diff:
2234 logger.info("Generating diff logs...")
2235 logger.info("Unzipping target-files for diffing...")
2236 target_dir = common.UnzipTemp(args[0], TARGET_DIFFING_UNZIP_PATTERN)
2237 source_dir = common.UnzipTemp(
2238 OPTIONS.incremental_source, TARGET_DIFFING_UNZIP_PATTERN)
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002239
Tao Baof0c4aa22018-04-30 20:29:30 -07002240 with open(OPTIONS.log_diff, 'w') as out_file:
2241 import target_files_diff
2242 target_files_diff.recursiveDiff(
2243 '', source_dir, target_dir, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07002244
Tao Bao32fcdab2018-10-12 10:30:39 -07002245 logger.info("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002246
2247
2248if __name__ == '__main__':
2249 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002250 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002251 main(sys.argv[1:])
Tao Bao32fcdab2018-10-12 10:30:39 -07002252 except common.ExternalError:
2253 logger.exception("\n ERROR:\n")
Doug Zongkereef39442009-04-02 12:14:19 -07002254 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002255 finally:
2256 common.Cleanup()