blob: 1e7bb3a94867154de48136f3d5e62bb64ceca5ec [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
Tao Bao30df8b42018-04-23 15:32:53 -070081Non-A/B OTA specific options
82
83 -b (--binary) <file>
84 Use the given binary as the update-binary in the output package, instead
85 of the binary in the build's target_files. Use for development only.
86
87 --block
88 Generate a block-based OTA for non-A/B device. We have deprecated the
89 support for file-based OTA since O. Block-based OTA will be used by
90 default for all non-A/B devices. Keeping this flag here to not break
91 existing callers.
92
93 -e (--extra_script) <file>
94 Insert the contents of file at the end of the update script.
Tao Bao43078aa2015-04-21 14:32:35 -070095
leozwangaa6c1a12015-08-14 10:57:58 -070096 --full_bootloader
97 Similar to --full_radio. When generating an incremental OTA, always
98 include a full copy of bootloader image.
99
Tao Bao30df8b42018-04-23 15:32:53 -0700100 --full_radio
101 When generating an incremental OTA, always include a full copy of radio
102 image. This option is only meaningful when -i is specified, because a full
103 radio is always included in a full OTA if applicable.
Michael Runge63f01de2014-10-28 19:24:19 -0700104
Tao Bao30df8b42018-04-23 15:32:53 -0700105 --log_diff <file>
106 Generate a log file that shows the differences in the source and target
107 builds for an incremental package. This option is only meaningful when -i
108 is specified.
109
110 -o (--oem_settings) <main_file[,additional_files...]>
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800111 Comma seperated list of files used to specify the expected OEM-specific
Tao Bao481bab82017-12-21 11:23:09 -0800112 properties on the OEM partition of the intended device. Multiple expected
113 values can be used by providing multiple files. Only the first dict will
114 be used to compute fingerprint, while the rest will be used to assert
115 OEM-specific properties.
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800116
Tao Bao8608cde2016-02-25 19:49:55 -0800117 --oem_no_mount
Tao Bao30df8b42018-04-23 15:32:53 -0700118 For devices with OEM-specific properties but without an OEM partition, do
119 not mount the OEM partition in the updater-script. This should be very
120 rarely used, since it's expected to have a dedicated OEM partition for
121 OEM-specific properties. Only meaningful when -o is specified.
Tao Bao8608cde2016-02-25 19:49:55 -0800122
Tao Bao30df8b42018-04-23 15:32:53 -0700123 --stash_threshold <float>
124 Specify the threshold that will be used to compute the maximum allowed
125 stash size (defaults to 0.8).
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700126
Tao Bao30df8b42018-04-23 15:32:53 -0700127 -t (--worker_threads) <int>
128 Specify the number of worker-threads that will be used when generating
129 patches for incremental updates (defaults to 3).
Tao Bao3e6161a2017-02-28 11:48:48 -0800130
Tao Bao30df8b42018-04-23 15:32:53 -0700131 --verify
132 Verify the checksums of the updated system and vendor (if any) partitions.
133 Non-A/B incremental OTAs only.
Doug Zongker1c390a22009-05-14 19:06:36 -0700134
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800135 -2 (--two_step)
Tao Bao30df8b42018-04-23 15:32:53 -0700136 Generate a 'two-step' OTA package, where recovery is updated first, so
137 that any changes made to the system partition are done using the new
138 recovery (new kernel, etc.).
139
140A/B OTA specific options
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800141
Tianjie Xu1b079832019-08-28 12:19:23 -0700142 --disable_fec_computation
143 Disable the on device FEC data computation for incremental updates.
144
Tao Baof7140c02018-01-30 17:09:24 -0800145 --include_secondary
146 Additionally include the payload for secondary slot images (default:
147 False). Only meaningful when generating A/B OTAs.
148
149 By default, an A/B OTA package doesn't contain the images for the
150 secondary slot (e.g. system_other.img). Specifying this flag allows
151 generating a separate payload that will install secondary slot images.
152
153 Such a package needs to be applied in a two-stage manner, with a reboot
154 in-between. During the first stage, the updater applies the primary
155 payload only. Upon finishing, it reboots the device into the newly updated
156 slot. It then continues to install the secondary payload to the inactive
157 slot, but without switching the active slot at the end (needs the matching
158 support in update_engine, i.e. SWITCH_SLOT_ON_REBOOT flag).
159
160 Due to the special install procedure, the secondary payload will be always
161 generated as a full payload.
162
Tao Baodea0f8b2016-06-20 17:55:06 -0700163 --payload_signer <signer>
164 Specify the signer when signing the payload and metadata for A/B OTAs.
165 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
166 with the package private key. If the private key cannot be accessed
167 directly, a payload signer that knows how to do that should be specified.
168 The signer will be supplied with "-inkey <path_to_key>",
169 "-in <input_file>" and "-out <output_file>" parameters.
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700170
171 --payload_signer_args <args>
172 Specify the arguments needed for payload signer.
Tao Bao15a146a2018-02-21 16:06:59 -0800173
xunchang376cc7c2019-04-08 23:04:58 -0700174 --payload_signer_key_size <key_size>
175 Specify the key size in bytes of the payload signer.
176
Tao Bao15a146a2018-02-21 16:06:59 -0800177 --skip_postinstall
178 Skip the postinstall hooks when generating an A/B OTA package (default:
179 False). Note that this discards ALL the hooks, including non-optional
180 ones. Should only be used if caller knows it's safe to do so (e.g. all the
181 postinstall work is to dexopt apps and a data wipe will happen immediately
182 after). Only meaningful when generating A/B OTAs.
Doug Zongkereef39442009-04-02 12:14:19 -0700183"""
184
Tao Bao89fbb0f2017-01-10 10:47:58 -0800185from __future__ import print_function
186
Tianjie Xuf67dd802019-05-20 17:50:36 -0700187import collections
Tao Bao32fcdab2018-10-12 10:30:39 -0700188import logging
Doug Zongkerfc44a512014-08-26 13:10:25 -0700189import multiprocessing
Tao Bao2dd1c482017-02-03 16:49:39 -0800190import os.path
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700191import shlex
Tao Bao15a146a2018-02-21 16:06:59 -0800192import shutil
Tao Bao85f16982018-03-08 16:28:33 -0800193import struct
Tao Bao481bab82017-12-21 11:23:09 -0800194import sys
Doug Zongkereef39442009-04-02 12:14:19 -0700195import zipfile
196
Yifan Hong9276cf02019-08-21 16:37:04 -0700197import check_target_files_vintf
Doug Zongkereef39442009-04-02 12:14:19 -0700198import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700199import edify_generator
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700200import verity_utils
Doug Zongkereef39442009-04-02 12:14:19 -0700201
Tao Bao481bab82017-12-21 11:23:09 -0800202if sys.hexversion < 0x02070000:
203 print("Python 2.7 or newer is required.", file=sys.stderr)
204 sys.exit(1)
205
Tao Bao32fcdab2018-10-12 10:30:39 -0700206logger = logging.getLogger(__name__)
Tao Bao481bab82017-12-21 11:23:09 -0800207
Doug Zongkereef39442009-04-02 12:14:19 -0700208OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700209OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700210OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700211OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700212OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700213OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800214OPTIONS.downgrade = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700215OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700216OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
217if OPTIONS.worker_threads == 0:
218 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800219OPTIONS.two_step = False
Tao Baof7140c02018-01-30 17:09:24 -0800220OPTIONS.include_secondary = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900221OPTIONS.no_signing = False
Tao Bao457cbf62017-03-06 09:56:01 -0800222OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -0800223OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700224OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800225OPTIONS.oem_no_mount = False
Tao Bao43078aa2015-04-21 14:32:35 -0700226OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700227OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700228# Stash size cannot exceed cache_size * threshold.
229OPTIONS.cache_size = None
230OPTIONS.stash_threshold = 0.8
Tao Baod62c6032015-11-30 09:40:20 -0800231OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700232OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700233OPTIONS.payload_signer_args = []
xunchang376cc7c2019-04-08 23:04:58 -0700234OPTIONS.payload_signer_key_size = None
Tao Bao5f8ff932017-03-21 22:35:00 -0700235OPTIONS.extracted_input = None
Christian Oderf63e2cd2017-05-01 22:30:15 +0200236OPTIONS.key_passwords = []
Tao Bao15a146a2018-02-21 16:06:59 -0800237OPTIONS.skip_postinstall = False
Yifan Hong50e79542018-11-08 17:44:12 -0800238OPTIONS.retrofit_dynamic_partitions = False
xunchangabfa2652019-02-19 16:27:10 -0800239OPTIONS.skip_compatibility_check = False
xunchang1cfe2512019-02-19 14:14:48 -0800240OPTIONS.output_metadata_path = None
Tianjie Xu1b079832019-08-28 12:19:23 -0700241OPTIONS.disable_fec_computation = False
Tao Bao15a146a2018-02-21 16:06:59 -0800242
Tao Bao8dcf7382015-05-21 14:09:49 -0700243
Tao Bao2dd1c482017-02-03 16:49:39 -0800244METADATA_NAME = 'META-INF/com/android/metadata'
Tao Bao15a146a2018-02-21 16:06:59 -0800245POSTINSTALL_CONFIG = 'META/postinstall_config.txt'
Yifan Hong50e79542018-11-08 17:44:12 -0800246DYNAMIC_PARTITION_INFO = 'META/dynamic_partitions_info.txt'
Yifan Hongb433eba2019-03-06 12:42:53 -0800247AB_PARTITIONS = 'META/ab_partitions.txt'
Tao Bao04808502019-07-25 23:11:41 -0700248UNZIP_PATTERN = ['IMAGES/*', 'META/*', 'OTA/*', 'RADIO/*']
Tao Baof0c4aa22018-04-30 20:29:30 -0700249# Files to be unzipped for target diffing purpose.
250TARGET_DIFFING_UNZIP_PATTERN = ['BOOT', 'RECOVERY', 'SYSTEM/*', 'VENDOR/*',
251 'PRODUCT/*', 'SYSTEM_EXT/*', 'ODM/*']
Yifan Hongb433eba2019-03-06 12:42:53 -0800252RETROFIT_DAP_UNZIP_PATTERN = ['OTA/super_*.img', AB_PARTITIONS]
Tao Bao3e759462019-09-17 22:43:11 -0700253
254# Images to be excluded from secondary payload. We essentially only keep
255# 'system_other' and bootloader partitions.
256SECONDARY_PAYLOAD_SKIPPED_IMAGES = [
257 'boot', 'dtbo', 'modem', 'odm', 'product', 'radio', 'recovery',
258 'system_ext', 'vbmeta', 'vbmeta_system', 'vbmeta_vendor', 'vendor']
Tao Bao6b0b2f92017-03-05 11:38:11 -0800259
Tao Bao2dd1c482017-02-03 16:49:39 -0800260
Tao Baofabe0832018-01-17 15:52:28 -0800261class PayloadSigner(object):
262 """A class that wraps the payload signing works.
263
264 When generating a Payload, hashes of the payload and metadata files will be
265 signed with the device key, either by calling an external payload signer or
266 by calling openssl with the package key. This class provides a unified
267 interface, so that callers can just call PayloadSigner.Sign().
268
269 If an external payload signer has been specified (OPTIONS.payload_signer), it
270 calls the signer with the provided args (OPTIONS.payload_signer_args). Note
271 that the signing key should be provided as part of the payload_signer_args.
272 Otherwise without an external signer, it uses the package key
273 (OPTIONS.package_key) and calls openssl for the signing works.
274 """
275
276 def __init__(self):
277 if OPTIONS.payload_signer is None:
278 # Prepare the payload signing key.
279 private_key = OPTIONS.package_key + OPTIONS.private_key_suffix
280 pw = OPTIONS.key_passwords[OPTIONS.package_key]
281
282 cmd = ["openssl", "pkcs8", "-in", private_key, "-inform", "DER"]
283 cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"])
284 signing_key = common.MakeTempFile(prefix="key-", suffix=".key")
285 cmd.extend(["-out", signing_key])
Tao Baobec89c12018-10-15 11:53:28 -0700286 common.RunAndCheckOutput(cmd, verbose=False)
Tao Baofabe0832018-01-17 15:52:28 -0800287
288 self.signer = "openssl"
289 self.signer_args = ["pkeyutl", "-sign", "-inkey", signing_key,
290 "-pkeyopt", "digest:sha256"]
xunchang376cc7c2019-04-08 23:04:58 -0700291 self.key_size = self._GetKeySizeInBytes(signing_key)
Tao Baofabe0832018-01-17 15:52:28 -0800292 else:
293 self.signer = OPTIONS.payload_signer
294 self.signer_args = OPTIONS.payload_signer_args
xunchang376cc7c2019-04-08 23:04:58 -0700295 if OPTIONS.payload_signer_key_size:
296 self.key_size = int(OPTIONS.payload_signer_key_size)
297 assert self.key_size == 256 or self.key_size == 512, \
298 "Unsupported key size {}".format(OPTIONS.payload_signer_key_size)
299 else:
300 self.key_size = 256
301
302 @staticmethod
303 def _GetKeySizeInBytes(signing_key):
304 modulus_file = common.MakeTempFile(prefix="modulus-")
305 cmd = ["openssl", "rsa", "-inform", "PEM", "-in", signing_key, "-modulus",
306 "-noout", "-out", modulus_file]
307 common.RunAndCheckOutput(cmd, verbose=False)
308
309 with open(modulus_file) as f:
310 modulus_string = f.read()
311 # The modulus string has the format "Modulus=$data", where $data is the
312 # concatenation of hex dump of the modulus.
313 MODULUS_PREFIX = "Modulus="
314 assert modulus_string.startswith(MODULUS_PREFIX)
315 modulus_string = modulus_string[len(MODULUS_PREFIX):]
Tao Bao59cf0c52019-06-25 10:04:24 -0700316 key_size = len(modulus_string) // 2
xunchang376cc7c2019-04-08 23:04:58 -0700317 assert key_size == 256 or key_size == 512, \
318 "Unsupported key size {}".format(key_size)
319 return key_size
Tao Baofabe0832018-01-17 15:52:28 -0800320
321 def Sign(self, in_file):
322 """Signs the given input file. Returns the output filename."""
323 out_file = common.MakeTempFile(prefix="signed-", suffix=".bin")
324 cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file]
Tao Bao718faed2019-08-02 13:24:19 -0700325 common.RunAndCheckOutput(cmd)
Tao Baofabe0832018-01-17 15:52:28 -0800326 return out_file
327
328
Tao Bao40b18822018-01-30 18:19:04 -0800329class Payload(object):
330 """Manages the creation and the signing of an A/B OTA Payload."""
331
332 PAYLOAD_BIN = 'payload.bin'
333 PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
Tao Baof7140c02018-01-30 17:09:24 -0800334 SECONDARY_PAYLOAD_BIN = 'secondary/payload.bin'
335 SECONDARY_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt'
Tao Bao40b18822018-01-30 18:19:04 -0800336
Tao Bao667ff572018-02-10 00:02:40 -0800337 def __init__(self, secondary=False):
338 """Initializes a Payload instance.
339
340 Args:
341 secondary: Whether it's generating a secondary payload (default: False).
342 """
Tao Bao40b18822018-01-30 18:19:04 -0800343 self.payload_file = None
344 self.payload_properties = None
Tao Bao667ff572018-02-10 00:02:40 -0800345 self.secondary = secondary
Tao Bao40b18822018-01-30 18:19:04 -0800346
Tao Baof0c4aa22018-04-30 20:29:30 -0700347 def _Run(self, cmd): # pylint: disable=no-self-use
Tao Bao718faed2019-08-02 13:24:19 -0700348 # Don't pipe (buffer) the output if verbose is set. Let
349 # brillo_update_payload write to stdout/stderr directly, so its progress can
350 # be monitored.
351 if OPTIONS.verbose:
352 common.RunAndCheckOutput(cmd, stdout=None, stderr=None)
353 else:
354 common.RunAndCheckOutput(cmd)
355
Tao Bao40b18822018-01-30 18:19:04 -0800356 def Generate(self, target_file, source_file=None, additional_args=None):
357 """Generates a payload from the given target-files zip(s).
358
359 Args:
360 target_file: The filename of the target build target-files zip.
361 source_file: The filename of the source build target-files zip; or None if
362 generating a full OTA.
363 additional_args: A list of additional args that should be passed to
364 brillo_update_payload script; or None.
365 """
366 if additional_args is None:
367 additional_args = []
368
369 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
370 cmd = ["brillo_update_payload", "generate",
371 "--payload", payload_file,
372 "--target_image", target_file]
373 if source_file is not None:
374 cmd.extend(["--source_image", source_file])
Tianjie Xu1b079832019-08-28 12:19:23 -0700375 if OPTIONS.disable_fec_computation:
376 cmd.extend(["--disable_fec_computation", "true"])
Tao Bao40b18822018-01-30 18:19:04 -0800377 cmd.extend(additional_args)
Tao Bao718faed2019-08-02 13:24:19 -0700378 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800379
380 self.payload_file = payload_file
381 self.payload_properties = None
382
383 def Sign(self, payload_signer):
384 """Generates and signs the hashes of the payload and metadata.
385
386 Args:
387 payload_signer: A PayloadSigner() instance that serves the signing work.
388
389 Raises:
390 AssertionError: On any failure when calling brillo_update_payload script.
391 """
392 assert isinstance(payload_signer, PayloadSigner)
393
394 # 1. Generate hashes of the payload and metadata files.
395 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
396 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
397 cmd = ["brillo_update_payload", "hash",
398 "--unsigned_payload", self.payload_file,
xunchang376cc7c2019-04-08 23:04:58 -0700399 "--signature_size", str(payload_signer.key_size),
Tao Bao40b18822018-01-30 18:19:04 -0800400 "--metadata_hash_file", metadata_sig_file,
401 "--payload_hash_file", payload_sig_file]
Tao Bao718faed2019-08-02 13:24:19 -0700402 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800403
404 # 2. Sign the hashes.
405 signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
406 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
407
408 # 3. Insert the signatures back into the payload file.
409 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
410 suffix=".bin")
411 cmd = ["brillo_update_payload", "sign",
412 "--unsigned_payload", self.payload_file,
413 "--payload", signed_payload_file,
xunchang376cc7c2019-04-08 23:04:58 -0700414 "--signature_size", str(payload_signer.key_size),
Tao Bao40b18822018-01-30 18:19:04 -0800415 "--metadata_signature_file", signed_metadata_sig_file,
416 "--payload_signature_file", signed_payload_sig_file]
Tao Bao718faed2019-08-02 13:24:19 -0700417 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800418
419 # 4. Dump the signed payload properties.
420 properties_file = common.MakeTempFile(prefix="payload-properties-",
421 suffix=".txt")
422 cmd = ["brillo_update_payload", "properties",
423 "--payload", signed_payload_file,
424 "--properties_file", properties_file]
Tao Bao718faed2019-08-02 13:24:19 -0700425 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800426
Tao Bao667ff572018-02-10 00:02:40 -0800427 if self.secondary:
428 with open(properties_file, "a") as f:
429 f.write("SWITCH_SLOT_ON_REBOOT=0\n")
430
Tao Bao40b18822018-01-30 18:19:04 -0800431 if OPTIONS.wipe_user_data:
432 with open(properties_file, "a") as f:
433 f.write("POWERWASH=1\n")
434
435 self.payload_file = signed_payload_file
436 self.payload_properties = properties_file
437
Tao Bao667ff572018-02-10 00:02:40 -0800438 def WriteToZip(self, output_zip):
Tao Bao40b18822018-01-30 18:19:04 -0800439 """Writes the payload to the given zip.
440
441 Args:
442 output_zip: The output ZipFile instance.
443 """
444 assert self.payload_file is not None
445 assert self.payload_properties is not None
446
Tao Bao667ff572018-02-10 00:02:40 -0800447 if self.secondary:
Tao Baof7140c02018-01-30 17:09:24 -0800448 payload_arcname = Payload.SECONDARY_PAYLOAD_BIN
449 payload_properties_arcname = Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT
450 else:
451 payload_arcname = Payload.PAYLOAD_BIN
452 payload_properties_arcname = Payload.PAYLOAD_PROPERTIES_TXT
453
Tao Bao40b18822018-01-30 18:19:04 -0800454 # Add the signed payload file and properties into the zip. In order to
455 # support streaming, we pack them as ZIP_STORED. So these entries can be
456 # read directly with the offset and length pairs.
Tao Baof7140c02018-01-30 17:09:24 -0800457 common.ZipWrite(output_zip, self.payload_file, arcname=payload_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800458 compress_type=zipfile.ZIP_STORED)
459 common.ZipWrite(output_zip, self.payload_properties,
Tao Baof7140c02018-01-30 17:09:24 -0800460 arcname=payload_properties_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800461 compress_type=zipfile.ZIP_STORED)
462
463
Doug Zongkereef39442009-04-02 12:14:19 -0700464def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200465 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700466
Doug Zongker951495f2009-08-14 12:44:19 -0700467 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
468 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700469
470
Tao Bao481bab82017-12-21 11:23:09 -0800471def _LoadOemDicts(oem_source):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800472 """Returns the list of loaded OEM properties dict."""
Tao Bao481bab82017-12-21 11:23:09 -0800473 if not oem_source:
474 return None
475
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800476 oem_dicts = []
Tao Bao481bab82017-12-21 11:23:09 -0800477 for oem_file in oem_source:
478 with open(oem_file) as fp:
479 oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines()))
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800480 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700481
Doug Zongkereef39442009-04-02 12:14:19 -0700482
Tao Baod42e97e2016-11-30 12:11:57 -0800483def _WriteRecoveryImageToBoot(script, output_zip):
484 """Find and write recovery image to /boot in two-step OTA.
485
486 In two-step OTAs, we write recovery image to /boot as the first step so that
487 we can reboot to there and install a new recovery image to /recovery.
488 A special "recovery-two-step.img" will be preferred, which encodes the correct
489 path of "/boot". Otherwise the device may show "device is corrupt" message
490 when booting into /boot.
491
492 Fall back to using the regular recovery.img if the two-step recovery image
493 doesn't exist. Note that rebuilding the special image at this point may be
494 infeasible, because we don't have the desired boot signer and keys when
495 calling ota_from_target_files.py.
496 """
497
498 recovery_two_step_img_name = "recovery-two-step.img"
499 recovery_two_step_img_path = os.path.join(
Tao Bao04808502019-07-25 23:11:41 -0700500 OPTIONS.input_tmp, "OTA", recovery_two_step_img_name)
Tao Baod42e97e2016-11-30 12:11:57 -0800501 if os.path.exists(recovery_two_step_img_path):
Tao Bao04808502019-07-25 23:11:41 -0700502 common.ZipWrite(
503 output_zip,
504 recovery_two_step_img_path,
505 arcname=recovery_two_step_img_name)
Tao Bao32fcdab2018-10-12 10:30:39 -0700506 logger.info(
507 "two-step package: using %s in stage 1/3", recovery_two_step_img_name)
Tao Baod42e97e2016-11-30 12:11:57 -0800508 script.WriteRawImage("/boot", recovery_two_step_img_name)
509 else:
Tao Bao32fcdab2018-10-12 10:30:39 -0700510 logger.info("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800511 # The "recovery.img" entry has been written into package earlier.
512 script.WriteRawImage("/boot", "recovery.img")
513
514
Bill Peckhame868aec2019-09-17 17:06:47 -0700515def HasRecoveryPatch(target_files_zip, info_dict):
516 board_uses_vendorimage = info_dict.get("board_uses_vendorimage") == "true"
517
518 if board_uses_vendorimage:
519 target_files_dir = "VENDOR"
520 else:
521 target_files_dir = "SYSTEM/vendor"
522
523 patch = "%s/recovery-from-boot.p" % target_files_dir
524 img = "%s/etc/recovery.img" %target_files_dir
525
Tao Baof2cffbd2015-07-22 12:33:18 -0700526 namelist = [name for name in target_files_zip.namelist()]
Bill Peckhame868aec2019-09-17 17:06:47 -0700527 return (patch in namelist or img in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700528
Tao Bao457cbf62017-03-06 09:56:01 -0800529
Yifan Hong51d37562019-04-23 17:06:46 -0700530def HasPartition(target_files_zip, partition):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700531 try:
Yifan Hong51d37562019-04-23 17:06:46 -0700532 target_files_zip.getinfo(partition.upper() + "/")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700533 return True
534 except KeyError:
535 return False
536
Tao Bao457cbf62017-03-06 09:56:01 -0800537
Yifan Hong9276cf02019-08-21 16:37:04 -0700538def HasTrebleEnabled(target_files, target_info):
539 def HasVendorPartition(target_files):
540 if os.path.isdir(target_files):
541 return os.path.isdir(os.path.join(target_files, "VENDOR"))
542 if zipfile.is_zipfile(target_files):
543 return HasPartition(zipfile.ZipFile(target_files), "vendor")
544 raise ValueError("Unknown target_files argument")
Yifan Hong51d37562019-04-23 17:06:46 -0700545
Yifan Hong9276cf02019-08-21 16:37:04 -0700546 return (HasVendorPartition(target_files) and
Tao Bao481bab82017-12-21 11:23:09 -0800547 target_info.GetBuildProp("ro.treble.enabled") == "true")
Tao Baobcd1d162017-08-26 13:10:26 -0700548
549
Tao Bao481bab82017-12-21 11:23:09 -0800550def WriteFingerprintAssertion(script, target_info, source_info):
551 source_oem_props = source_info.oem_props
552 target_oem_props = target_info.oem_props
Michael Runge6e836112014-04-15 17:40:21 -0700553
Tao Bao481bab82017-12-21 11:23:09 -0800554 if source_oem_props is None and target_oem_props is None:
555 script.AssertSomeFingerprint(
556 source_info.fingerprint, target_info.fingerprint)
557 elif source_oem_props is not None and target_oem_props is not None:
558 script.AssertSomeThumbprint(
559 target_info.GetBuildProp("ro.build.thumbprint"),
560 source_info.GetBuildProp("ro.build.thumbprint"))
561 elif source_oem_props is None and target_oem_props is not None:
562 script.AssertFingerprintOrThumbprint(
563 source_info.fingerprint,
564 target_info.GetBuildProp("ro.build.thumbprint"))
565 else:
566 script.AssertFingerprintOrThumbprint(
567 target_info.fingerprint,
568 source_info.GetBuildProp("ro.build.thumbprint"))
Doug Zongker73ef8252009-07-23 15:12:53 -0700569
Doug Zongkerfc44a512014-08-26 13:10:25 -0700570
Yifan Hong9276cf02019-08-21 16:37:04 -0700571def CheckVintfIfTrebleEnabled(target_files, target_info):
572 """Checks compatibility info of the input target files.
Tao Bao21803d32017-04-19 10:16:09 -0700573
Yifan Hong9276cf02019-08-21 16:37:04 -0700574 Metadata used for compatibility verification is retrieved from target_zip.
Tao Bao21803d32017-04-19 10:16:09 -0700575
Yifan Hong9276cf02019-08-21 16:37:04 -0700576 Compatibility should only be checked for devices that have enabled
Tao Baobcd1d162017-08-26 13:10:26 -0700577 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700578
579 Args:
Yifan Hong9276cf02019-08-21 16:37:04 -0700580 target_files: Path to zip file containing the source files to be included
581 for OTA. Can also be the path to extracted directory.
Tao Bao481bab82017-12-21 11:23:09 -0800582 target_info: The BuildInfo instance that holds the target build info.
Tao Bao21803d32017-04-19 10:16:09 -0700583 """
584
Tao Baobcd1d162017-08-26 13:10:26 -0700585 # Will only proceed if the target has enabled the Treble support (as well as
586 # having a /vendor partition).
Yifan Hong9276cf02019-08-21 16:37:04 -0700587 if not HasTrebleEnabled(target_files, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700588 return
589
xunchangabfa2652019-02-19 16:27:10 -0800590 # Skip adding the compatibility package as a workaround for b/114240221. The
591 # compatibility will always fail on devices without qualified kernels.
592 if OPTIONS.skip_compatibility_check:
593 return
594
Yifan Hong9276cf02019-08-21 16:37:04 -0700595 if not check_target_files_vintf.CheckVintf(target_files, target_info):
596 raise RuntimeError("VINTF compatibility check failed")
Tao Bao21803d32017-04-19 10:16:09 -0700597
598
Tianjie Xuf67dd802019-05-20 17:50:36 -0700599def GetBlockDifferences(target_zip, source_zip, target_info, source_info,
600 device_specific):
601 """Returns a ordered dict of block differences with partition name as key."""
602
603 def GetIncrementalBlockDifferenceForPartition(name):
604 if not HasPartition(source_zip, name):
605 raise RuntimeError("can't generate incremental that adds {}".format(name))
606
607 partition_src = common.GetUserImage(name, OPTIONS.source_tmp, source_zip,
608 info_dict=source_info,
609 allow_shared_blocks=allow_shared_blocks)
610
611 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
612 name, 4096, target_info)
613 partition_tgt = common.GetUserImage(name, OPTIONS.target_tmp, target_zip,
614 info_dict=target_info,
615 allow_shared_blocks=allow_shared_blocks,
616 hashtree_info_generator=
617 hashtree_info_generator)
618
619 # Check the first block of the source system partition for remount R/W only
620 # if the filesystem is ext4.
621 partition_source_info = source_info["fstab"]["/" + name]
622 check_first_block = partition_source_info.fs_type == "ext4"
623 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
624 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
625 # b) the blocks listed in block map may not contain all the bytes for a
626 # given file (because they're rounded to be 4K-aligned).
627 partition_target_info = target_info["fstab"]["/" + name]
628 disable_imgdiff = (partition_source_info.fs_type == "squashfs" or
629 partition_target_info.fs_type == "squashfs")
630 return common.BlockDifference(name, partition_src, partition_tgt,
631 check_first_block,
632 version=blockimgdiff_version,
633 disable_imgdiff=disable_imgdiff)
634
635 if source_zip:
636 # See notes in common.GetUserImage()
637 allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or
638 target_info.get('ext4_share_dup_blocks') == "true")
639 blockimgdiff_version = max(
640 int(i) for i in target_info.get(
641 "blockimgdiff_versions", "1").split(","))
642 assert blockimgdiff_version >= 3
643
644 block_diff_dict = collections.OrderedDict()
645 partition_names = ["system", "vendor", "product", "odm", "system_ext"]
646 for partition in partition_names:
647 if not HasPartition(target_zip, partition):
648 continue
649 # Full OTA update.
650 if not source_zip:
651 tgt = common.GetUserImage(partition, OPTIONS.input_tmp, target_zip,
652 info_dict=target_info,
653 reset_file_map=True)
654 block_diff_dict[partition] = common.BlockDifference(partition, tgt,
655 src=None)
656 # Incremental OTA update.
657 else:
658 block_diff_dict[partition] = GetIncrementalBlockDifferenceForPartition(
659 partition)
660 assert "system" in block_diff_dict
661
662 # Get the block diffs from the device specific script. If there is a
663 # duplicate block diff for a partition, ignore the diff in the generic script
664 # and use the one in the device specific script instead.
665 if source_zip:
666 device_specific_diffs = device_specific.IncrementalOTA_GetBlockDifferences()
667 function_name = "IncrementalOTA_GetBlockDifferences"
668 else:
669 device_specific_diffs = device_specific.FullOTA_GetBlockDifferences()
670 function_name = "FullOTA_GetBlockDifferences"
671
672 if device_specific_diffs:
673 assert all(isinstance(diff, common.BlockDifference)
674 for diff in device_specific_diffs), \
675 "{} is not returning a list of BlockDifference objects".format(
676 function_name)
677 for diff in device_specific_diffs:
678 if diff.partition in block_diff_dict:
679 logger.warning("Duplicate block difference found. Device specific block"
680 " diff for partition '%s' overrides the one in generic"
681 " script.", diff.partition)
682 block_diff_dict[diff.partition] = diff
683
684 return block_diff_dict
685
686
Tao Bao491d7e22018-02-21 13:17:22 -0800687def WriteFullOTAPackage(input_zip, output_file):
Tao Bao1c320f82019-10-04 23:25:12 -0700688 target_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700689
Tao Bao481bab82017-12-21 11:23:09 -0800690 # We don't know what version it will be installed on top of. We expect the API
691 # just won't change very often. Similarly for fstab, it might have changed in
692 # the target build.
693 target_api_version = target_info["recovery_api_version"]
694 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700695
Tao Bao481bab82017-12-21 11:23:09 -0800696 if target_info.oem_props and not OPTIONS.oem_no_mount:
697 target_info.WriteMountOemScript(script)
698
Tao Baodf3a48b2018-01-10 16:30:43 -0800699 metadata = GetPackageMetadata(target_info)
Doug Zongker2ea21062010-04-28 16:05:21 -0700700
Tao Bao491d7e22018-02-21 13:17:22 -0800701 if not OPTIONS.no_signing:
702 staging_file = common.MakeTempFile(suffix='.zip')
703 else:
704 staging_file = output_file
705
706 output_zip = zipfile.ZipFile(
707 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
708
Doug Zongker05d3dea2009-06-22 11:32:31 -0700709 device_specific = common.DeviceSpecificParams(
710 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800711 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700712 output_zip=output_zip,
713 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700714 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700715 metadata=metadata,
716 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700717
Bill Peckhame868aec2019-09-17 17:06:47 -0700718 assert HasRecoveryPatch(input_zip, info_dict=OPTIONS.info_dict)
Doug Zongkerc9253822014-02-04 12:17:58 -0800719
Tao Bao481bab82017-12-21 11:23:09 -0800720 # Assertions (e.g. downgrade check, device properties check).
721 ts = target_info.GetBuildProp("ro.build.date.utc")
722 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700723 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700724
Tao Bao481bab82017-12-21 11:23:09 -0800725 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700726 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800727
Tianjie Xuf67dd802019-05-20 17:50:36 -0700728 block_diff_dict = GetBlockDifferences(target_zip=input_zip, source_zip=None,
729 target_info=target_info,
730 source_info=None,
731 device_specific=device_specific)
732
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800733 # Two-step package strategy (in chronological order, which is *not*
734 # the order in which the generated script has things):
735 #
736 # if stage is not "2/3" or "3/3":
737 # write recovery image to boot partition
738 # set stage to "2/3"
739 # reboot to boot partition and restart recovery
740 # else if stage is "2/3":
741 # write recovery image to recovery partition
742 # set stage to "3/3"
743 # reboot to recovery partition and restart recovery
744 # else:
745 # (stage must be "3/3")
746 # set stage to ""
747 # do normal full package installation:
748 # wipe and install system, boot image, etc.
749 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700750 # complete script normally
751 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800752
753 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
754 OPTIONS.input_tmp, "RECOVERY")
755 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800756 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800757 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800758 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800759 assert fs.fs_type.upper() == "EMMC", \
760 "two-step packages only supported on devices with EMMC /misc partitions"
761 bcb_dev = {"bcb_dev": fs.device}
762 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
763 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700764if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800765""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800766
767 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
768 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800769 script.WriteRawImage("/recovery", "recovery.img")
770 script.AppendExtra("""
771set_stage("%(bcb_dev)s", "3/3");
772reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700773else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800774""" % bcb_dev)
775
Tao Baod42e97e2016-11-30 12:11:57 -0800776 # Stage 3/3: Make changes.
777 script.Comment("Stage 3/3")
778
Tao Bao6c55a8a2015-04-08 15:30:27 -0700779 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800780 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700781
Doug Zongkere5ff5902012-01-17 10:55:37 -0800782 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700783
Tianjie Xuf67dd802019-05-20 17:50:36 -0700784 # All other partitions as well as the data wipe use 10% of the progress, and
785 # the update of the system partition takes the remaining progress.
786 system_progress = 0.9 - (len(block_diff_dict) - 1) * 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700787 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800788 system_progress -= 0.1
Tianjie Xuf67dd802019-05-20 17:50:36 -0700789 progress_dict = {partition: 0.1 for partition in block_diff_dict}
790 progress_dict["system"] = system_progress
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700791
Yifan Hong10c530d2018-12-27 17:34:18 -0800792 if target_info.get('use_dynamic_partitions') == "true":
793 # Use empty source_info_dict to indicate that all partitions / groups must
794 # be re-added.
795 dynamic_partitions_diff = common.DynamicPartitionsDifference(
796 info_dict=OPTIONS.info_dict,
Tianjie Xuf67dd802019-05-20 17:50:36 -0700797 block_diffs=block_diff_dict.values(),
Yifan Hong10c530d2018-12-27 17:34:18 -0800798 progress_dict=progress_dict)
799 dynamic_partitions_diff.WriteScript(script, output_zip,
800 write_verify_script=OPTIONS.verify)
801 else:
Tianjie Xuf67dd802019-05-20 17:50:36 -0700802 for block_diff in block_diff_dict.values():
Yifan Hong10c530d2018-12-27 17:34:18 -0800803 block_diff.WriteScript(script, output_zip,
804 progress=progress_dict.get(block_diff.partition),
805 write_verify_script=OPTIONS.verify)
Doug Zongker73ef8252009-07-23 15:12:53 -0700806
Yifan Hong9276cf02019-08-21 16:37:04 -0700807 CheckVintfIfTrebleEnabled(OPTIONS.input_tmp, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700808
Yifan Hong10c530d2018-12-27 17:34:18 -0800809 boot_img = common.GetBootableImage(
810 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Tao Bao481bab82017-12-21 11:23:09 -0800811 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700812 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700813
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700814 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700815
Tianjie Xuf67dd802019-05-20 17:50:36 -0700816 script.ShowProgress(0.1, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700817 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700818
Doug Zongker1c390a22009-05-14 19:06:36 -0700819 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700820 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700821
Doug Zongker14833602010-02-02 13:12:04 -0800822 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800823
Doug Zongker922206e2014-03-04 13:16:24 -0800824 if OPTIONS.wipe_user_data:
825 script.ShowProgress(0.1, 10)
826 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700827
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800828 if OPTIONS.two_step:
829 script.AppendExtra("""
830set_stage("%(bcb_dev)s", "");
831""" % bcb_dev)
832 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800833
834 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
835 script.Comment("Stage 1/3")
836 _WriteRecoveryImageToBoot(script, output_zip)
837
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800838 script.AppendExtra("""
839set_stage("%(bcb_dev)s", "2/3");
840reboot_now("%(bcb_dev)s", "");
841endif;
842endif;
843""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800844
Tao Bao5d182562016-02-23 11:38:39 -0800845 script.SetProgress(1)
846 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800847 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -0800848
849 # We haven't written the metadata entry, which will be done in
850 # FinalizeMetadata.
851 common.ZipClose(output_zip)
852
853 needed_property_files = (
854 NonAbOtaPropertyFiles(),
855 )
856 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Doug Zongker2ea21062010-04-28 16:05:21 -0700857
Doug Zongkerfc44a512014-08-26 13:10:25 -0700858
xunchang1cfe2512019-02-19 14:14:48 -0800859def WriteMetadata(metadata, output):
860 """Writes the metadata to the zip archive or a file.
861
862 Args:
863 metadata: The metadata dict for the package.
864 output: A ZipFile object or a string of the output file path.
865 """
866
Tao Bao59cf0c52019-06-25 10:04:24 -0700867 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.items())])
xunchang1cfe2512019-02-19 14:14:48 -0800868 if isinstance(output, zipfile.ZipFile):
869 common.ZipWriteStr(output, METADATA_NAME, value,
870 compress_type=zipfile.ZIP_STORED)
871 return
872
873 with open(output, 'w') as f:
874 f.write(value)
Doug Zongkereef39442009-04-02 12:14:19 -0700875
Doug Zongkerfc44a512014-08-26 13:10:25 -0700876
Tao Bao481bab82017-12-21 11:23:09 -0800877def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -0800878 # Only incremental OTAs are allowed to reach here.
879 assert OPTIONS.incremental_source is not None
880
Tao Bao481bab82017-12-21 11:23:09 -0800881 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
882 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Bao59cf0c52019-06-25 10:04:24 -0700883 is_downgrade = int(post_timestamp) < int(pre_timestamp)
Tao Baob31892e2017-02-07 11:21:17 -0800884
885 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800886 if not is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700887 raise RuntimeError(
888 "--downgrade or --override_timestamp specified but no downgrade "
889 "detected: pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800890 metadata["ota-downgrade"] = "yes"
Tao Baob31892e2017-02-07 11:21:17 -0800891 else:
892 if is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700893 raise RuntimeError(
894 "Downgrade detected based on timestamp check: pre: %s, post: %s. "
895 "Need to specify --override_timestamp OR --downgrade to allow "
896 "building the incremental." % (pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800897
898
Tao Baodf3a48b2018-01-10 16:30:43 -0800899def GetPackageMetadata(target_info, source_info=None):
900 """Generates and returns the metadata dict.
901
902 It generates a dict() that contains the info to be written into an OTA
903 package (META-INF/com/android/metadata). It also handles the detection of
Tao Baofaa8e0b2018-04-12 14:31:43 -0700904 downgrade / data wipe based on the global options.
Tao Baodf3a48b2018-01-10 16:30:43 -0800905
906 Args:
907 target_info: The BuildInfo instance that holds the target build info.
908 source_info: The BuildInfo instance that holds the source build info, or
909 None if generating full OTA.
910
911 Returns:
912 A dict to be written into package metadata entry.
913 """
Tao Bao1c320f82019-10-04 23:25:12 -0700914 assert isinstance(target_info, common.BuildInfo)
915 assert source_info is None or isinstance(source_info, common.BuildInfo)
Tao Baodf3a48b2018-01-10 16:30:43 -0800916
917 metadata = {
918 'post-build' : target_info.fingerprint,
919 'post-build-incremental' : target_info.GetBuildProp(
920 'ro.build.version.incremental'),
Tao Bao35dc2552018-02-01 13:18:00 -0800921 'post-sdk-level' : target_info.GetBuildProp(
922 'ro.build.version.sdk'),
923 'post-security-patch-level' : target_info.GetBuildProp(
924 'ro.build.version.security_patch'),
Tao Baodf3a48b2018-01-10 16:30:43 -0800925 }
926
927 if target_info.is_ab:
928 metadata['ota-type'] = 'AB'
929 metadata['ota-required-cache'] = '0'
930 else:
931 metadata['ota-type'] = 'BLOCK'
932
933 if OPTIONS.wipe_user_data:
934 metadata['ota-wipe'] = 'yes'
935
Tao Bao393eeb42019-03-06 16:00:38 -0800936 if OPTIONS.retrofit_dynamic_partitions:
937 metadata['ota-retrofit-dynamic-partitions'] = 'yes'
938
Tao Baodf3a48b2018-01-10 16:30:43 -0800939 is_incremental = source_info is not None
940 if is_incremental:
941 metadata['pre-build'] = source_info.fingerprint
942 metadata['pre-build-incremental'] = source_info.GetBuildProp(
943 'ro.build.version.incremental')
944 metadata['pre-device'] = source_info.device
945 else:
946 metadata['pre-device'] = target_info.device
947
Tao Baofaa8e0b2018-04-12 14:31:43 -0700948 # Use the actual post-timestamp, even for a downgrade case.
949 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
950
951 # Detect downgrades and set up downgrade flags accordingly.
Tao Baodf3a48b2018-01-10 16:30:43 -0800952 if is_incremental:
953 HandleDowngradeMetadata(metadata, target_info, source_info)
Tao Baodf3a48b2018-01-10 16:30:43 -0800954
955 return metadata
956
957
Tao Baod3fc38a2018-03-08 16:09:01 -0800958class PropertyFiles(object):
959 """A class that computes the property-files string for an OTA package.
960
961 A property-files string is a comma-separated string that contains the
962 offset/size info for an OTA package. The entries, which must be ZIP_STORED,
963 can be fetched directly with the package URL along with the offset/size info.
964 These strings can be used for streaming A/B OTAs, or allowing an updater to
965 download package metadata entry directly, without paying the cost of
966 downloading entire package.
Tao Baofe5b69a2018-03-02 09:47:43 -0800967
Tao Baocc8e2662018-03-01 19:30:00 -0800968 Computing the final property-files string requires two passes. Because doing
969 the whole package signing (with signapk.jar) will possibly reorder the ZIP
970 entries, which may in turn invalidate earlier computed ZIP entry offset/size
971 values.
972
973 This class provides functions to be called for each pass. The general flow is
974 as follows.
975
Tao Baod3fc38a2018-03-08 16:09:01 -0800976 property_files = PropertyFiles()
Tao Baocc8e2662018-03-01 19:30:00 -0800977 # The first pass, which writes placeholders before doing initial signing.
978 property_files.Compute()
979 SignOutput()
980
981 # The second pass, by replacing the placeholders with actual data.
982 property_files.Finalize()
983 SignOutput()
984
985 And the caller can additionally verify the final result.
986
987 property_files.Verify()
Tao Baofe5b69a2018-03-02 09:47:43 -0800988 """
989
Tao Baocc8e2662018-03-01 19:30:00 -0800990 def __init__(self):
Tao Baod3fc38a2018-03-08 16:09:01 -0800991 self.name = None
992 self.required = ()
993 self.optional = ()
Tao Baofe5b69a2018-03-02 09:47:43 -0800994
Tao Baocc8e2662018-03-01 19:30:00 -0800995 def Compute(self, input_zip):
996 """Computes and returns a property-files string with placeholders.
Tao Baofe5b69a2018-03-02 09:47:43 -0800997
Tao Baocc8e2662018-03-01 19:30:00 -0800998 We reserve extra space for the offset and size of the metadata entry itself,
999 although we don't know the final values until the package gets signed.
Tao Baofe5b69a2018-03-02 09:47:43 -08001000
Tao Baocc8e2662018-03-01 19:30:00 -08001001 Args:
1002 input_zip: The input ZIP file.
Tao Baofe5b69a2018-03-02 09:47:43 -08001003
Tao Baocc8e2662018-03-01 19:30:00 -08001004 Returns:
1005 A string with placeholders for the metadata offset/size info, e.g.
1006 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1007 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001008 return self.GetPropertyFilesString(input_zip, reserve_space=True)
Tao Baofe5b69a2018-03-02 09:47:43 -08001009
Tao Baod2ce2ed2018-03-16 12:59:42 -07001010 class InsufficientSpaceException(Exception):
1011 pass
1012
Tao Baocc8e2662018-03-01 19:30:00 -08001013 def Finalize(self, input_zip, reserved_length):
1014 """Finalizes a property-files string with actual METADATA offset/size info.
1015
1016 The input ZIP file has been signed, with the ZIP entries in the desired
1017 place (signapk.jar will possibly reorder the ZIP entries). Now we compute
1018 the ZIP entry offsets and construct the property-files string with actual
1019 data. Note that during this process, we must pad the property-files string
1020 to the reserved length, so that the METADATA entry size remains the same.
1021 Otherwise the entries' offsets and sizes may change again.
1022
1023 Args:
1024 input_zip: The input ZIP file.
1025 reserved_length: The reserved length of the property-files string during
1026 the call to Compute(). The final string must be no more than this
1027 size.
1028
1029 Returns:
1030 A property-files string including the metadata offset/size info, e.g.
1031 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379 ".
1032
1033 Raises:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001034 InsufficientSpaceException: If the reserved length is insufficient to hold
1035 the final string.
Tao Baocc8e2662018-03-01 19:30:00 -08001036 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001037 result = self.GetPropertyFilesString(input_zip, reserve_space=False)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001038 if len(result) > reserved_length:
1039 raise self.InsufficientSpaceException(
1040 'Insufficient reserved space: reserved={}, actual={}'.format(
1041 reserved_length, len(result)))
1042
Tao Baocc8e2662018-03-01 19:30:00 -08001043 result += ' ' * (reserved_length - len(result))
1044 return result
1045
1046 def Verify(self, input_zip, expected):
1047 """Verifies the input ZIP file contains the expected property-files string.
1048
1049 Args:
1050 input_zip: The input ZIP file.
1051 expected: The property-files string that's computed from Finalize().
1052
1053 Raises:
1054 AssertionError: On finding a mismatch.
1055 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001056 actual = self.GetPropertyFilesString(input_zip)
Tao Baocc8e2662018-03-01 19:30:00 -08001057 assert actual == expected, \
1058 "Mismatching streaming metadata: {} vs {}.".format(actual, expected)
1059
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001060 def GetPropertyFilesString(self, zip_file, reserve_space=False):
1061 """
1062 Constructs the property-files string per request.
1063
1064 Args:
1065 zip_file: The input ZIP file.
1066 reserved_length: The reserved length of the property-files string.
1067
1068 Returns:
1069 A property-files string including the metadata offset/size info, e.g.
1070 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1071 """
Tao Baocc8e2662018-03-01 19:30:00 -08001072
1073 def ComputeEntryOffsetSize(name):
1074 """Computes the zip entry offset and size."""
1075 info = zip_file.getinfo(name)
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001076 offset = info.header_offset
1077 offset += zipfile.sizeFileHeader
1078 offset += len(info.extra) + len(info.filename)
Tao Baocc8e2662018-03-01 19:30:00 -08001079 size = info.file_size
1080 return '%s:%d:%d' % (os.path.basename(name), offset, size)
1081
1082 tokens = []
Tao Bao85f16982018-03-08 16:28:33 -08001083 tokens.extend(self._GetPrecomputed(zip_file))
Tao Baocc8e2662018-03-01 19:30:00 -08001084 for entry in self.required:
1085 tokens.append(ComputeEntryOffsetSize(entry))
1086 for entry in self.optional:
1087 if entry in zip_file.namelist():
1088 tokens.append(ComputeEntryOffsetSize(entry))
1089
1090 # 'META-INF/com/android/metadata' is required. We don't know its actual
1091 # offset and length (as well as the values for other entries). So we reserve
Tao Baod2ce2ed2018-03-16 12:59:42 -07001092 # 15-byte as a placeholder ('offset:length'), which is sufficient to cover
1093 # the space for metadata entry. Because 'offset' allows a max of 10-digit
1094 # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the
1095 # reserved space serves the metadata entry only.
Tao Baocc8e2662018-03-01 19:30:00 -08001096 if reserve_space:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001097 tokens.append('metadata:' + ' ' * 15)
Tao Baocc8e2662018-03-01 19:30:00 -08001098 else:
1099 tokens.append(ComputeEntryOffsetSize(METADATA_NAME))
1100
1101 return ','.join(tokens)
Tao Baofe5b69a2018-03-02 09:47:43 -08001102
Tao Bao85f16982018-03-08 16:28:33 -08001103 def _GetPrecomputed(self, input_zip):
1104 """Computes the additional tokens to be included into the property-files.
1105
1106 This applies to tokens without actual ZIP entries, such as
1107 payload_metadadata.bin. We want to expose the offset/size to updaters, so
1108 that they can download the payload metadata directly with the info.
1109
1110 Args:
1111 input_zip: The input zip file.
1112
1113 Returns:
1114 A list of strings (tokens) to be added to the property-files string.
1115 """
1116 # pylint: disable=no-self-use
1117 # pylint: disable=unused-argument
1118 return []
1119
Tao Baofe5b69a2018-03-02 09:47:43 -08001120
Tao Baod3fc38a2018-03-08 16:09:01 -08001121class StreamingPropertyFiles(PropertyFiles):
1122 """A subclass for computing the property-files for streaming A/B OTAs."""
1123
1124 def __init__(self):
1125 super(StreamingPropertyFiles, self).__init__()
1126 self.name = 'ota-streaming-property-files'
1127 self.required = (
1128 # payload.bin and payload_properties.txt must exist.
1129 'payload.bin',
1130 'payload_properties.txt',
1131 )
1132 self.optional = (
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001133 # care_map is available only if dm-verity is enabled.
1134 'care_map.pb',
Tao Baod3fc38a2018-03-08 16:09:01 -08001135 'care_map.txt',
1136 # compatibility.zip is available only if target supports Treble.
1137 'compatibility.zip',
1138 )
1139
1140
Tao Bao85f16982018-03-08 16:28:33 -08001141class AbOtaPropertyFiles(StreamingPropertyFiles):
1142 """The property-files for A/B OTA that includes payload_metadata.bin info.
1143
1144 Since P, we expose one more token (aka property-file), in addition to the ones
1145 for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'.
1146 'payload_metadata.bin' is the header part of a payload ('payload.bin'), which
1147 doesn't exist as a separate ZIP entry, but can be used to verify if the
1148 payload can be applied on the given device.
1149
1150 For backward compatibility, we keep both of the 'ota-streaming-property-files'
1151 and the newly added 'ota-property-files' in P. The new token will only be
1152 available in 'ota-property-files'.
1153 """
1154
1155 def __init__(self):
1156 super(AbOtaPropertyFiles, self).__init__()
1157 self.name = 'ota-property-files'
1158
1159 def _GetPrecomputed(self, input_zip):
1160 offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip)
1161 return ['payload_metadata.bin:{}:{}'.format(offset, size)]
1162
1163 @staticmethod
1164 def _GetPayloadMetadataOffsetAndSize(input_zip):
1165 """Computes the offset and size of the payload metadata for a given package.
1166
1167 (From system/update_engine/update_metadata.proto)
1168 A delta update file contains all the deltas needed to update a system from
1169 one specific version to another specific version. The update format is
1170 represented by this struct pseudocode:
1171
1172 struct delta_update_file {
1173 char magic[4] = "CrAU";
1174 uint64 file_format_version;
1175 uint64 manifest_size; // Size of protobuf DeltaArchiveManifest
1176
1177 // Only present if format_version > 1:
1178 uint32 metadata_signature_size;
1179
1180 // The Bzip2 compressed DeltaArchiveManifest
1181 char manifest[metadata_signature_size];
1182
1183 // The signature of the metadata (from the beginning of the payload up to
1184 // this location, not including the signature itself). This is a
1185 // serialized Signatures message.
1186 char medatada_signature_message[metadata_signature_size];
1187
1188 // Data blobs for files, no specific format. The specific offset
1189 // and length of each data blob is recorded in the DeltaArchiveManifest.
1190 struct {
1191 char data[];
1192 } blobs[];
1193
1194 // These two are not signed:
1195 uint64 payload_signatures_message_size;
1196 char payload_signatures_message[];
1197 };
1198
1199 'payload-metadata.bin' contains all the bytes from the beginning of the
1200 payload, till the end of 'medatada_signature_message'.
1201 """
1202 payload_info = input_zip.getinfo('payload.bin')
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001203 payload_offset = payload_info.header_offset
1204 payload_offset += zipfile.sizeFileHeader
1205 payload_offset += len(payload_info.extra) + len(payload_info.filename)
Tao Bao85f16982018-03-08 16:28:33 -08001206 payload_size = payload_info.file_size
1207
Tao Bao59cf0c52019-06-25 10:04:24 -07001208 with input_zip.open('payload.bin') as payload_fp:
Tao Bao85f16982018-03-08 16:28:33 -08001209 header_bin = payload_fp.read(24)
1210
1211 # network byte order (big-endian)
1212 header = struct.unpack("!IQQL", header_bin)
1213
1214 # 'CrAU'
1215 magic = header[0]
1216 assert magic == 0x43724155, "Invalid magic: {:x}".format(magic)
1217
1218 manifest_size = header[2]
1219 metadata_signature_size = header[3]
1220 metadata_total = 24 + manifest_size + metadata_signature_size
1221 assert metadata_total < payload_size
1222
1223 return (payload_offset, metadata_total)
1224
1225
Tao Bao491d7e22018-02-21 13:17:22 -08001226class NonAbOtaPropertyFiles(PropertyFiles):
1227 """The property-files for non-A/B OTA.
1228
1229 For non-A/B OTA, the property-files string contains the info for METADATA
1230 entry, with which a system updater can be fetched the package metadata prior
1231 to downloading the entire package.
1232 """
1233
1234 def __init__(self):
1235 super(NonAbOtaPropertyFiles, self).__init__()
1236 self.name = 'ota-property-files'
1237
1238
Tao Baod3fc38a2018-03-08 16:09:01 -08001239def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
Tao Baofe5b69a2018-03-02 09:47:43 -08001240 """Finalizes the metadata and signs an A/B OTA package.
1241
1242 In order to stream an A/B OTA package, we need 'ota-streaming-property-files'
1243 that contains the offsets and sizes for the ZIP entries. An example
1244 property-files string is as follows.
1245
1246 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379"
1247
1248 OTA server can pass down this string, in addition to the package URL, to the
1249 system update client. System update client can then fetch individual ZIP
1250 entries (ZIP_STORED) directly at the given offset of the URL.
1251
1252 Args:
1253 metadata: The metadata dict for the package.
1254 input_file: The input ZIP filename that doesn't contain the package METADATA
1255 entry yet.
1256 output_file: The final output ZIP filename.
Tao Baod3fc38a2018-03-08 16:09:01 -08001257 needed_property_files: The list of PropertyFiles' to be generated.
Tao Baofe5b69a2018-03-02 09:47:43 -08001258 """
Tao Baofe5b69a2018-03-02 09:47:43 -08001259
Tao Baod2ce2ed2018-03-16 12:59:42 -07001260 def ComputeAllPropertyFiles(input_file, needed_property_files):
1261 # Write the current metadata entry with placeholders.
1262 with zipfile.ZipFile(input_file) as input_zip:
1263 for property_files in needed_property_files:
1264 metadata[property_files.name] = property_files.Compute(input_zip)
1265 namelist = input_zip.namelist()
Tao Baofe5b69a2018-03-02 09:47:43 -08001266
Tao Baod2ce2ed2018-03-16 12:59:42 -07001267 if METADATA_NAME in namelist:
1268 common.ZipDelete(input_file, METADATA_NAME)
1269 output_zip = zipfile.ZipFile(input_file, 'a')
1270 WriteMetadata(metadata, output_zip)
1271 common.ZipClose(output_zip)
1272
1273 if OPTIONS.no_signing:
1274 return input_file
1275
Tao Bao491d7e22018-02-21 13:17:22 -08001276 prelim_signing = common.MakeTempFile(suffix='.zip')
1277 SignOutput(input_file, prelim_signing)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001278 return prelim_signing
Tao Baofe5b69a2018-03-02 09:47:43 -08001279
Tao Baod2ce2ed2018-03-16 12:59:42 -07001280 def FinalizeAllPropertyFiles(prelim_signing, needed_property_files):
1281 with zipfile.ZipFile(prelim_signing) as prelim_signing_zip:
1282 for property_files in needed_property_files:
1283 metadata[property_files.name] = property_files.Finalize(
1284 prelim_signing_zip, len(metadata[property_files.name]))
1285
1286 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP
1287 # entries, as well as padding the entry headers. We do a preliminary signing
1288 # (with an incomplete metadata entry) to allow that to happen. Then compute
1289 # the ZIP entry offsets, write back the final metadata and do the final
1290 # signing.
1291 prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files)
1292 try:
1293 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
1294 except PropertyFiles.InsufficientSpaceException:
1295 # Even with the preliminary signing, the entry orders may change
1296 # dramatically, which leads to insufficiently reserved space during the
1297 # first call to ComputeAllPropertyFiles(). In that case, we redo all the
1298 # preliminary signing works, based on the already ordered ZIP entries, to
1299 # address the issue.
1300 prelim_signing = ComputeAllPropertyFiles(
1301 prelim_signing, needed_property_files)
1302 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
Tao Baofe5b69a2018-03-02 09:47:43 -08001303
1304 # Replace the METADATA entry.
1305 common.ZipDelete(prelim_signing, METADATA_NAME)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001306 output_zip = zipfile.ZipFile(prelim_signing, 'a')
Tao Baofe5b69a2018-03-02 09:47:43 -08001307 WriteMetadata(metadata, output_zip)
1308 common.ZipClose(output_zip)
1309
1310 # Re-sign the package after updating the metadata entry.
Tao Bao491d7e22018-02-21 13:17:22 -08001311 if OPTIONS.no_signing:
1312 output_file = prelim_signing
1313 else:
1314 SignOutput(prelim_signing, output_file)
Tao Baofe5b69a2018-03-02 09:47:43 -08001315
1316 # Reopen the final signed zip to double check the streaming metadata.
Tao Baod2ce2ed2018-03-16 12:59:42 -07001317 with zipfile.ZipFile(output_file) as output_zip:
Tao Baod3fc38a2018-03-08 16:09:01 -08001318 for property_files in needed_property_files:
1319 property_files.Verify(output_zip, metadata[property_files.name].strip())
Tao Baofe5b69a2018-03-02 09:47:43 -08001320
xunchang1cfe2512019-02-19 14:14:48 -08001321 # If requested, dump the metadata to a separate file.
1322 output_metadata_path = OPTIONS.output_metadata_path
1323 if output_metadata_path:
1324 WriteMetadata(metadata, output_metadata_path)
1325
Tao Baofe5b69a2018-03-02 09:47:43 -08001326
Tao Bao491d7e22018-02-21 13:17:22 -08001327def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
Tao Bao1c320f82019-10-04 23:25:12 -07001328 target_info = common.BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1329 source_info = common.BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -08001330
Tao Bao481bab82017-12-21 11:23:09 -08001331 target_api_version = target_info["recovery_api_version"]
1332 source_api_version = source_info["recovery_api_version"]
1333 if source_api_version == 0:
Tao Bao32fcdab2018-10-12 10:30:39 -07001334 logger.warning(
1335 "Generating edify script for a source that can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001336
Tao Bao481bab82017-12-21 11:23:09 -08001337 script = edify_generator.EdifyGenerator(
1338 source_api_version, target_info, fstab=source_info["fstab"])
1339
1340 if target_info.oem_props or source_info.oem_props:
1341 if not OPTIONS.oem_no_mount:
1342 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -07001343
Tao Baodf3a48b2018-01-10 16:30:43 -08001344 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -08001345
Tao Bao491d7e22018-02-21 13:17:22 -08001346 if not OPTIONS.no_signing:
1347 staging_file = common.MakeTempFile(suffix='.zip')
1348 else:
1349 staging_file = output_file
1350
1351 output_zip = zipfile.ZipFile(
1352 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
1353
Geremy Condra36bd3652014-02-06 19:45:10 -08001354 device_specific = common.DeviceSpecificParams(
1355 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001356 source_version=source_api_version,
Yifan Hong8a66a712019-04-04 15:37:57 -07001357 source_tmp=OPTIONS.source_tmp,
Geremy Condra36bd3652014-02-06 19:45:10 -08001358 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001359 target_version=target_api_version,
Yifan Hong8a66a712019-04-04 15:37:57 -07001360 target_tmp=OPTIONS.target_tmp,
Geremy Condra36bd3652014-02-06 19:45:10 -08001361 output_zip=output_zip,
1362 script=script,
1363 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -08001364 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001365
Geremy Condra36bd3652014-02-06 19:45:10 -08001366 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001367 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001368 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001369 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001370 updating_boot = (not OPTIONS.two_step and
1371 (source_boot.data != target_boot.data))
1372
Geremy Condra36bd3652014-02-06 19:45:10 -08001373 target_recovery = common.GetBootableImage(
1374 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -08001375
Tianjie Xuf67dd802019-05-20 17:50:36 -07001376 block_diff_dict = GetBlockDifferences(target_zip=target_zip,
1377 source_zip=source_zip,
1378 target_info=target_info,
1379 source_info=source_info,
1380 device_specific=device_specific)
Geremy Condra36bd3652014-02-06 19:45:10 -08001381
Yifan Hong9276cf02019-08-21 16:37:04 -07001382 CheckVintfIfTrebleEnabled(OPTIONS.target_tmp, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001383
Tao Bao481bab82017-12-21 11:23:09 -08001384 # Assertions (e.g. device properties check).
1385 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -08001386 device_specific.IncrementalOTA_Assertions()
1387
1388 # Two-step incremental package strategy (in chronological order,
1389 # which is *not* the order in which the generated script has
1390 # things):
1391 #
1392 # if stage is not "2/3" or "3/3":
1393 # do verification on current system
1394 # write recovery image to boot partition
1395 # set stage to "2/3"
1396 # reboot to boot partition and restart recovery
1397 # else if stage is "2/3":
1398 # write recovery image to recovery partition
1399 # set stage to "3/3"
1400 # reboot to recovery partition and restart recovery
1401 # else:
1402 # (stage must be "3/3")
1403 # perform update:
1404 # patch system files, etc.
1405 # force full install of new boot image
1406 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001407 # complete script normally
1408 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001409
1410 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001411 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001412 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001413 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001414 assert fs.fs_type.upper() == "EMMC", \
1415 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001416 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001417 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1418 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001419if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001420""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001421
1422 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1423 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001424 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001425 script.WriteRawImage("/recovery", "recovery.img")
1426 script.AppendExtra("""
1427set_stage("%(bcb_dev)s", "3/3");
1428reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001429else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001430""" % bcb_dev)
1431
Tao Baod42e97e2016-11-30 12:11:57 -08001432 # Stage 1/3: (a) Verify the current system.
1433 script.Comment("Stage 1/3")
1434
Tao Bao6c55a8a2015-04-08 15:30:27 -07001435 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001436 script.Print("Source: {}".format(source_info.fingerprint))
1437 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001438
Geremy Condra36bd3652014-02-06 19:45:10 -08001439 script.Print("Verifying current system...")
1440
1441 device_specific.IncrementalOTA_VerifyBegin()
1442
Tao Bao481bab82017-12-21 11:23:09 -08001443 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001444
Tao Baod8d14be2016-02-04 14:26:02 -08001445 # Check the required cache size (i.e. stashed blocks).
Tianjie Xuf67dd802019-05-20 17:50:36 -07001446 required_cache_sizes = [diff.required_cache for diff in
1447 block_diff_dict.values()]
Geremy Condra36bd3652014-02-06 19:45:10 -08001448 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -08001449 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001450 d = common.Difference(target_boot, source_boot)
1451 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001452 if d is None:
1453 include_full_boot = True
1454 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1455 else:
1456 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001457
Tao Bao32fcdab2018-10-12 10:30:39 -07001458 logger.info(
1459 "boot target: %d source: %d diff: %d", target_boot.size,
1460 source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -08001461
Tao Bao51216552018-08-26 11:53:15 -07001462 common.ZipWriteStr(output_zip, "boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001463
Tao Bao51216552018-08-26 11:53:15 -07001464 script.PatchPartitionCheck(
1465 "{}:{}:{}:{}".format(
1466 boot_type, boot_device, target_boot.size, target_boot.sha1),
1467 "{}:{}:{}:{}".format(
1468 boot_type, boot_device, source_boot.size, source_boot.sha1))
1469
Tianjie Xuf67dd802019-05-20 17:50:36 -07001470 required_cache_sizes.append(target_boot.size)
Tao Baod8d14be2016-02-04 14:26:02 -08001471
Tianjie Xuf67dd802019-05-20 17:50:36 -07001472 if required_cache_sizes:
1473 script.CacheFreeSpaceCheck(max(required_cache_sizes))
1474
1475 # Verify the existing partitions.
1476 for diff in block_diff_dict.values():
1477 diff.WriteVerifyScript(script, touched_blocks_only=True)
Geremy Condra36bd3652014-02-06 19:45:10 -08001478
1479 device_specific.IncrementalOTA_VerifyEnd()
1480
1481 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001482 # Stage 1/3: (b) Write recovery image to /boot.
1483 _WriteRecoveryImageToBoot(script, output_zip)
1484
Geremy Condra36bd3652014-02-06 19:45:10 -08001485 script.AppendExtra("""
1486set_stage("%(bcb_dev)s", "2/3");
1487reboot_now("%(bcb_dev)s", "");
1488else
1489""" % bcb_dev)
1490
Tao Baod42e97e2016-11-30 12:11:57 -08001491 # Stage 3/3: Make changes.
1492 script.Comment("Stage 3/3")
1493
Geremy Condra36bd3652014-02-06 19:45:10 -08001494 script.Comment("---- start making changes here ----")
1495
1496 device_specific.IncrementalOTA_InstallBegin()
1497
Tianjie Xuf67dd802019-05-20 17:50:36 -07001498 progress_dict = {partition: 0.1 for partition in block_diff_dict}
1499 progress_dict["system"] = 1 - len(block_diff_dict) * 0.1
Yifan Hong10c530d2018-12-27 17:34:18 -08001500
1501 if OPTIONS.source_info_dict.get("use_dynamic_partitions") == "true":
1502 if OPTIONS.target_info_dict.get("use_dynamic_partitions") != "true":
1503 raise RuntimeError(
1504 "can't generate incremental that disables dynamic partitions")
1505 dynamic_partitions_diff = common.DynamicPartitionsDifference(
1506 info_dict=OPTIONS.target_info_dict,
1507 source_info_dict=OPTIONS.source_info_dict,
Tianjie Xuf67dd802019-05-20 17:50:36 -07001508 block_diffs=block_diff_dict.values(),
Yifan Hong10c530d2018-12-27 17:34:18 -08001509 progress_dict=progress_dict)
1510 dynamic_partitions_diff.WriteScript(
1511 script, output_zip, write_verify_script=OPTIONS.verify)
1512 else:
Tianjie Xuf67dd802019-05-20 17:50:36 -07001513 for block_diff in block_diff_dict.values():
Yifan Hong10c530d2018-12-27 17:34:18 -08001514 block_diff.WriteScript(script, output_zip,
1515 progress=progress_dict.get(block_diff.partition),
1516 write_verify_script=OPTIONS.verify)
Geremy Condra36bd3652014-02-06 19:45:10 -08001517
1518 if OPTIONS.two_step:
1519 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1520 script.WriteRawImage("/boot", "boot.img")
Tao Bao32fcdab2018-10-12 10:30:39 -07001521 logger.info("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001522
1523 if not OPTIONS.two_step:
1524 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001525 if include_full_boot:
Tao Bao32fcdab2018-10-12 10:30:39 -07001526 logger.info("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001527 script.Print("Installing boot image...")
1528 script.WriteRawImage("/boot", "boot.img")
1529 else:
1530 # Produce the boot image by applying a patch to the current
1531 # contents of the boot partition, and write it back to the
1532 # partition.
Tao Bao32fcdab2018-10-12 10:30:39 -07001533 logger.info("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001534 script.Print("Patching boot image...")
1535 script.ShowProgress(0.1, 10)
Tao Bao51216552018-08-26 11:53:15 -07001536 script.PatchPartition(
1537 '{}:{}:{}:{}'.format(
1538 boot_type, boot_device, target_boot.size, target_boot.sha1),
1539 '{}:{}:{}:{}'.format(
1540 boot_type, boot_device, source_boot.size, source_boot.sha1),
1541 'boot.img.p')
Geremy Condra36bd3652014-02-06 19:45:10 -08001542 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001543 logger.info("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001544
1545 # Do device-specific installation (eg, write radio image).
1546 device_specific.IncrementalOTA_InstallEnd()
1547
1548 if OPTIONS.extra_script is not None:
1549 script.AppendExtra(OPTIONS.extra_script)
1550
Doug Zongker922206e2014-03-04 13:16:24 -08001551 if OPTIONS.wipe_user_data:
1552 script.Print("Erasing user data...")
1553 script.FormatPartition("/data")
1554
Geremy Condra36bd3652014-02-06 19:45:10 -08001555 if OPTIONS.two_step:
1556 script.AppendExtra("""
1557set_stage("%(bcb_dev)s", "");
1558endif;
1559endif;
1560""" % bcb_dev)
1561
1562 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001563 # For downgrade OTAs, we prefer to use the update-binary in the source
1564 # build that is actually newer than the one in the target build.
1565 if OPTIONS.downgrade:
1566 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1567 else:
1568 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001569 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001570
1571 # We haven't written the metadata entry yet, which will be handled in
1572 # FinalizeMetadata().
1573 common.ZipClose(output_zip)
1574
1575 # Sign the generated zip package unless no_signing is specified.
1576 needed_property_files = (
1577 NonAbOtaPropertyFiles(),
1578 )
1579 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Geremy Condra36bd3652014-02-06 19:45:10 -08001580
Doug Zongker32b527d2014-03-04 10:03:02 -08001581
Tao Bao15a146a2018-02-21 16:06:59 -08001582def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
Tao Baof7140c02018-01-30 17:09:24 -08001583 """Returns a target-files.zip file for generating secondary payload.
1584
1585 Although the original target-files.zip already contains secondary slot
1586 images (i.e. IMAGES/system_other.img), we need to rename the files to the
1587 ones without _other suffix. Note that we cannot instead modify the names in
1588 META/ab_partitions.txt, because there are no matching partitions on device.
1589
1590 For the partitions that don't have secondary images, the ones for primary
1591 slot will be used. This is to ensure that we always have valid boot, vbmeta,
1592 bootloader images in the inactive slot.
1593
1594 Args:
1595 input_file: The input target-files.zip file.
Tao Bao15a146a2018-02-21 16:06:59 -08001596 skip_postinstall: Whether to skip copying the postinstall config file.
Tao Baof7140c02018-01-30 17:09:24 -08001597
1598 Returns:
1599 The filename of the target-files.zip for generating secondary payload.
1600 """
Tianjie Xu1c808002019-09-11 00:29:26 -07001601
1602 def GetInfoForSecondaryImages(info_file):
1603 """Updates info file for secondary payload generation.
1604
1605 Scan each line in the info file, and remove the unwanted partitions from
1606 the dynamic partition list in the related properties. e.g.
1607 "super_google_dynamic_partitions_partition_list=system vendor product"
1608 will become "super_google_dynamic_partitions_partition_list=system".
1609
1610 Args:
1611 info_file: The input info file. e.g. misc_info.txt.
1612
1613 Returns:
1614 A string of the updated info content.
1615 """
1616
1617 output_list = []
1618 with open(info_file) as f:
1619 lines = f.read().splitlines()
1620
1621 # The suffix in partition_list variables that follows the name of the
1622 # partition group.
1623 LIST_SUFFIX = 'partition_list'
1624 for line in lines:
1625 if line.startswith('#') or '=' not in line:
1626 output_list.append(line)
1627 continue
1628 key, value = line.strip().split('=', 1)
1629 if key == 'dynamic_partition_list' or key.endswith(LIST_SUFFIX):
1630 partitions = value.split()
1631 partitions = [partition for partition in partitions if partition
Tao Bao3e759462019-09-17 22:43:11 -07001632 not in SECONDARY_PAYLOAD_SKIPPED_IMAGES]
Tianjie Xu1c808002019-09-11 00:29:26 -07001633 output_list.append('{}={}'.format(key, ' '.join(partitions)))
1634 else:
1635 output_list.append(line)
1636 return '\n'.join(output_list)
1637
Tao Baof7140c02018-01-30 17:09:24 -08001638 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1639 target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
1640
Tao Baodba59ee2018-01-09 13:21:02 -08001641 with zipfile.ZipFile(input_file, 'r') as input_zip:
1642 infolist = input_zip.infolist()
Tao Bao12489802018-07-12 14:47:38 -07001643
Tao Bao0ff15de2019-03-20 11:26:06 -07001644 input_tmp = common.UnzipTemp(input_file, UNZIP_PATTERN)
Tao Baodba59ee2018-01-09 13:21:02 -08001645 for info in infolist:
Tao Baof7140c02018-01-30 17:09:24 -08001646 unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
1647 if info.filename == 'IMAGES/system_other.img':
1648 common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
1649
1650 # Primary images and friends need to be skipped explicitly.
1651 elif info.filename in ('IMAGES/system.img',
1652 'IMAGES/system.map'):
1653 pass
Tao Bao3e759462019-09-17 22:43:11 -07001654
1655 # Copy images that are not in SECONDARY_PAYLOAD_SKIPPED_IMAGES.
1656 elif info.filename.startswith(('IMAGES/', 'RADIO/')):
1657 image_name = os.path.basename(info.filename)
1658 if image_name not in ['{}.img'.format(partition) for partition in
1659 SECONDARY_PAYLOAD_SKIPPED_IMAGES]:
1660 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
Tao Baof7140c02018-01-30 17:09:24 -08001661
Tao Bao15a146a2018-02-21 16:06:59 -08001662 # Skip copying the postinstall config if requested.
1663 elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
1664 pass
1665
Tianjie Xu1c808002019-09-11 00:29:26 -07001666 elif info.filename.startswith('META/'):
1667 # Remove the unnecessary partitions for secondary images from the
1668 # ab_partitions file.
1669 if info.filename == AB_PARTITIONS:
1670 with open(unzipped_file) as f:
1671 partition_list = f.read().splitlines()
1672 partition_list = [partition for partition in partition_list if partition
Tao Bao3e759462019-09-17 22:43:11 -07001673 and partition not in SECONDARY_PAYLOAD_SKIPPED_IMAGES]
Tianjie Xu1c808002019-09-11 00:29:26 -07001674 common.ZipWriteStr(target_zip, info.filename, '\n'.join(partition_list))
1675 # Remove the unnecessary partitions from the dynamic partitions list.
1676 elif (info.filename == 'META/misc_info.txt' or
1677 info.filename == DYNAMIC_PARTITION_INFO):
1678 modified_info = GetInfoForSecondaryImages(unzipped_file)
1679 common.ZipWriteStr(target_zip, info.filename, modified_info)
1680 else:
1681 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
Tao Baof7140c02018-01-30 17:09:24 -08001682
Tao Baof7140c02018-01-30 17:09:24 -08001683 common.ZipClose(target_zip)
1684
1685 return target_file
1686
1687
Tao Bao15a146a2018-02-21 16:06:59 -08001688def GetTargetFilesZipWithoutPostinstallConfig(input_file):
1689 """Returns a target-files.zip that's not containing postinstall_config.txt.
1690
1691 This allows brillo_update_payload script to skip writing all the postinstall
1692 hooks in the generated payload. The input target-files.zip file will be
1693 duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
1694 contain the postinstall_config.txt entry, the input file will be returned.
1695
1696 Args:
1697 input_file: The input target-files.zip filename.
1698
1699 Returns:
1700 The filename of target-files.zip that doesn't contain postinstall config.
1701 """
1702 # We should only make a copy if postinstall_config entry exists.
1703 with zipfile.ZipFile(input_file, 'r') as input_zip:
1704 if POSTINSTALL_CONFIG not in input_zip.namelist():
1705 return input_file
1706
1707 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1708 shutil.copyfile(input_file, target_file)
1709 common.ZipDelete(target_file, POSTINSTALL_CONFIG)
1710 return target_file
1711
1712
Yifan Hong50e79542018-11-08 17:44:12 -08001713def GetTargetFilesZipForRetrofitDynamicPartitions(input_file,
Yifan Hongb433eba2019-03-06 12:42:53 -08001714 super_block_devices,
1715 dynamic_partition_list):
Yifan Hong50e79542018-11-08 17:44:12 -08001716 """Returns a target-files.zip for retrofitting dynamic partitions.
1717
1718 This allows brillo_update_payload to generate an OTA based on the exact
1719 bits on the block devices. Postinstall is disabled.
1720
1721 Args:
1722 input_file: The input target-files.zip filename.
1723 super_block_devices: The list of super block devices
Yifan Hongb433eba2019-03-06 12:42:53 -08001724 dynamic_partition_list: The list of dynamic partitions
Yifan Hong50e79542018-11-08 17:44:12 -08001725
1726 Returns:
1727 The filename of target-files.zip with *.img replaced with super_*.img for
1728 each block device in super_block_devices.
1729 """
1730 assert super_block_devices, "No super_block_devices are specified."
1731
1732 replace = {'OTA/super_{}.img'.format(dev): 'IMAGES/{}.img'.format(dev)
Tao Bao03fecb62018-11-28 10:59:23 -08001733 for dev in super_block_devices}
Yifan Hong50e79542018-11-08 17:44:12 -08001734
1735 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1736 shutil.copyfile(input_file, target_file)
1737
Tao Baoa3705452019-06-24 15:33:41 -07001738 with zipfile.ZipFile(input_file) as input_zip:
Yifan Hong50e79542018-11-08 17:44:12 -08001739 namelist = input_zip.namelist()
1740
Yifan Hongb433eba2019-03-06 12:42:53 -08001741 input_tmp = common.UnzipTemp(input_file, RETROFIT_DAP_UNZIP_PATTERN)
1742
1743 # Remove partitions from META/ab_partitions.txt that is in
1744 # dynamic_partition_list but not in super_block_devices so that
1745 # brillo_update_payload won't generate update for those logical partitions.
1746 ab_partitions_file = os.path.join(input_tmp, *AB_PARTITIONS.split('/'))
1747 with open(ab_partitions_file) as f:
1748 ab_partitions_lines = f.readlines()
1749 ab_partitions = [line.strip() for line in ab_partitions_lines]
1750 # Assert that all super_block_devices are in ab_partitions
1751 super_device_not_updated = [partition for partition in super_block_devices
1752 if partition not in ab_partitions]
1753 assert not super_device_not_updated, \
1754 "{} is in super_block_devices but not in {}".format(
1755 super_device_not_updated, AB_PARTITIONS)
1756 # ab_partitions -= (dynamic_partition_list - super_block_devices)
1757 new_ab_partitions = common.MakeTempFile(prefix="ab_partitions", suffix=".txt")
1758 with open(new_ab_partitions, 'w') as f:
1759 for partition in ab_partitions:
1760 if (partition in dynamic_partition_list and
1761 partition not in super_block_devices):
Tao Bao59cf0c52019-06-25 10:04:24 -07001762 logger.info("Dropping %s from ab_partitions.txt", partition)
1763 continue
Yifan Hongb433eba2019-03-06 12:42:53 -08001764 f.write(partition + "\n")
1765 to_delete = [AB_PARTITIONS]
1766
Yifan Hong50e79542018-11-08 17:44:12 -08001767 # Always skip postinstall for a retrofit update.
Yifan Hongb433eba2019-03-06 12:42:53 -08001768 to_delete += [POSTINSTALL_CONFIG]
Yifan Hong50e79542018-11-08 17:44:12 -08001769
1770 # Delete dynamic_partitions_info.txt so that brillo_update_payload thinks this
1771 # is a regular update on devices without dynamic partitions support.
1772 to_delete += [DYNAMIC_PARTITION_INFO]
1773
Tao Bao03fecb62018-11-28 10:59:23 -08001774 # Remove the existing partition images as well as the map files.
Tao Bao59cf0c52019-06-25 10:04:24 -07001775 to_delete += list(replace.values())
Tao Bao03fecb62018-11-28 10:59:23 -08001776 to_delete += ['IMAGES/{}.map'.format(dev) for dev in super_block_devices]
Yifan Hong50e79542018-11-08 17:44:12 -08001777
1778 common.ZipDelete(target_file, to_delete)
1779
Yifan Hong50e79542018-11-08 17:44:12 -08001780 target_zip = zipfile.ZipFile(target_file, 'a', allowZip64=True)
1781
1782 # Write super_{foo}.img as {foo}.img.
1783 for src, dst in replace.items():
1784 assert src in namelist, \
Tao Bao59cf0c52019-06-25 10:04:24 -07001785 'Missing {} in {}; {} cannot be written'.format(src, input_file, dst)
Yifan Hong50e79542018-11-08 17:44:12 -08001786 unzipped_file = os.path.join(input_tmp, *src.split('/'))
1787 common.ZipWrite(target_zip, unzipped_file, arcname=dst)
1788
Yifan Hongb433eba2019-03-06 12:42:53 -08001789 # Write new ab_partitions.txt file
1790 common.ZipWrite(target_zip, new_ab_partitions, arcname=AB_PARTITIONS)
1791
Yifan Hong50e79542018-11-08 17:44:12 -08001792 common.ZipClose(target_zip)
1793
1794 return target_file
1795
1796
Tao Baof0c4aa22018-04-30 20:29:30 -07001797def GenerateAbOtaPackage(target_file, output_file, source_file=None):
Tao Baofe5b69a2018-03-02 09:47:43 -08001798 """Generates an Android OTA package that has A/B update payload."""
Tao Baodea0f8b2016-06-20 17:55:06 -07001799 # Stage the output zip package for package signing.
Tao Bao491d7e22018-02-21 13:17:22 -08001800 if not OPTIONS.no_signing:
1801 staging_file = common.MakeTempFile(suffix='.zip')
1802 else:
1803 staging_file = output_file
Tao Baoa652c002018-03-01 19:31:38 -08001804 output_zip = zipfile.ZipFile(staging_file, "w",
Tao Baoc098e9e2016-01-07 13:03:56 -08001805 compression=zipfile.ZIP_DEFLATED)
1806
Tao Bao481bab82017-12-21 11:23:09 -08001807 if source_file is not None:
Tao Bao1c320f82019-10-04 23:25:12 -07001808 target_info = common.BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1809 source_info = common.BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Tao Bao481bab82017-12-21 11:23:09 -08001810 else:
Tao Bao1c320f82019-10-04 23:25:12 -07001811 target_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Tao Bao481bab82017-12-21 11:23:09 -08001812 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001813
Tao Bao481bab82017-12-21 11:23:09 -08001814 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001815 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001816
Yifan Hong50e79542018-11-08 17:44:12 -08001817 if OPTIONS.retrofit_dynamic_partitions:
1818 target_file = GetTargetFilesZipForRetrofitDynamicPartitions(
Yifan Hongb433eba2019-03-06 12:42:53 -08001819 target_file, target_info.get("super_block_devices").strip().split(),
1820 target_info.get("dynamic_partition_list").strip().split())
Yifan Hong50e79542018-11-08 17:44:12 -08001821 elif OPTIONS.skip_postinstall:
Tao Bao15a146a2018-02-21 16:06:59 -08001822 target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
1823
Tao Bao40b18822018-01-30 18:19:04 -08001824 # Generate payload.
1825 payload = Payload()
1826
1827 # Enforce a max timestamp this payload can be applied on top of.
Tao Baoff1b86e2017-10-03 14:17:57 -07001828 if OPTIONS.downgrade:
Tao Bao2a12ed72018-01-22 11:35:00 -08001829 max_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baoff1b86e2017-10-03 14:17:57 -07001830 else:
1831 max_timestamp = metadata["post-timestamp"]
Tao Bao40b18822018-01-30 18:19:04 -08001832 additional_args = ["--max_timestamp", max_timestamp]
Tao Baoc098e9e2016-01-07 13:03:56 -08001833
Tao Bao40b18822018-01-30 18:19:04 -08001834 payload.Generate(target_file, source_file, additional_args)
Tao Baoc098e9e2016-01-07 13:03:56 -08001835
Tao Bao40b18822018-01-30 18:19:04 -08001836 # Sign the payload.
Tao Baof7140c02018-01-30 17:09:24 -08001837 payload_signer = PayloadSigner()
1838 payload.Sign(payload_signer)
Tao Baoc098e9e2016-01-07 13:03:56 -08001839
Tao Bao40b18822018-01-30 18:19:04 -08001840 # Write the payload into output zip.
1841 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001842
Tao Baof7140c02018-01-30 17:09:24 -08001843 # Generate and include the secondary payload that installs secondary images
1844 # (e.g. system_other.img).
1845 if OPTIONS.include_secondary:
1846 # We always include a full payload for the secondary slot, even when
1847 # building an incremental OTA. See the comments for "--include_secondary".
Tao Bao15a146a2018-02-21 16:06:59 -08001848 secondary_target_file = GetTargetFilesZipForSecondaryImages(
1849 target_file, OPTIONS.skip_postinstall)
Tao Bao667ff572018-02-10 00:02:40 -08001850 secondary_payload = Payload(secondary=True)
Tao Baodb1fe412018-02-09 23:15:05 -08001851 secondary_payload.Generate(secondary_target_file,
1852 additional_args=additional_args)
Tao Baof7140c02018-01-30 17:09:24 -08001853 secondary_payload.Sign(payload_signer)
Tao Bao667ff572018-02-10 00:02:40 -08001854 secondary_payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08001855
Tianjie Xucfa86222016-03-07 16:31:19 -08001856 # If dm-verity is supported for the device, copy contents of care_map
1857 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001858 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08001859 if (target_info.get("verity") == "true" or
1860 target_info.get("avb_enable") == "true"):
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001861 care_map_list = [x for x in ["care_map.pb", "care_map.txt"] if
1862 "META/" + x in target_zip.namelist()]
1863
1864 # Adds care_map if either the protobuf format or the plain text one exists.
1865 if care_map_list:
1866 care_map_name = care_map_list[0]
1867 care_map_data = target_zip.read("META/" + care_map_name)
1868 # In order to support streaming, care_map needs to be packed as
Tao Bao40b18822018-01-30 18:19:04 -08001869 # ZIP_STORED.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001870 common.ZipWriteStr(output_zip, care_map_name, care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08001871 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001872 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001873 logger.warning("Cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07001874
Tao Bao21803d32017-04-19 10:16:09 -07001875 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001876
Yifan Hong9276cf02019-08-21 16:37:04 -07001877 CheckVintfIfTrebleEnabled(target_file, target_info)
1878
Tao Baofe5b69a2018-03-02 09:47:43 -08001879 # We haven't written the metadata entry yet, which will be handled in
1880 # FinalizeMetadata().
Tao Baoc96316c2017-01-24 22:10:49 -08001881 common.ZipClose(output_zip)
1882
Tao Bao85f16982018-03-08 16:28:33 -08001883 # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers
1884 # all the info of the latter. However, system updaters and OTA servers need to
1885 # take time to switch to the new flag. We keep both of the flags for
1886 # P-timeframe, and will remove StreamingPropertyFiles in later release.
Tao Baod3fc38a2018-03-08 16:09:01 -08001887 needed_property_files = (
Tao Bao85f16982018-03-08 16:28:33 -08001888 AbOtaPropertyFiles(),
Tao Baod3fc38a2018-03-08 16:09:01 -08001889 StreamingPropertyFiles(),
1890 )
1891 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Tao Baoc96316c2017-01-24 22:10:49 -08001892
Tao Baoc098e9e2016-01-07 13:03:56 -08001893
Tao Baof0c4aa22018-04-30 20:29:30 -07001894def GenerateNonAbOtaPackage(target_file, output_file, source_file=None):
1895 """Generates a non-A/B OTA package."""
1896 # Sanity check the loaded info dicts first.
1897 if OPTIONS.info_dict.get("no_recovery") == "true":
1898 raise common.ExternalError(
1899 "--- target build has specified no recovery ---")
1900
1901 # Non-A/B OTAs rely on /cache partition to store temporary files.
1902 cache_size = OPTIONS.info_dict.get("cache_size")
1903 if cache_size is None:
1904 logger.warning("--- can't determine the cache partition size ---")
1905 OPTIONS.cache_size = cache_size
1906
1907 if OPTIONS.extra_script is not None:
1908 with open(OPTIONS.extra_script) as fp:
1909 OPTIONS.extra_script = fp.read()
1910
1911 if OPTIONS.extracted_input is not None:
1912 OPTIONS.input_tmp = OPTIONS.extracted_input
1913 else:
1914 logger.info("unzipping target target-files...")
1915 OPTIONS.input_tmp = common.UnzipTemp(target_file, UNZIP_PATTERN)
1916 OPTIONS.target_tmp = OPTIONS.input_tmp
1917
1918 # If the caller explicitly specified the device-specific extensions path via
1919 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
1920 # is present in the target target_files. Otherwise, take the path of the file
1921 # from 'tool_extensions' in the info dict and look for that in the local
1922 # filesystem, relative to the current directory.
1923 if OPTIONS.device_specific is None:
1924 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1925 if os.path.exists(from_input):
1926 logger.info("(using device-specific extensions from target_files)")
1927 OPTIONS.device_specific = from_input
1928 else:
1929 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
1930
1931 if OPTIONS.device_specific is not None:
1932 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
1933
1934 # Generate a full OTA.
1935 if source_file is None:
1936 with zipfile.ZipFile(target_file) as input_zip:
1937 WriteFullOTAPackage(
1938 input_zip,
1939 output_file)
1940
1941 # Generate an incremental OTA.
1942 else:
1943 logger.info("unzipping source target-files...")
1944 OPTIONS.source_tmp = common.UnzipTemp(
1945 OPTIONS.incremental_source, UNZIP_PATTERN)
1946 with zipfile.ZipFile(target_file) as input_zip, \
1947 zipfile.ZipFile(source_file) as source_zip:
1948 WriteBlockIncrementalOTAPackage(
1949 input_zip,
1950 source_zip,
1951 output_file)
1952
1953
Doug Zongkereef39442009-04-02 12:14:19 -07001954def main(argv):
1955
1956 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07001957 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07001958 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001959 elif o in ("-i", "--incremental_from"):
1960 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001961 elif o == "--full_radio":
1962 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001963 elif o == "--full_bootloader":
1964 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08001965 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001966 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08001967 elif o == "--downgrade":
1968 OPTIONS.downgrade = True
1969 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08001970 elif o == "--override_timestamp":
Tao Baofaa8e0b2018-04-12 14:31:43 -07001971 OPTIONS.downgrade = True
Michael Runge6e836112014-04-15 17:40:21 -07001972 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001973 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08001974 elif o == "--oem_no_mount":
1975 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001976 elif o in ("-e", "--extra_script"):
1977 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001978 elif o in ("-t", "--worker_threads"):
1979 if a.isdigit():
1980 OPTIONS.worker_threads = int(a)
1981 else:
1982 raise ValueError("Cannot parse value %r for option %r - only "
1983 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001984 elif o in ("-2", "--two_step"):
1985 OPTIONS.two_step = True
Tao Baof7140c02018-01-30 17:09:24 -08001986 elif o == "--include_secondary":
1987 OPTIONS.include_secondary = True
Doug Zongker26e66192014-02-20 13:22:07 -08001988 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001989 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001990 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001991 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001992 elif o == "--block":
1993 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001994 elif o in ("-b", "--binary"):
1995 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07001996 elif o == "--stash_threshold":
1997 try:
1998 OPTIONS.stash_threshold = float(a)
1999 except ValueError:
2000 raise ValueError("Cannot parse value %r for option %r - expecting "
2001 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08002002 elif o == "--log_diff":
2003 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002004 elif o == "--payload_signer":
2005 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002006 elif o == "--payload_signer_args":
2007 OPTIONS.payload_signer_args = shlex.split(a)
xunchang376cc7c2019-04-08 23:04:58 -07002008 elif o == "--payload_signer_key_size":
2009 OPTIONS.payload_signer_key_size = a
Dan Willemsencea5cd22017-03-21 14:44:27 -07002010 elif o == "--extracted_input_target_files":
2011 OPTIONS.extracted_input = a
Tao Bao15a146a2018-02-21 16:06:59 -08002012 elif o == "--skip_postinstall":
2013 OPTIONS.skip_postinstall = True
Yifan Hong50e79542018-11-08 17:44:12 -08002014 elif o == "--retrofit_dynamic_partitions":
2015 OPTIONS.retrofit_dynamic_partitions = True
xunchangabfa2652019-02-19 16:27:10 -08002016 elif o == "--skip_compatibility_check":
2017 OPTIONS.skip_compatibility_check = True
xunchang1cfe2512019-02-19 14:14:48 -08002018 elif o == "--output_metadata_path":
2019 OPTIONS.output_metadata_path = a
Tianjie Xu1b079832019-08-28 12:19:23 -07002020 elif o == "--disable_fec_computation":
2021 OPTIONS.disable_fec_computation = True
Doug Zongkereef39442009-04-02 12:14:19 -07002022 else:
2023 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002024 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002025
2026 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08002027 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002028 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07002029 "package_key=",
2030 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002031 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002032 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002033 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08002034 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08002035 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07002036 "extra_script=",
2037 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002038 "two_step",
Tao Baof7140c02018-01-30 17:09:24 -08002039 "include_secondary",
Dan Albert8b72aef2015-03-23 19:13:21 -07002040 "no_signing",
2041 "block",
2042 "binary=",
2043 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08002044 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002045 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07002046 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002047 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002048 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002049 "payload_signer_args=",
xunchang376cc7c2019-04-08 23:04:58 -07002050 "payload_signer_key_size=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07002051 "extracted_input_target_files=",
Tao Bao15a146a2018-02-21 16:06:59 -08002052 "skip_postinstall",
Yifan Hong50e79542018-11-08 17:44:12 -08002053 "retrofit_dynamic_partitions",
xunchangabfa2652019-02-19 16:27:10 -08002054 "skip_compatibility_check",
xunchang1cfe2512019-02-19 14:14:48 -08002055 "output_metadata_path=",
Tianjie Xu1b079832019-08-28 12:19:23 -07002056 "disable_fec_computation",
Dan Albert8b72aef2015-03-23 19:13:21 -07002057 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002058
2059 if len(args) != 2:
2060 common.Usage(__doc__)
2061 sys.exit(1)
2062
Tao Bao32fcdab2018-10-12 10:30:39 -07002063 common.InitLogging()
2064
Tao Bao5d182562016-02-23 11:38:39 -08002065 if OPTIONS.downgrade:
Tao Bao5d182562016-02-23 11:38:39 -08002066 # We should only allow downgrading incrementals (as opposed to full).
2067 # Otherwise the device may go back from arbitrary build with this full
2068 # OTA package.
2069 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002070 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08002071
Tao Bao2db13852018-01-08 22:28:57 -08002072 # Load the build info dicts from the zip directly or the extracted input
2073 # directory. We don't need to unzip the entire target-files zips, because they
2074 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
2075 # When loading the info dicts, we don't need to provide the second parameter
2076 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
2077 # some properties with their actual paths, such as 'selinux_fc',
2078 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07002079 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08002080 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07002081 else:
Tao Bao2db13852018-01-08 22:28:57 -08002082 with zipfile.ZipFile(args[0], 'r') as input_zip:
2083 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08002084
Tao Bao32fcdab2018-10-12 10:30:39 -07002085 logger.info("--- target info ---")
2086 common.DumpInfoDict(OPTIONS.info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002087
2088 # Load the source build dict if applicable.
2089 if OPTIONS.incremental_source is not None:
2090 OPTIONS.target_info_dict = OPTIONS.info_dict
2091 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
2092 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2093
Tao Bao32fcdab2018-10-12 10:30:39 -07002094 logger.info("--- source info ---")
2095 common.DumpInfoDict(OPTIONS.source_info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002096
2097 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08002098 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
2099
Yifan Hong50e79542018-11-08 17:44:12 -08002100 # Assume retrofitting dynamic partitions when base build does not set
Yifan Hong50611032018-11-20 14:27:38 -08002101 # use_dynamic_partitions but target build does.
Yifan Hong50e79542018-11-08 17:44:12 -08002102 if (OPTIONS.source_info_dict and
Yifan Hong50611032018-11-20 14:27:38 -08002103 OPTIONS.source_info_dict.get("use_dynamic_partitions") != "true" and
2104 OPTIONS.target_info_dict.get("use_dynamic_partitions") == "true"):
Yifan Hong50e79542018-11-08 17:44:12 -08002105 if OPTIONS.target_info_dict.get("dynamic_partition_retrofit") != "true":
2106 raise common.ExternalError(
2107 "Expect to generate incremental OTA for retrofitting dynamic "
2108 "partitions, but dynamic_partition_retrofit is not set in target "
2109 "build.")
2110 logger.info("Implicitly generating retrofit incremental OTA.")
2111 OPTIONS.retrofit_dynamic_partitions = True
2112
2113 # Skip postinstall for retrofitting dynamic partitions.
2114 if OPTIONS.retrofit_dynamic_partitions:
2115 OPTIONS.skip_postinstall = True
2116
Tao Baoc098e9e2016-01-07 13:03:56 -08002117 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2118
Christian Oderf63e2cd2017-05-01 22:30:15 +02002119 # Use the default key to sign the package if not specified with package_key.
2120 # package_keys are needed on ab_updates, so always define them if an
2121 # ab_update is getting created.
2122 if not OPTIONS.no_signing or ab_update:
2123 if OPTIONS.package_key is None:
2124 OPTIONS.package_key = OPTIONS.info_dict.get(
2125 "default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -07002126 "build/make/target/product/security/testkey")
Christian Oderf63e2cd2017-05-01 22:30:15 +02002127 # Get signing keys
2128 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
2129
Tao Baoc098e9e2016-01-07 13:03:56 -08002130 if ab_update:
Tao Baof0c4aa22018-04-30 20:29:30 -07002131 GenerateAbOtaPackage(
Tao Baoc098e9e2016-01-07 13:03:56 -08002132 target_file=args[0],
2133 output_file=args[1],
2134 source_file=OPTIONS.incremental_source)
2135
Dan Willemsencea5cd22017-03-21 14:44:27 -07002136 else:
Tao Baof0c4aa22018-04-30 20:29:30 -07002137 GenerateNonAbOtaPackage(
2138 target_file=args[0],
2139 output_file=args[1],
2140 source_file=OPTIONS.incremental_source)
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002141
Tao Baof0c4aa22018-04-30 20:29:30 -07002142 # Post OTA generation works.
2143 if OPTIONS.incremental_source is not None and OPTIONS.log_diff:
2144 logger.info("Generating diff logs...")
2145 logger.info("Unzipping target-files for diffing...")
2146 target_dir = common.UnzipTemp(args[0], TARGET_DIFFING_UNZIP_PATTERN)
2147 source_dir = common.UnzipTemp(
2148 OPTIONS.incremental_source, TARGET_DIFFING_UNZIP_PATTERN)
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002149
Tao Baof0c4aa22018-04-30 20:29:30 -07002150 with open(OPTIONS.log_diff, 'w') as out_file:
2151 import target_files_diff
2152 target_files_diff.recursiveDiff(
2153 '', source_dir, target_dir, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07002154
Tao Bao32fcdab2018-10-12 10:30:39 -07002155 logger.info("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002156
2157
2158if __name__ == '__main__':
2159 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002160 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002161 main(sys.argv[1:])
Tao Bao32fcdab2018-10-12 10:30:39 -07002162 except common.ExternalError:
2163 logger.exception("\n ERROR:\n")
Doug Zongkereef39442009-04-02 12:14:19 -07002164 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002165 finally:
2166 common.Cleanup()