blob: 92a46a20cd2c41a429d346cbe7f329f2e2c84fac [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
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700174 --payload_signer_maximum_signature_size <signature_size>
175 The maximum signature size (in bytes) that would be generated by the given
176 payload signer. Only meaningful when custom payload signer is specified
177 via '--payload_signer'.
178 If the signer uses a RSA key, this should be the number of bytes to
179 represent the modulus. If it uses an EC key, this is the size of a
180 DER-encoded ECDSA signature.
181
xunchang376cc7c2019-04-08 23:04:58 -0700182 --payload_signer_key_size <key_size>
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700183 Deprecated. Use the '--payload_signer_maximum_signature_size' instead.
xunchang376cc7c2019-04-08 23:04:58 -0700184
Tao Bao15a146a2018-02-21 16:06:59 -0800185 --skip_postinstall
186 Skip the postinstall hooks when generating an A/B OTA package (default:
187 False). Note that this discards ALL the hooks, including non-optional
188 ones. Should only be used if caller knows it's safe to do so (e.g. all the
189 postinstall work is to dexopt apps and a data wipe will happen immediately
190 after). Only meaningful when generating A/B OTAs.
Doug Zongkereef39442009-04-02 12:14:19 -0700191"""
192
Tao Bao89fbb0f2017-01-10 10:47:58 -0800193from __future__ import print_function
194
Tianjie Xuf67dd802019-05-20 17:50:36 -0700195import collections
Tao Bao32fcdab2018-10-12 10:30:39 -0700196import logging
Doug Zongkerfc44a512014-08-26 13:10:25 -0700197import multiprocessing
Tao Bao2dd1c482017-02-03 16:49:39 -0800198import os.path
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700199import shlex
Tao Bao15a146a2018-02-21 16:06:59 -0800200import shutil
Tao Bao85f16982018-03-08 16:28:33 -0800201import struct
Tao Bao481bab82017-12-21 11:23:09 -0800202import sys
Doug Zongkereef39442009-04-02 12:14:19 -0700203import zipfile
204
Yifan Hong9276cf02019-08-21 16:37:04 -0700205import check_target_files_vintf
Doug Zongkereef39442009-04-02 12:14:19 -0700206import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700207import edify_generator
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700208import verity_utils
Doug Zongkereef39442009-04-02 12:14:19 -0700209
Tao Bao481bab82017-12-21 11:23:09 -0800210if sys.hexversion < 0x02070000:
211 print("Python 2.7 or newer is required.", file=sys.stderr)
212 sys.exit(1)
213
Tao Bao32fcdab2018-10-12 10:30:39 -0700214logger = logging.getLogger(__name__)
Tao Bao481bab82017-12-21 11:23:09 -0800215
Doug Zongkereef39442009-04-02 12:14:19 -0700216OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700217OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700218OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700219OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700220OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700221OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800222OPTIONS.downgrade = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700223OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700224OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
225if OPTIONS.worker_threads == 0:
226 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800227OPTIONS.two_step = False
Tao Baof7140c02018-01-30 17:09:24 -0800228OPTIONS.include_secondary = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900229OPTIONS.no_signing = False
Tao Bao457cbf62017-03-06 09:56:01 -0800230OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -0800231OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700232OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800233OPTIONS.oem_no_mount = False
Tao Bao43078aa2015-04-21 14:32:35 -0700234OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700235OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700236# Stash size cannot exceed cache_size * threshold.
237OPTIONS.cache_size = None
238OPTIONS.stash_threshold = 0.8
Tao Baod62c6032015-11-30 09:40:20 -0800239OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700240OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700241OPTIONS.payload_signer_args = []
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700242OPTIONS.payload_signer_maximum_signature_size = None
Tao Bao5f8ff932017-03-21 22:35:00 -0700243OPTIONS.extracted_input = None
Christian Oderf63e2cd2017-05-01 22:30:15 +0200244OPTIONS.key_passwords = []
Tao Bao15a146a2018-02-21 16:06:59 -0800245OPTIONS.skip_postinstall = False
Yifan Hong50e79542018-11-08 17:44:12 -0800246OPTIONS.retrofit_dynamic_partitions = False
xunchangabfa2652019-02-19 16:27:10 -0800247OPTIONS.skip_compatibility_check = False
xunchang1cfe2512019-02-19 14:14:48 -0800248OPTIONS.output_metadata_path = None
Tianjie Xu1b079832019-08-28 12:19:23 -0700249OPTIONS.disable_fec_computation = False
Tao Bao15a146a2018-02-21 16:06:59 -0800250
Tao Bao8dcf7382015-05-21 14:09:49 -0700251
Tao Bao2dd1c482017-02-03 16:49:39 -0800252METADATA_NAME = 'META-INF/com/android/metadata'
Tao Bao15a146a2018-02-21 16:06:59 -0800253POSTINSTALL_CONFIG = 'META/postinstall_config.txt'
Yifan Hong50e79542018-11-08 17:44:12 -0800254DYNAMIC_PARTITION_INFO = 'META/dynamic_partitions_info.txt'
Yifan Hongb433eba2019-03-06 12:42:53 -0800255AB_PARTITIONS = 'META/ab_partitions.txt'
Tao Bao04808502019-07-25 23:11:41 -0700256UNZIP_PATTERN = ['IMAGES/*', 'META/*', 'OTA/*', 'RADIO/*']
Tao Baof0c4aa22018-04-30 20:29:30 -0700257# Files to be unzipped for target diffing purpose.
258TARGET_DIFFING_UNZIP_PATTERN = ['BOOT', 'RECOVERY', 'SYSTEM/*', 'VENDOR/*',
259 'PRODUCT/*', 'SYSTEM_EXT/*', 'ODM/*']
Yifan Hongb433eba2019-03-06 12:42:53 -0800260RETROFIT_DAP_UNZIP_PATTERN = ['OTA/super_*.img', AB_PARTITIONS]
Tao Bao3e759462019-09-17 22:43:11 -0700261
262# Images to be excluded from secondary payload. We essentially only keep
263# 'system_other' and bootloader partitions.
264SECONDARY_PAYLOAD_SKIPPED_IMAGES = [
265 'boot', 'dtbo', 'modem', 'odm', 'product', 'radio', 'recovery',
266 'system_ext', 'vbmeta', 'vbmeta_system', 'vbmeta_vendor', 'vendor']
Tao Bao6b0b2f92017-03-05 11:38:11 -0800267
Tao Bao2dd1c482017-02-03 16:49:39 -0800268
Tao Baofabe0832018-01-17 15:52:28 -0800269class PayloadSigner(object):
270 """A class that wraps the payload signing works.
271
272 When generating a Payload, hashes of the payload and metadata files will be
273 signed with the device key, either by calling an external payload signer or
274 by calling openssl with the package key. This class provides a unified
275 interface, so that callers can just call PayloadSigner.Sign().
276
277 If an external payload signer has been specified (OPTIONS.payload_signer), it
278 calls the signer with the provided args (OPTIONS.payload_signer_args). Note
279 that the signing key should be provided as part of the payload_signer_args.
280 Otherwise without an external signer, it uses the package key
281 (OPTIONS.package_key) and calls openssl for the signing works.
282 """
283
284 def __init__(self):
285 if OPTIONS.payload_signer is None:
286 # Prepare the payload signing key.
287 private_key = OPTIONS.package_key + OPTIONS.private_key_suffix
288 pw = OPTIONS.key_passwords[OPTIONS.package_key]
289
290 cmd = ["openssl", "pkcs8", "-in", private_key, "-inform", "DER"]
291 cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"])
292 signing_key = common.MakeTempFile(prefix="key-", suffix=".key")
293 cmd.extend(["-out", signing_key])
Tao Baobec89c12018-10-15 11:53:28 -0700294 common.RunAndCheckOutput(cmd, verbose=False)
Tao Baofabe0832018-01-17 15:52:28 -0800295
296 self.signer = "openssl"
297 self.signer_args = ["pkeyutl", "-sign", "-inkey", signing_key,
298 "-pkeyopt", "digest:sha256"]
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700299 self.maximum_signature_size = self._GetMaximumSignatureSizeInBytes(
300 signing_key)
Tao Baofabe0832018-01-17 15:52:28 -0800301 else:
302 self.signer = OPTIONS.payload_signer
303 self.signer_args = OPTIONS.payload_signer_args
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700304 if OPTIONS.payload_signer_maximum_signature_size:
305 self.maximum_signature_size = int(
306 OPTIONS.payload_signer_maximum_signature_size)
xunchang376cc7c2019-04-08 23:04:58 -0700307 else:
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700308 # The legacy config uses RSA2048 keys.
309 logger.warning("The maximum signature size for payload signer is not"
310 " set, default to 256 bytes.")
311 self.maximum_signature_size = 256
xunchang376cc7c2019-04-08 23:04:58 -0700312
313 @staticmethod
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700314 def _GetMaximumSignatureSizeInBytes(signing_key):
315 out_signature_size_file = common.MakeTempFile("signature_size")
316 cmd = ["delta_generator", "--out_maximum_signature_size_file={}".format(
317 out_signature_size_file), "--private_key={}".format(signing_key)]
318 common.RunAndCheckOutput(cmd)
319 with open(out_signature_size_file) as f:
320 signature_size = f.read().rstrip()
Luca Stefani88e1a142020-03-27 14:05:12 +0100321 logger.info("%s outputs the maximum signature size: %s", cmd[0],
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700322 signature_size)
323 return int(signature_size)
Tao Baofabe0832018-01-17 15:52:28 -0800324
325 def Sign(self, in_file):
326 """Signs the given input file. Returns the output filename."""
327 out_file = common.MakeTempFile(prefix="signed-", suffix=".bin")
328 cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file]
Tao Bao718faed2019-08-02 13:24:19 -0700329 common.RunAndCheckOutput(cmd)
Tao Baofabe0832018-01-17 15:52:28 -0800330 return out_file
331
332
Tao Bao40b18822018-01-30 18:19:04 -0800333class Payload(object):
334 """Manages the creation and the signing of an A/B OTA Payload."""
335
336 PAYLOAD_BIN = 'payload.bin'
337 PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
Tao Baof7140c02018-01-30 17:09:24 -0800338 SECONDARY_PAYLOAD_BIN = 'secondary/payload.bin'
339 SECONDARY_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt'
Tao Bao40b18822018-01-30 18:19:04 -0800340
Tao Bao667ff572018-02-10 00:02:40 -0800341 def __init__(self, secondary=False):
342 """Initializes a Payload instance.
343
344 Args:
345 secondary: Whether it's generating a secondary payload (default: False).
346 """
Tao Bao40b18822018-01-30 18:19:04 -0800347 self.payload_file = None
348 self.payload_properties = None
Tao Bao667ff572018-02-10 00:02:40 -0800349 self.secondary = secondary
Tao Bao40b18822018-01-30 18:19:04 -0800350
Tao Baof0c4aa22018-04-30 20:29:30 -0700351 def _Run(self, cmd): # pylint: disable=no-self-use
Tao Bao718faed2019-08-02 13:24:19 -0700352 # Don't pipe (buffer) the output if verbose is set. Let
353 # brillo_update_payload write to stdout/stderr directly, so its progress can
354 # be monitored.
355 if OPTIONS.verbose:
356 common.RunAndCheckOutput(cmd, stdout=None, stderr=None)
357 else:
358 common.RunAndCheckOutput(cmd)
359
Tao Bao40b18822018-01-30 18:19:04 -0800360 def Generate(self, target_file, source_file=None, additional_args=None):
361 """Generates a payload from the given target-files zip(s).
362
363 Args:
364 target_file: The filename of the target build target-files zip.
365 source_file: The filename of the source build target-files zip; or None if
366 generating a full OTA.
367 additional_args: A list of additional args that should be passed to
368 brillo_update_payload script; or None.
369 """
370 if additional_args is None:
371 additional_args = []
372
373 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
374 cmd = ["brillo_update_payload", "generate",
375 "--payload", payload_file,
376 "--target_image", target_file]
377 if source_file is not None:
378 cmd.extend(["--source_image", source_file])
Tianjie Xu1b079832019-08-28 12:19:23 -0700379 if OPTIONS.disable_fec_computation:
380 cmd.extend(["--disable_fec_computation", "true"])
Tao Bao40b18822018-01-30 18:19:04 -0800381 cmd.extend(additional_args)
Tao Bao718faed2019-08-02 13:24:19 -0700382 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800383
384 self.payload_file = payload_file
385 self.payload_properties = None
386
387 def Sign(self, payload_signer):
388 """Generates and signs the hashes of the payload and metadata.
389
390 Args:
391 payload_signer: A PayloadSigner() instance that serves the signing work.
392
393 Raises:
394 AssertionError: On any failure when calling brillo_update_payload script.
395 """
396 assert isinstance(payload_signer, PayloadSigner)
397
398 # 1. Generate hashes of the payload and metadata files.
399 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
400 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
401 cmd = ["brillo_update_payload", "hash",
402 "--unsigned_payload", self.payload_file,
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700403 "--signature_size", str(payload_signer.maximum_signature_size),
Tao Bao40b18822018-01-30 18:19:04 -0800404 "--metadata_hash_file", metadata_sig_file,
405 "--payload_hash_file", payload_sig_file]
Tao Bao718faed2019-08-02 13:24:19 -0700406 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800407
408 # 2. Sign the hashes.
409 signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
410 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
411
412 # 3. Insert the signatures back into the payload file.
413 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
414 suffix=".bin")
415 cmd = ["brillo_update_payload", "sign",
416 "--unsigned_payload", self.payload_file,
417 "--payload", signed_payload_file,
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700418 "--signature_size", str(payload_signer.maximum_signature_size),
Tao Bao40b18822018-01-30 18:19:04 -0800419 "--metadata_signature_file", signed_metadata_sig_file,
420 "--payload_signature_file", signed_payload_sig_file]
Tao Bao718faed2019-08-02 13:24:19 -0700421 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800422
423 # 4. Dump the signed payload properties.
424 properties_file = common.MakeTempFile(prefix="payload-properties-",
425 suffix=".txt")
426 cmd = ["brillo_update_payload", "properties",
427 "--payload", signed_payload_file,
428 "--properties_file", properties_file]
Tao Bao718faed2019-08-02 13:24:19 -0700429 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800430
Tao Bao667ff572018-02-10 00:02:40 -0800431 if self.secondary:
432 with open(properties_file, "a") as f:
433 f.write("SWITCH_SLOT_ON_REBOOT=0\n")
434
Tao Bao40b18822018-01-30 18:19:04 -0800435 if OPTIONS.wipe_user_data:
436 with open(properties_file, "a") as f:
437 f.write("POWERWASH=1\n")
438
439 self.payload_file = signed_payload_file
440 self.payload_properties = properties_file
441
Tao Bao667ff572018-02-10 00:02:40 -0800442 def WriteToZip(self, output_zip):
Tao Bao40b18822018-01-30 18:19:04 -0800443 """Writes the payload to the given zip.
444
445 Args:
446 output_zip: The output ZipFile instance.
447 """
448 assert self.payload_file is not None
449 assert self.payload_properties is not None
450
Tao Bao667ff572018-02-10 00:02:40 -0800451 if self.secondary:
Tao Baof7140c02018-01-30 17:09:24 -0800452 payload_arcname = Payload.SECONDARY_PAYLOAD_BIN
453 payload_properties_arcname = Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT
454 else:
455 payload_arcname = Payload.PAYLOAD_BIN
456 payload_properties_arcname = Payload.PAYLOAD_PROPERTIES_TXT
457
Tao Bao40b18822018-01-30 18:19:04 -0800458 # Add the signed payload file and properties into the zip. In order to
459 # support streaming, we pack them as ZIP_STORED. So these entries can be
460 # read directly with the offset and length pairs.
Tao Baof7140c02018-01-30 17:09:24 -0800461 common.ZipWrite(output_zip, self.payload_file, arcname=payload_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800462 compress_type=zipfile.ZIP_STORED)
463 common.ZipWrite(output_zip, self.payload_properties,
Tao Baof7140c02018-01-30 17:09:24 -0800464 arcname=payload_properties_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800465 compress_type=zipfile.ZIP_STORED)
466
467
Doug Zongkereef39442009-04-02 12:14:19 -0700468def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200469 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700470
Doug Zongker951495f2009-08-14 12:44:19 -0700471 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
472 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700473
474
Tao Bao481bab82017-12-21 11:23:09 -0800475def _LoadOemDicts(oem_source):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800476 """Returns the list of loaded OEM properties dict."""
Tao Bao481bab82017-12-21 11:23:09 -0800477 if not oem_source:
478 return None
479
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800480 oem_dicts = []
Tao Bao481bab82017-12-21 11:23:09 -0800481 for oem_file in oem_source:
482 with open(oem_file) as fp:
483 oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines()))
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800484 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700485
Doug Zongkereef39442009-04-02 12:14:19 -0700486
Tao Baod42e97e2016-11-30 12:11:57 -0800487def _WriteRecoveryImageToBoot(script, output_zip):
488 """Find and write recovery image to /boot in two-step OTA.
489
490 In two-step OTAs, we write recovery image to /boot as the first step so that
491 we can reboot to there and install a new recovery image to /recovery.
492 A special "recovery-two-step.img" will be preferred, which encodes the correct
493 path of "/boot". Otherwise the device may show "device is corrupt" message
494 when booting into /boot.
495
496 Fall back to using the regular recovery.img if the two-step recovery image
497 doesn't exist. Note that rebuilding the special image at this point may be
498 infeasible, because we don't have the desired boot signer and keys when
499 calling ota_from_target_files.py.
500 """
501
502 recovery_two_step_img_name = "recovery-two-step.img"
503 recovery_two_step_img_path = os.path.join(
Tao Bao04808502019-07-25 23:11:41 -0700504 OPTIONS.input_tmp, "OTA", recovery_two_step_img_name)
Tao Baod42e97e2016-11-30 12:11:57 -0800505 if os.path.exists(recovery_two_step_img_path):
Tao Bao04808502019-07-25 23:11:41 -0700506 common.ZipWrite(
507 output_zip,
508 recovery_two_step_img_path,
509 arcname=recovery_two_step_img_name)
Tao Bao32fcdab2018-10-12 10:30:39 -0700510 logger.info(
511 "two-step package: using %s in stage 1/3", recovery_two_step_img_name)
Tao Baod42e97e2016-11-30 12:11:57 -0800512 script.WriteRawImage("/boot", recovery_two_step_img_name)
513 else:
Tao Bao32fcdab2018-10-12 10:30:39 -0700514 logger.info("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800515 # The "recovery.img" entry has been written into package earlier.
516 script.WriteRawImage("/boot", "recovery.img")
517
518
Bill Peckhame868aec2019-09-17 17:06:47 -0700519def HasRecoveryPatch(target_files_zip, info_dict):
520 board_uses_vendorimage = info_dict.get("board_uses_vendorimage") == "true"
521
522 if board_uses_vendorimage:
523 target_files_dir = "VENDOR"
524 else:
525 target_files_dir = "SYSTEM/vendor"
526
527 patch = "%s/recovery-from-boot.p" % target_files_dir
528 img = "%s/etc/recovery.img" %target_files_dir
529
Tao Baof2cffbd2015-07-22 12:33:18 -0700530 namelist = [name for name in target_files_zip.namelist()]
Bill Peckhame868aec2019-09-17 17:06:47 -0700531 return (patch in namelist or img in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700532
Tao Bao457cbf62017-03-06 09:56:01 -0800533
Yifan Hong51d37562019-04-23 17:06:46 -0700534def HasPartition(target_files_zip, partition):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700535 try:
Yifan Hong51d37562019-04-23 17:06:46 -0700536 target_files_zip.getinfo(partition.upper() + "/")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700537 return True
538 except KeyError:
539 return False
540
Tao Bao457cbf62017-03-06 09:56:01 -0800541
Yifan Hong9276cf02019-08-21 16:37:04 -0700542def HasTrebleEnabled(target_files, target_info):
543 def HasVendorPartition(target_files):
544 if os.path.isdir(target_files):
545 return os.path.isdir(os.path.join(target_files, "VENDOR"))
546 if zipfile.is_zipfile(target_files):
547 return HasPartition(zipfile.ZipFile(target_files), "vendor")
548 raise ValueError("Unknown target_files argument")
Yifan Hong51d37562019-04-23 17:06:46 -0700549
Yifan Hong9276cf02019-08-21 16:37:04 -0700550 return (HasVendorPartition(target_files) and
Tao Bao481bab82017-12-21 11:23:09 -0800551 target_info.GetBuildProp("ro.treble.enabled") == "true")
Tao Baobcd1d162017-08-26 13:10:26 -0700552
553
Tao Bao481bab82017-12-21 11:23:09 -0800554def WriteFingerprintAssertion(script, target_info, source_info):
555 source_oem_props = source_info.oem_props
556 target_oem_props = target_info.oem_props
Michael Runge6e836112014-04-15 17:40:21 -0700557
Tao Bao481bab82017-12-21 11:23:09 -0800558 if source_oem_props is None and target_oem_props is None:
559 script.AssertSomeFingerprint(
560 source_info.fingerprint, target_info.fingerprint)
561 elif source_oem_props is not None and target_oem_props is not None:
562 script.AssertSomeThumbprint(
563 target_info.GetBuildProp("ro.build.thumbprint"),
564 source_info.GetBuildProp("ro.build.thumbprint"))
565 elif source_oem_props is None and target_oem_props is not None:
566 script.AssertFingerprintOrThumbprint(
567 source_info.fingerprint,
568 target_info.GetBuildProp("ro.build.thumbprint"))
569 else:
570 script.AssertFingerprintOrThumbprint(
571 target_info.fingerprint,
572 source_info.GetBuildProp("ro.build.thumbprint"))
Doug Zongker73ef8252009-07-23 15:12:53 -0700573
Doug Zongkerfc44a512014-08-26 13:10:25 -0700574
Yifan Hong9276cf02019-08-21 16:37:04 -0700575def CheckVintfIfTrebleEnabled(target_files, target_info):
576 """Checks compatibility info of the input target files.
Tao Bao21803d32017-04-19 10:16:09 -0700577
Yifan Hong9276cf02019-08-21 16:37:04 -0700578 Metadata used for compatibility verification is retrieved from target_zip.
Tao Bao21803d32017-04-19 10:16:09 -0700579
Yifan Hong9276cf02019-08-21 16:37:04 -0700580 Compatibility should only be checked for devices that have enabled
Tao Baobcd1d162017-08-26 13:10:26 -0700581 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700582
583 Args:
Yifan Hong9276cf02019-08-21 16:37:04 -0700584 target_files: Path to zip file containing the source files to be included
585 for OTA. Can also be the path to extracted directory.
Tao Bao481bab82017-12-21 11:23:09 -0800586 target_info: The BuildInfo instance that holds the target build info.
Tao Bao21803d32017-04-19 10:16:09 -0700587 """
588
Tao Baobcd1d162017-08-26 13:10:26 -0700589 # Will only proceed if the target has enabled the Treble support (as well as
590 # having a /vendor partition).
Yifan Hong9276cf02019-08-21 16:37:04 -0700591 if not HasTrebleEnabled(target_files, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700592 return
593
xunchangabfa2652019-02-19 16:27:10 -0800594 # Skip adding the compatibility package as a workaround for b/114240221. The
595 # compatibility will always fail on devices without qualified kernels.
596 if OPTIONS.skip_compatibility_check:
597 return
598
Yifan Hong9276cf02019-08-21 16:37:04 -0700599 if not check_target_files_vintf.CheckVintf(target_files, target_info):
600 raise RuntimeError("VINTF compatibility check failed")
Tao Bao21803d32017-04-19 10:16:09 -0700601
602
Tianjie Xuf67dd802019-05-20 17:50:36 -0700603def GetBlockDifferences(target_zip, source_zip, target_info, source_info,
604 device_specific):
605 """Returns a ordered dict of block differences with partition name as key."""
606
607 def GetIncrementalBlockDifferenceForPartition(name):
608 if not HasPartition(source_zip, name):
609 raise RuntimeError("can't generate incremental that adds {}".format(name))
610
611 partition_src = common.GetUserImage(name, OPTIONS.source_tmp, source_zip,
612 info_dict=source_info,
613 allow_shared_blocks=allow_shared_blocks)
614
615 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
616 name, 4096, target_info)
617 partition_tgt = common.GetUserImage(name, OPTIONS.target_tmp, target_zip,
618 info_dict=target_info,
619 allow_shared_blocks=allow_shared_blocks,
620 hashtree_info_generator=
621 hashtree_info_generator)
622
623 # Check the first block of the source system partition for remount R/W only
624 # if the filesystem is ext4.
625 partition_source_info = source_info["fstab"]["/" + name]
626 check_first_block = partition_source_info.fs_type == "ext4"
627 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
628 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
629 # b) the blocks listed in block map may not contain all the bytes for a
630 # given file (because they're rounded to be 4K-aligned).
631 partition_target_info = target_info["fstab"]["/" + name]
632 disable_imgdiff = (partition_source_info.fs_type == "squashfs" or
633 partition_target_info.fs_type == "squashfs")
Xindong Xu2a7aaa62020-03-13 15:59:22 +0800634 return common.BlockDifference(name, partition_tgt, partition_src,
Tianjie Xuf67dd802019-05-20 17:50:36 -0700635 check_first_block,
636 version=blockimgdiff_version,
637 disable_imgdiff=disable_imgdiff)
638
639 if source_zip:
640 # See notes in common.GetUserImage()
641 allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or
642 target_info.get('ext4_share_dup_blocks') == "true")
643 blockimgdiff_version = max(
644 int(i) for i in target_info.get(
645 "blockimgdiff_versions", "1").split(","))
646 assert blockimgdiff_version >= 3
647
648 block_diff_dict = collections.OrderedDict()
649 partition_names = ["system", "vendor", "product", "odm", "system_ext"]
650 for partition in partition_names:
651 if not HasPartition(target_zip, partition):
652 continue
653 # Full OTA update.
654 if not source_zip:
655 tgt = common.GetUserImage(partition, OPTIONS.input_tmp, target_zip,
656 info_dict=target_info,
657 reset_file_map=True)
658 block_diff_dict[partition] = common.BlockDifference(partition, tgt,
659 src=None)
660 # Incremental OTA update.
661 else:
662 block_diff_dict[partition] = GetIncrementalBlockDifferenceForPartition(
663 partition)
664 assert "system" in block_diff_dict
665
666 # Get the block diffs from the device specific script. If there is a
667 # duplicate block diff for a partition, ignore the diff in the generic script
668 # and use the one in the device specific script instead.
669 if source_zip:
670 device_specific_diffs = device_specific.IncrementalOTA_GetBlockDifferences()
671 function_name = "IncrementalOTA_GetBlockDifferences"
672 else:
673 device_specific_diffs = device_specific.FullOTA_GetBlockDifferences()
674 function_name = "FullOTA_GetBlockDifferences"
675
676 if device_specific_diffs:
677 assert all(isinstance(diff, common.BlockDifference)
678 for diff in device_specific_diffs), \
679 "{} is not returning a list of BlockDifference objects".format(
680 function_name)
681 for diff in device_specific_diffs:
682 if diff.partition in block_diff_dict:
683 logger.warning("Duplicate block difference found. Device specific block"
684 " diff for partition '%s' overrides the one in generic"
685 " script.", diff.partition)
686 block_diff_dict[diff.partition] = diff
687
688 return block_diff_dict
689
690
Tao Bao491d7e22018-02-21 13:17:22 -0800691def WriteFullOTAPackage(input_zip, output_file):
Tao Bao1c320f82019-10-04 23:25:12 -0700692 target_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700693
Tao Bao481bab82017-12-21 11:23:09 -0800694 # We don't know what version it will be installed on top of. We expect the API
695 # just won't change very often. Similarly for fstab, it might have changed in
696 # the target build.
697 target_api_version = target_info["recovery_api_version"]
698 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700699
Tao Bao481bab82017-12-21 11:23:09 -0800700 if target_info.oem_props and not OPTIONS.oem_no_mount:
701 target_info.WriteMountOemScript(script)
702
Tao Baodf3a48b2018-01-10 16:30:43 -0800703 metadata = GetPackageMetadata(target_info)
Doug Zongker2ea21062010-04-28 16:05:21 -0700704
Tao Bao491d7e22018-02-21 13:17:22 -0800705 if not OPTIONS.no_signing:
706 staging_file = common.MakeTempFile(suffix='.zip')
707 else:
708 staging_file = output_file
709
710 output_zip = zipfile.ZipFile(
711 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
712
Doug Zongker05d3dea2009-06-22 11:32:31 -0700713 device_specific = common.DeviceSpecificParams(
714 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800715 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700716 output_zip=output_zip,
717 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700718 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700719 metadata=metadata,
720 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700721
Bill Peckhame868aec2019-09-17 17:06:47 -0700722 assert HasRecoveryPatch(input_zip, info_dict=OPTIONS.info_dict)
Doug Zongkerc9253822014-02-04 12:17:58 -0800723
Tao Bao481bab82017-12-21 11:23:09 -0800724 # Assertions (e.g. downgrade check, device properties check).
725 ts = target_info.GetBuildProp("ro.build.date.utc")
726 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700727 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700728
Tao Bao481bab82017-12-21 11:23:09 -0800729 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700730 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800731
Tianjie Xuf67dd802019-05-20 17:50:36 -0700732 block_diff_dict = GetBlockDifferences(target_zip=input_zip, source_zip=None,
733 target_info=target_info,
734 source_info=None,
735 device_specific=device_specific)
736
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800737 # Two-step package strategy (in chronological order, which is *not*
738 # the order in which the generated script has things):
739 #
740 # if stage is not "2/3" or "3/3":
741 # write recovery image to boot partition
742 # set stage to "2/3"
743 # reboot to boot partition and restart recovery
744 # else if stage is "2/3":
745 # write recovery image to recovery partition
746 # set stage to "3/3"
747 # reboot to recovery partition and restart recovery
748 # else:
749 # (stage must be "3/3")
750 # set stage to ""
751 # do normal full package installation:
752 # wipe and install system, boot image, etc.
753 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700754 # complete script normally
755 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800756
757 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
758 OPTIONS.input_tmp, "RECOVERY")
759 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800760 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800761 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800762 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800763 assert fs.fs_type.upper() == "EMMC", \
764 "two-step packages only supported on devices with EMMC /misc partitions"
765 bcb_dev = {"bcb_dev": fs.device}
766 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
767 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700768if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800769""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800770
771 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
772 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800773 script.WriteRawImage("/recovery", "recovery.img")
774 script.AppendExtra("""
775set_stage("%(bcb_dev)s", "3/3");
776reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700777else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800778""" % bcb_dev)
779
Tao Baod42e97e2016-11-30 12:11:57 -0800780 # Stage 3/3: Make changes.
781 script.Comment("Stage 3/3")
782
Tao Bao6c55a8a2015-04-08 15:30:27 -0700783 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800784 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700785
Doug Zongkere5ff5902012-01-17 10:55:37 -0800786 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700787
Tianjie Xuf67dd802019-05-20 17:50:36 -0700788 # All other partitions as well as the data wipe use 10% of the progress, and
789 # the update of the system partition takes the remaining progress.
790 system_progress = 0.9 - (len(block_diff_dict) - 1) * 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700791 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800792 system_progress -= 0.1
Tianjie Xuf67dd802019-05-20 17:50:36 -0700793 progress_dict = {partition: 0.1 for partition in block_diff_dict}
794 progress_dict["system"] = system_progress
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700795
Yifan Hong10c530d2018-12-27 17:34:18 -0800796 if target_info.get('use_dynamic_partitions') == "true":
797 # Use empty source_info_dict to indicate that all partitions / groups must
798 # be re-added.
799 dynamic_partitions_diff = common.DynamicPartitionsDifference(
800 info_dict=OPTIONS.info_dict,
Tianjie Xuf67dd802019-05-20 17:50:36 -0700801 block_diffs=block_diff_dict.values(),
Yifan Hong10c530d2018-12-27 17:34:18 -0800802 progress_dict=progress_dict)
803 dynamic_partitions_diff.WriteScript(script, output_zip,
804 write_verify_script=OPTIONS.verify)
805 else:
Tianjie Xuf67dd802019-05-20 17:50:36 -0700806 for block_diff in block_diff_dict.values():
Yifan Hong10c530d2018-12-27 17:34:18 -0800807 block_diff.WriteScript(script, output_zip,
808 progress=progress_dict.get(block_diff.partition),
809 write_verify_script=OPTIONS.verify)
Doug Zongker73ef8252009-07-23 15:12:53 -0700810
Yifan Hong9276cf02019-08-21 16:37:04 -0700811 CheckVintfIfTrebleEnabled(OPTIONS.input_tmp, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700812
Yifan Hong10c530d2018-12-27 17:34:18 -0800813 boot_img = common.GetBootableImage(
814 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Tao Bao481bab82017-12-21 11:23:09 -0800815 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700816 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700817
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700818 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700819
Tianjie Xuf67dd802019-05-20 17:50:36 -0700820 script.ShowProgress(0.1, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700821 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700822
Doug Zongker1c390a22009-05-14 19:06:36 -0700823 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700824 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700825
Doug Zongker14833602010-02-02 13:12:04 -0800826 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800827
Doug Zongker922206e2014-03-04 13:16:24 -0800828 if OPTIONS.wipe_user_data:
829 script.ShowProgress(0.1, 10)
830 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700831
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800832 if OPTIONS.two_step:
833 script.AppendExtra("""
834set_stage("%(bcb_dev)s", "");
835""" % bcb_dev)
836 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800837
838 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
839 script.Comment("Stage 1/3")
840 _WriteRecoveryImageToBoot(script, output_zip)
841
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800842 script.AppendExtra("""
843set_stage("%(bcb_dev)s", "2/3");
844reboot_now("%(bcb_dev)s", "");
845endif;
846endif;
847""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800848
Tao Bao5d182562016-02-23 11:38:39 -0800849 script.SetProgress(1)
850 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800851 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -0800852
853 # We haven't written the metadata entry, which will be done in
854 # FinalizeMetadata.
855 common.ZipClose(output_zip)
856
857 needed_property_files = (
858 NonAbOtaPropertyFiles(),
859 )
860 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Doug Zongker2ea21062010-04-28 16:05:21 -0700861
Doug Zongkerfc44a512014-08-26 13:10:25 -0700862
xunchang1cfe2512019-02-19 14:14:48 -0800863def WriteMetadata(metadata, output):
864 """Writes the metadata to the zip archive or a file.
865
866 Args:
867 metadata: The metadata dict for the package.
868 output: A ZipFile object or a string of the output file path.
869 """
870
Tao Bao59cf0c52019-06-25 10:04:24 -0700871 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.items())])
xunchang1cfe2512019-02-19 14:14:48 -0800872 if isinstance(output, zipfile.ZipFile):
873 common.ZipWriteStr(output, METADATA_NAME, value,
874 compress_type=zipfile.ZIP_STORED)
875 return
876
877 with open(output, 'w') as f:
878 f.write(value)
Doug Zongkereef39442009-04-02 12:14:19 -0700879
Doug Zongkerfc44a512014-08-26 13:10:25 -0700880
Tao Bao481bab82017-12-21 11:23:09 -0800881def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -0800882 # Only incremental OTAs are allowed to reach here.
883 assert OPTIONS.incremental_source is not None
884
Tao Bao481bab82017-12-21 11:23:09 -0800885 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
886 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Bao59cf0c52019-06-25 10:04:24 -0700887 is_downgrade = int(post_timestamp) < int(pre_timestamp)
Tao Baob31892e2017-02-07 11:21:17 -0800888
889 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800890 if not is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700891 raise RuntimeError(
892 "--downgrade or --override_timestamp specified but no downgrade "
893 "detected: pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800894 metadata["ota-downgrade"] = "yes"
Tao Baob31892e2017-02-07 11:21:17 -0800895 else:
896 if is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700897 raise RuntimeError(
898 "Downgrade detected based on timestamp check: pre: %s, post: %s. "
899 "Need to specify --override_timestamp OR --downgrade to allow "
900 "building the incremental." % (pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800901
902
Tao Baodf3a48b2018-01-10 16:30:43 -0800903def GetPackageMetadata(target_info, source_info=None):
904 """Generates and returns the metadata dict.
905
906 It generates a dict() that contains the info to be written into an OTA
907 package (META-INF/com/android/metadata). It also handles the detection of
Tao Baofaa8e0b2018-04-12 14:31:43 -0700908 downgrade / data wipe based on the global options.
Tao Baodf3a48b2018-01-10 16:30:43 -0800909
910 Args:
911 target_info: The BuildInfo instance that holds the target build info.
912 source_info: The BuildInfo instance that holds the source build info, or
913 None if generating full OTA.
914
915 Returns:
916 A dict to be written into package metadata entry.
917 """
Tao Bao1c320f82019-10-04 23:25:12 -0700918 assert isinstance(target_info, common.BuildInfo)
919 assert source_info is None or isinstance(source_info, common.BuildInfo)
Tao Baodf3a48b2018-01-10 16:30:43 -0800920
921 metadata = {
922 'post-build' : target_info.fingerprint,
923 'post-build-incremental' : target_info.GetBuildProp(
924 'ro.build.version.incremental'),
Tao Bao35dc2552018-02-01 13:18:00 -0800925 'post-sdk-level' : target_info.GetBuildProp(
926 'ro.build.version.sdk'),
927 'post-security-patch-level' : target_info.GetBuildProp(
928 'ro.build.version.security_patch'),
Tao Baodf3a48b2018-01-10 16:30:43 -0800929 }
930
931 if target_info.is_ab:
932 metadata['ota-type'] = 'AB'
933 metadata['ota-required-cache'] = '0'
934 else:
935 metadata['ota-type'] = 'BLOCK'
936
937 if OPTIONS.wipe_user_data:
938 metadata['ota-wipe'] = 'yes'
939
Tao Bao393eeb42019-03-06 16:00:38 -0800940 if OPTIONS.retrofit_dynamic_partitions:
941 metadata['ota-retrofit-dynamic-partitions'] = 'yes'
942
Tao Baodf3a48b2018-01-10 16:30:43 -0800943 is_incremental = source_info is not None
944 if is_incremental:
945 metadata['pre-build'] = source_info.fingerprint
946 metadata['pre-build-incremental'] = source_info.GetBuildProp(
947 'ro.build.version.incremental')
948 metadata['pre-device'] = source_info.device
949 else:
950 metadata['pre-device'] = target_info.device
951
Tao Baofaa8e0b2018-04-12 14:31:43 -0700952 # Use the actual post-timestamp, even for a downgrade case.
953 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
954
955 # Detect downgrades and set up downgrade flags accordingly.
Tao Baodf3a48b2018-01-10 16:30:43 -0800956 if is_incremental:
957 HandleDowngradeMetadata(metadata, target_info, source_info)
Tao Baodf3a48b2018-01-10 16:30:43 -0800958
959 return metadata
960
961
Tao Baod3fc38a2018-03-08 16:09:01 -0800962class PropertyFiles(object):
963 """A class that computes the property-files string for an OTA package.
964
965 A property-files string is a comma-separated string that contains the
966 offset/size info for an OTA package. The entries, which must be ZIP_STORED,
967 can be fetched directly with the package URL along with the offset/size info.
968 These strings can be used for streaming A/B OTAs, or allowing an updater to
969 download package metadata entry directly, without paying the cost of
970 downloading entire package.
Tao Baofe5b69a2018-03-02 09:47:43 -0800971
Tao Baocc8e2662018-03-01 19:30:00 -0800972 Computing the final property-files string requires two passes. Because doing
973 the whole package signing (with signapk.jar) will possibly reorder the ZIP
974 entries, which may in turn invalidate earlier computed ZIP entry offset/size
975 values.
976
977 This class provides functions to be called for each pass. The general flow is
978 as follows.
979
Tao Baod3fc38a2018-03-08 16:09:01 -0800980 property_files = PropertyFiles()
Tao Baocc8e2662018-03-01 19:30:00 -0800981 # The first pass, which writes placeholders before doing initial signing.
982 property_files.Compute()
983 SignOutput()
984
985 # The second pass, by replacing the placeholders with actual data.
986 property_files.Finalize()
987 SignOutput()
988
989 And the caller can additionally verify the final result.
990
991 property_files.Verify()
Tao Baofe5b69a2018-03-02 09:47:43 -0800992 """
993
Tao Baocc8e2662018-03-01 19:30:00 -0800994 def __init__(self):
Tao Baod3fc38a2018-03-08 16:09:01 -0800995 self.name = None
996 self.required = ()
997 self.optional = ()
Tao Baofe5b69a2018-03-02 09:47:43 -0800998
Tao Baocc8e2662018-03-01 19:30:00 -0800999 def Compute(self, input_zip):
1000 """Computes and returns a property-files string with placeholders.
Tao Baofe5b69a2018-03-02 09:47:43 -08001001
Tao Baocc8e2662018-03-01 19:30:00 -08001002 We reserve extra space for the offset and size of the metadata entry itself,
1003 although we don't know the final values until the package gets signed.
Tao Baofe5b69a2018-03-02 09:47:43 -08001004
Tao Baocc8e2662018-03-01 19:30:00 -08001005 Args:
1006 input_zip: The input ZIP file.
Tao Baofe5b69a2018-03-02 09:47:43 -08001007
Tao Baocc8e2662018-03-01 19:30:00 -08001008 Returns:
1009 A string with placeholders for the metadata offset/size info, e.g.
1010 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1011 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001012 return self.GetPropertyFilesString(input_zip, reserve_space=True)
Tao Baofe5b69a2018-03-02 09:47:43 -08001013
Tao Baod2ce2ed2018-03-16 12:59:42 -07001014 class InsufficientSpaceException(Exception):
1015 pass
1016
Tao Baocc8e2662018-03-01 19:30:00 -08001017 def Finalize(self, input_zip, reserved_length):
1018 """Finalizes a property-files string with actual METADATA offset/size info.
1019
1020 The input ZIP file has been signed, with the ZIP entries in the desired
1021 place (signapk.jar will possibly reorder the ZIP entries). Now we compute
1022 the ZIP entry offsets and construct the property-files string with actual
1023 data. Note that during this process, we must pad the property-files string
1024 to the reserved length, so that the METADATA entry size remains the same.
1025 Otherwise the entries' offsets and sizes may change again.
1026
1027 Args:
1028 input_zip: The input ZIP file.
1029 reserved_length: The reserved length of the property-files string during
1030 the call to Compute(). The final string must be no more than this
1031 size.
1032
1033 Returns:
1034 A property-files string including the metadata offset/size info, e.g.
1035 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379 ".
1036
1037 Raises:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001038 InsufficientSpaceException: If the reserved length is insufficient to hold
1039 the final string.
Tao Baocc8e2662018-03-01 19:30:00 -08001040 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001041 result = self.GetPropertyFilesString(input_zip, reserve_space=False)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001042 if len(result) > reserved_length:
1043 raise self.InsufficientSpaceException(
1044 'Insufficient reserved space: reserved={}, actual={}'.format(
1045 reserved_length, len(result)))
1046
Tao Baocc8e2662018-03-01 19:30:00 -08001047 result += ' ' * (reserved_length - len(result))
1048 return result
1049
1050 def Verify(self, input_zip, expected):
1051 """Verifies the input ZIP file contains the expected property-files string.
1052
1053 Args:
1054 input_zip: The input ZIP file.
1055 expected: The property-files string that's computed from Finalize().
1056
1057 Raises:
1058 AssertionError: On finding a mismatch.
1059 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001060 actual = self.GetPropertyFilesString(input_zip)
Tao Baocc8e2662018-03-01 19:30:00 -08001061 assert actual == expected, \
1062 "Mismatching streaming metadata: {} vs {}.".format(actual, expected)
1063
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001064 def GetPropertyFilesString(self, zip_file, reserve_space=False):
1065 """
1066 Constructs the property-files string per request.
1067
1068 Args:
1069 zip_file: The input ZIP file.
1070 reserved_length: The reserved length of the property-files string.
1071
1072 Returns:
1073 A property-files string including the metadata offset/size info, e.g.
1074 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1075 """
Tao Baocc8e2662018-03-01 19:30:00 -08001076
1077 def ComputeEntryOffsetSize(name):
1078 """Computes the zip entry offset and size."""
1079 info = zip_file.getinfo(name)
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001080 offset = info.header_offset
1081 offset += zipfile.sizeFileHeader
1082 offset += len(info.extra) + len(info.filename)
Tao Baocc8e2662018-03-01 19:30:00 -08001083 size = info.file_size
1084 return '%s:%d:%d' % (os.path.basename(name), offset, size)
1085
1086 tokens = []
Tao Bao85f16982018-03-08 16:28:33 -08001087 tokens.extend(self._GetPrecomputed(zip_file))
Tao Baocc8e2662018-03-01 19:30:00 -08001088 for entry in self.required:
1089 tokens.append(ComputeEntryOffsetSize(entry))
1090 for entry in self.optional:
1091 if entry in zip_file.namelist():
1092 tokens.append(ComputeEntryOffsetSize(entry))
1093
1094 # 'META-INF/com/android/metadata' is required. We don't know its actual
1095 # offset and length (as well as the values for other entries). So we reserve
Tao Baod2ce2ed2018-03-16 12:59:42 -07001096 # 15-byte as a placeholder ('offset:length'), which is sufficient to cover
1097 # the space for metadata entry. Because 'offset' allows a max of 10-digit
1098 # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the
1099 # reserved space serves the metadata entry only.
Tao Baocc8e2662018-03-01 19:30:00 -08001100 if reserve_space:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001101 tokens.append('metadata:' + ' ' * 15)
Tao Baocc8e2662018-03-01 19:30:00 -08001102 else:
1103 tokens.append(ComputeEntryOffsetSize(METADATA_NAME))
1104
1105 return ','.join(tokens)
Tao Baofe5b69a2018-03-02 09:47:43 -08001106
Tao Bao85f16982018-03-08 16:28:33 -08001107 def _GetPrecomputed(self, input_zip):
1108 """Computes the additional tokens to be included into the property-files.
1109
1110 This applies to tokens without actual ZIP entries, such as
1111 payload_metadadata.bin. We want to expose the offset/size to updaters, so
1112 that they can download the payload metadata directly with the info.
1113
1114 Args:
1115 input_zip: The input zip file.
1116
1117 Returns:
1118 A list of strings (tokens) to be added to the property-files string.
1119 """
1120 # pylint: disable=no-self-use
1121 # pylint: disable=unused-argument
1122 return []
1123
Tao Baofe5b69a2018-03-02 09:47:43 -08001124
Tao Baod3fc38a2018-03-08 16:09:01 -08001125class StreamingPropertyFiles(PropertyFiles):
1126 """A subclass for computing the property-files for streaming A/B OTAs."""
1127
1128 def __init__(self):
1129 super(StreamingPropertyFiles, self).__init__()
1130 self.name = 'ota-streaming-property-files'
1131 self.required = (
1132 # payload.bin and payload_properties.txt must exist.
1133 'payload.bin',
1134 'payload_properties.txt',
1135 )
1136 self.optional = (
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001137 # care_map is available only if dm-verity is enabled.
1138 'care_map.pb',
Tao Baod3fc38a2018-03-08 16:09:01 -08001139 'care_map.txt',
1140 # compatibility.zip is available only if target supports Treble.
1141 'compatibility.zip',
1142 )
1143
1144
Tao Bao85f16982018-03-08 16:28:33 -08001145class AbOtaPropertyFiles(StreamingPropertyFiles):
1146 """The property-files for A/B OTA that includes payload_metadata.bin info.
1147
1148 Since P, we expose one more token (aka property-file), in addition to the ones
1149 for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'.
1150 'payload_metadata.bin' is the header part of a payload ('payload.bin'), which
1151 doesn't exist as a separate ZIP entry, but can be used to verify if the
1152 payload can be applied on the given device.
1153
1154 For backward compatibility, we keep both of the 'ota-streaming-property-files'
1155 and the newly added 'ota-property-files' in P. The new token will only be
1156 available in 'ota-property-files'.
1157 """
1158
1159 def __init__(self):
1160 super(AbOtaPropertyFiles, self).__init__()
1161 self.name = 'ota-property-files'
1162
1163 def _GetPrecomputed(self, input_zip):
1164 offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip)
1165 return ['payload_metadata.bin:{}:{}'.format(offset, size)]
1166
1167 @staticmethod
1168 def _GetPayloadMetadataOffsetAndSize(input_zip):
1169 """Computes the offset and size of the payload metadata for a given package.
1170
1171 (From system/update_engine/update_metadata.proto)
1172 A delta update file contains all the deltas needed to update a system from
1173 one specific version to another specific version. The update format is
1174 represented by this struct pseudocode:
1175
1176 struct delta_update_file {
1177 char magic[4] = "CrAU";
1178 uint64 file_format_version;
1179 uint64 manifest_size; // Size of protobuf DeltaArchiveManifest
1180
1181 // Only present if format_version > 1:
1182 uint32 metadata_signature_size;
1183
1184 // The Bzip2 compressed DeltaArchiveManifest
1185 char manifest[metadata_signature_size];
1186
1187 // The signature of the metadata (from the beginning of the payload up to
1188 // this location, not including the signature itself). This is a
1189 // serialized Signatures message.
1190 char medatada_signature_message[metadata_signature_size];
1191
1192 // Data blobs for files, no specific format. The specific offset
1193 // and length of each data blob is recorded in the DeltaArchiveManifest.
1194 struct {
1195 char data[];
1196 } blobs[];
1197
1198 // These two are not signed:
1199 uint64 payload_signatures_message_size;
1200 char payload_signatures_message[];
1201 };
1202
1203 'payload-metadata.bin' contains all the bytes from the beginning of the
1204 payload, till the end of 'medatada_signature_message'.
1205 """
1206 payload_info = input_zip.getinfo('payload.bin')
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001207 payload_offset = payload_info.header_offset
1208 payload_offset += zipfile.sizeFileHeader
1209 payload_offset += len(payload_info.extra) + len(payload_info.filename)
Tao Bao85f16982018-03-08 16:28:33 -08001210 payload_size = payload_info.file_size
1211
Tao Bao59cf0c52019-06-25 10:04:24 -07001212 with input_zip.open('payload.bin') as payload_fp:
Tao Bao85f16982018-03-08 16:28:33 -08001213 header_bin = payload_fp.read(24)
1214
1215 # network byte order (big-endian)
1216 header = struct.unpack("!IQQL", header_bin)
1217
1218 # 'CrAU'
1219 magic = header[0]
1220 assert magic == 0x43724155, "Invalid magic: {:x}".format(magic)
1221
1222 manifest_size = header[2]
1223 metadata_signature_size = header[3]
1224 metadata_total = 24 + manifest_size + metadata_signature_size
1225 assert metadata_total < payload_size
1226
1227 return (payload_offset, metadata_total)
1228
1229
Tao Bao491d7e22018-02-21 13:17:22 -08001230class NonAbOtaPropertyFiles(PropertyFiles):
1231 """The property-files for non-A/B OTA.
1232
1233 For non-A/B OTA, the property-files string contains the info for METADATA
1234 entry, with which a system updater can be fetched the package metadata prior
1235 to downloading the entire package.
1236 """
1237
1238 def __init__(self):
1239 super(NonAbOtaPropertyFiles, self).__init__()
1240 self.name = 'ota-property-files'
1241
1242
Tao Baod3fc38a2018-03-08 16:09:01 -08001243def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
Tao Baofe5b69a2018-03-02 09:47:43 -08001244 """Finalizes the metadata and signs an A/B OTA package.
1245
1246 In order to stream an A/B OTA package, we need 'ota-streaming-property-files'
1247 that contains the offsets and sizes for the ZIP entries. An example
1248 property-files string is as follows.
1249
1250 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379"
1251
1252 OTA server can pass down this string, in addition to the package URL, to the
1253 system update client. System update client can then fetch individual ZIP
1254 entries (ZIP_STORED) directly at the given offset of the URL.
1255
1256 Args:
1257 metadata: The metadata dict for the package.
1258 input_file: The input ZIP filename that doesn't contain the package METADATA
1259 entry yet.
1260 output_file: The final output ZIP filename.
Tao Baod3fc38a2018-03-08 16:09:01 -08001261 needed_property_files: The list of PropertyFiles' to be generated.
Tao Baofe5b69a2018-03-02 09:47:43 -08001262 """
Tao Baofe5b69a2018-03-02 09:47:43 -08001263
Tao Baod2ce2ed2018-03-16 12:59:42 -07001264 def ComputeAllPropertyFiles(input_file, needed_property_files):
1265 # Write the current metadata entry with placeholders.
1266 with zipfile.ZipFile(input_file) as input_zip:
1267 for property_files in needed_property_files:
1268 metadata[property_files.name] = property_files.Compute(input_zip)
1269 namelist = input_zip.namelist()
Tao Baofe5b69a2018-03-02 09:47:43 -08001270
Tao Baod2ce2ed2018-03-16 12:59:42 -07001271 if METADATA_NAME in namelist:
1272 common.ZipDelete(input_file, METADATA_NAME)
1273 output_zip = zipfile.ZipFile(input_file, 'a')
1274 WriteMetadata(metadata, output_zip)
1275 common.ZipClose(output_zip)
1276
1277 if OPTIONS.no_signing:
1278 return input_file
1279
Tao Bao491d7e22018-02-21 13:17:22 -08001280 prelim_signing = common.MakeTempFile(suffix='.zip')
1281 SignOutput(input_file, prelim_signing)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001282 return prelim_signing
Tao Baofe5b69a2018-03-02 09:47:43 -08001283
Tao Baod2ce2ed2018-03-16 12:59:42 -07001284 def FinalizeAllPropertyFiles(prelim_signing, needed_property_files):
1285 with zipfile.ZipFile(prelim_signing) as prelim_signing_zip:
1286 for property_files in needed_property_files:
1287 metadata[property_files.name] = property_files.Finalize(
1288 prelim_signing_zip, len(metadata[property_files.name]))
1289
1290 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP
1291 # entries, as well as padding the entry headers. We do a preliminary signing
1292 # (with an incomplete metadata entry) to allow that to happen. Then compute
1293 # the ZIP entry offsets, write back the final metadata and do the final
1294 # signing.
1295 prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files)
1296 try:
1297 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
1298 except PropertyFiles.InsufficientSpaceException:
1299 # Even with the preliminary signing, the entry orders may change
1300 # dramatically, which leads to insufficiently reserved space during the
1301 # first call to ComputeAllPropertyFiles(). In that case, we redo all the
1302 # preliminary signing works, based on the already ordered ZIP entries, to
1303 # address the issue.
1304 prelim_signing = ComputeAllPropertyFiles(
1305 prelim_signing, needed_property_files)
1306 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
Tao Baofe5b69a2018-03-02 09:47:43 -08001307
1308 # Replace the METADATA entry.
1309 common.ZipDelete(prelim_signing, METADATA_NAME)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001310 output_zip = zipfile.ZipFile(prelim_signing, 'a')
Tao Baofe5b69a2018-03-02 09:47:43 -08001311 WriteMetadata(metadata, output_zip)
1312 common.ZipClose(output_zip)
1313
1314 # Re-sign the package after updating the metadata entry.
Tao Bao491d7e22018-02-21 13:17:22 -08001315 if OPTIONS.no_signing:
1316 output_file = prelim_signing
1317 else:
1318 SignOutput(prelim_signing, output_file)
Tao Baofe5b69a2018-03-02 09:47:43 -08001319
1320 # Reopen the final signed zip to double check the streaming metadata.
Tao Baod2ce2ed2018-03-16 12:59:42 -07001321 with zipfile.ZipFile(output_file) as output_zip:
Tao Baod3fc38a2018-03-08 16:09:01 -08001322 for property_files in needed_property_files:
1323 property_files.Verify(output_zip, metadata[property_files.name].strip())
Tao Baofe5b69a2018-03-02 09:47:43 -08001324
xunchang1cfe2512019-02-19 14:14:48 -08001325 # If requested, dump the metadata to a separate file.
1326 output_metadata_path = OPTIONS.output_metadata_path
1327 if output_metadata_path:
1328 WriteMetadata(metadata, output_metadata_path)
1329
Tao Baofe5b69a2018-03-02 09:47:43 -08001330
Tao Bao491d7e22018-02-21 13:17:22 -08001331def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
Tao Bao1c320f82019-10-04 23:25:12 -07001332 target_info = common.BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1333 source_info = common.BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -08001334
Tao Bao481bab82017-12-21 11:23:09 -08001335 target_api_version = target_info["recovery_api_version"]
1336 source_api_version = source_info["recovery_api_version"]
1337 if source_api_version == 0:
Tao Bao32fcdab2018-10-12 10:30:39 -07001338 logger.warning(
1339 "Generating edify script for a source that can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001340
Tao Bao481bab82017-12-21 11:23:09 -08001341 script = edify_generator.EdifyGenerator(
1342 source_api_version, target_info, fstab=source_info["fstab"])
1343
1344 if target_info.oem_props or source_info.oem_props:
1345 if not OPTIONS.oem_no_mount:
1346 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -07001347
Tao Baodf3a48b2018-01-10 16:30:43 -08001348 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -08001349
Tao Bao491d7e22018-02-21 13:17:22 -08001350 if not OPTIONS.no_signing:
1351 staging_file = common.MakeTempFile(suffix='.zip')
1352 else:
1353 staging_file = output_file
1354
1355 output_zip = zipfile.ZipFile(
1356 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
1357
Geremy Condra36bd3652014-02-06 19:45:10 -08001358 device_specific = common.DeviceSpecificParams(
1359 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001360 source_version=source_api_version,
Yifan Hong8a66a712019-04-04 15:37:57 -07001361 source_tmp=OPTIONS.source_tmp,
Geremy Condra36bd3652014-02-06 19:45:10 -08001362 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001363 target_version=target_api_version,
Yifan Hong8a66a712019-04-04 15:37:57 -07001364 target_tmp=OPTIONS.target_tmp,
Geremy Condra36bd3652014-02-06 19:45:10 -08001365 output_zip=output_zip,
1366 script=script,
1367 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -08001368 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001369
Geremy Condra36bd3652014-02-06 19:45:10 -08001370 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001371 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001372 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001373 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001374 updating_boot = (not OPTIONS.two_step and
1375 (source_boot.data != target_boot.data))
1376
Geremy Condra36bd3652014-02-06 19:45:10 -08001377 target_recovery = common.GetBootableImage(
1378 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -08001379
Tianjie Xuf67dd802019-05-20 17:50:36 -07001380 block_diff_dict = GetBlockDifferences(target_zip=target_zip,
1381 source_zip=source_zip,
1382 target_info=target_info,
1383 source_info=source_info,
1384 device_specific=device_specific)
Geremy Condra36bd3652014-02-06 19:45:10 -08001385
Yifan Hong9276cf02019-08-21 16:37:04 -07001386 CheckVintfIfTrebleEnabled(OPTIONS.target_tmp, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001387
Tao Bao481bab82017-12-21 11:23:09 -08001388 # Assertions (e.g. device properties check).
1389 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -08001390 device_specific.IncrementalOTA_Assertions()
1391
1392 # Two-step incremental package strategy (in chronological order,
1393 # which is *not* the order in which the generated script has
1394 # things):
1395 #
1396 # if stage is not "2/3" or "3/3":
1397 # do verification on current system
1398 # write recovery image to boot partition
1399 # set stage to "2/3"
1400 # reboot to boot partition and restart recovery
1401 # else if stage is "2/3":
1402 # write recovery image to recovery partition
1403 # set stage to "3/3"
1404 # reboot to recovery partition and restart recovery
1405 # else:
1406 # (stage must be "3/3")
1407 # perform update:
1408 # patch system files, etc.
1409 # force full install of new boot image
1410 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001411 # complete script normally
1412 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001413
1414 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001415 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001416 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001417 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001418 assert fs.fs_type.upper() == "EMMC", \
1419 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001420 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001421 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1422 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001423if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001424""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001425
1426 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1427 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001428 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001429 script.WriteRawImage("/recovery", "recovery.img")
1430 script.AppendExtra("""
1431set_stage("%(bcb_dev)s", "3/3");
1432reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001433else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001434""" % bcb_dev)
1435
Tao Baod42e97e2016-11-30 12:11:57 -08001436 # Stage 1/3: (a) Verify the current system.
1437 script.Comment("Stage 1/3")
1438
Tao Bao6c55a8a2015-04-08 15:30:27 -07001439 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001440 script.Print("Source: {}".format(source_info.fingerprint))
1441 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001442
Geremy Condra36bd3652014-02-06 19:45:10 -08001443 script.Print("Verifying current system...")
1444
1445 device_specific.IncrementalOTA_VerifyBegin()
1446
Tao Bao481bab82017-12-21 11:23:09 -08001447 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001448
Tao Baod8d14be2016-02-04 14:26:02 -08001449 # Check the required cache size (i.e. stashed blocks).
Tianjie Xuf67dd802019-05-20 17:50:36 -07001450 required_cache_sizes = [diff.required_cache for diff in
1451 block_diff_dict.values()]
Geremy Condra36bd3652014-02-06 19:45:10 -08001452 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -08001453 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001454 d = common.Difference(target_boot, source_boot)
1455 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001456 if d is None:
1457 include_full_boot = True
1458 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1459 else:
1460 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001461
Tao Bao32fcdab2018-10-12 10:30:39 -07001462 logger.info(
1463 "boot target: %d source: %d diff: %d", target_boot.size,
1464 source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -08001465
Tao Bao51216552018-08-26 11:53:15 -07001466 common.ZipWriteStr(output_zip, "boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001467
Tao Bao51216552018-08-26 11:53:15 -07001468 script.PatchPartitionCheck(
1469 "{}:{}:{}:{}".format(
1470 boot_type, boot_device, target_boot.size, target_boot.sha1),
1471 "{}:{}:{}:{}".format(
1472 boot_type, boot_device, source_boot.size, source_boot.sha1))
1473
Tianjie Xuf67dd802019-05-20 17:50:36 -07001474 required_cache_sizes.append(target_boot.size)
Tao Baod8d14be2016-02-04 14:26:02 -08001475
Tianjie Xuf67dd802019-05-20 17:50:36 -07001476 if required_cache_sizes:
1477 script.CacheFreeSpaceCheck(max(required_cache_sizes))
1478
1479 # Verify the existing partitions.
1480 for diff in block_diff_dict.values():
1481 diff.WriteVerifyScript(script, touched_blocks_only=True)
Geremy Condra36bd3652014-02-06 19:45:10 -08001482
1483 device_specific.IncrementalOTA_VerifyEnd()
1484
1485 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001486 # Stage 1/3: (b) Write recovery image to /boot.
1487 _WriteRecoveryImageToBoot(script, output_zip)
1488
Geremy Condra36bd3652014-02-06 19:45:10 -08001489 script.AppendExtra("""
1490set_stage("%(bcb_dev)s", "2/3");
1491reboot_now("%(bcb_dev)s", "");
1492else
1493""" % bcb_dev)
1494
Tao Baod42e97e2016-11-30 12:11:57 -08001495 # Stage 3/3: Make changes.
1496 script.Comment("Stage 3/3")
1497
Geremy Condra36bd3652014-02-06 19:45:10 -08001498 script.Comment("---- start making changes here ----")
1499
1500 device_specific.IncrementalOTA_InstallBegin()
1501
Tianjie Xuf67dd802019-05-20 17:50:36 -07001502 progress_dict = {partition: 0.1 for partition in block_diff_dict}
1503 progress_dict["system"] = 1 - len(block_diff_dict) * 0.1
Yifan Hong10c530d2018-12-27 17:34:18 -08001504
1505 if OPTIONS.source_info_dict.get("use_dynamic_partitions") == "true":
1506 if OPTIONS.target_info_dict.get("use_dynamic_partitions") != "true":
1507 raise RuntimeError(
1508 "can't generate incremental that disables dynamic partitions")
1509 dynamic_partitions_diff = common.DynamicPartitionsDifference(
1510 info_dict=OPTIONS.target_info_dict,
1511 source_info_dict=OPTIONS.source_info_dict,
Tianjie Xuf67dd802019-05-20 17:50:36 -07001512 block_diffs=block_diff_dict.values(),
Yifan Hong10c530d2018-12-27 17:34:18 -08001513 progress_dict=progress_dict)
1514 dynamic_partitions_diff.WriteScript(
1515 script, output_zip, write_verify_script=OPTIONS.verify)
1516 else:
Tianjie Xuf67dd802019-05-20 17:50:36 -07001517 for block_diff in block_diff_dict.values():
Yifan Hong10c530d2018-12-27 17:34:18 -08001518 block_diff.WriteScript(script, output_zip,
1519 progress=progress_dict.get(block_diff.partition),
1520 write_verify_script=OPTIONS.verify)
Geremy Condra36bd3652014-02-06 19:45:10 -08001521
1522 if OPTIONS.two_step:
1523 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1524 script.WriteRawImage("/boot", "boot.img")
Tao Bao32fcdab2018-10-12 10:30:39 -07001525 logger.info("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001526
1527 if not OPTIONS.two_step:
1528 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001529 if include_full_boot:
Tao Bao32fcdab2018-10-12 10:30:39 -07001530 logger.info("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001531 script.Print("Installing boot image...")
1532 script.WriteRawImage("/boot", "boot.img")
1533 else:
1534 # Produce the boot image by applying a patch to the current
1535 # contents of the boot partition, and write it back to the
1536 # partition.
Tao Bao32fcdab2018-10-12 10:30:39 -07001537 logger.info("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001538 script.Print("Patching boot image...")
1539 script.ShowProgress(0.1, 10)
Tao Bao51216552018-08-26 11:53:15 -07001540 script.PatchPartition(
1541 '{}:{}:{}:{}'.format(
1542 boot_type, boot_device, target_boot.size, target_boot.sha1),
1543 '{}:{}:{}:{}'.format(
1544 boot_type, boot_device, source_boot.size, source_boot.sha1),
1545 'boot.img.p')
Geremy Condra36bd3652014-02-06 19:45:10 -08001546 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001547 logger.info("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001548
1549 # Do device-specific installation (eg, write radio image).
1550 device_specific.IncrementalOTA_InstallEnd()
1551
1552 if OPTIONS.extra_script is not None:
1553 script.AppendExtra(OPTIONS.extra_script)
1554
Doug Zongker922206e2014-03-04 13:16:24 -08001555 if OPTIONS.wipe_user_data:
1556 script.Print("Erasing user data...")
1557 script.FormatPartition("/data")
1558
Geremy Condra36bd3652014-02-06 19:45:10 -08001559 if OPTIONS.two_step:
1560 script.AppendExtra("""
1561set_stage("%(bcb_dev)s", "");
1562endif;
1563endif;
1564""" % bcb_dev)
1565
1566 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001567 # For downgrade OTAs, we prefer to use the update-binary in the source
1568 # build that is actually newer than the one in the target build.
1569 if OPTIONS.downgrade:
1570 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1571 else:
1572 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001573 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001574
1575 # We haven't written the metadata entry yet, which will be handled in
1576 # FinalizeMetadata().
1577 common.ZipClose(output_zip)
1578
1579 # Sign the generated zip package unless no_signing is specified.
1580 needed_property_files = (
1581 NonAbOtaPropertyFiles(),
1582 )
1583 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Geremy Condra36bd3652014-02-06 19:45:10 -08001584
Doug Zongker32b527d2014-03-04 10:03:02 -08001585
Tao Bao15a146a2018-02-21 16:06:59 -08001586def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
Tao Baof7140c02018-01-30 17:09:24 -08001587 """Returns a target-files.zip file for generating secondary payload.
1588
1589 Although the original target-files.zip already contains secondary slot
1590 images (i.e. IMAGES/system_other.img), we need to rename the files to the
1591 ones without _other suffix. Note that we cannot instead modify the names in
1592 META/ab_partitions.txt, because there are no matching partitions on device.
1593
1594 For the partitions that don't have secondary images, the ones for primary
1595 slot will be used. This is to ensure that we always have valid boot, vbmeta,
1596 bootloader images in the inactive slot.
1597
1598 Args:
1599 input_file: The input target-files.zip file.
Tao Bao15a146a2018-02-21 16:06:59 -08001600 skip_postinstall: Whether to skip copying the postinstall config file.
Tao Baof7140c02018-01-30 17:09:24 -08001601
1602 Returns:
1603 The filename of the target-files.zip for generating secondary payload.
1604 """
Tianjie Xu1c808002019-09-11 00:29:26 -07001605
1606 def GetInfoForSecondaryImages(info_file):
1607 """Updates info file for secondary payload generation.
1608
1609 Scan each line in the info file, and remove the unwanted partitions from
1610 the dynamic partition list in the related properties. e.g.
1611 "super_google_dynamic_partitions_partition_list=system vendor product"
1612 will become "super_google_dynamic_partitions_partition_list=system".
1613
1614 Args:
1615 info_file: The input info file. e.g. misc_info.txt.
1616
1617 Returns:
1618 A string of the updated info content.
1619 """
1620
1621 output_list = []
1622 with open(info_file) as f:
1623 lines = f.read().splitlines()
1624
1625 # The suffix in partition_list variables that follows the name of the
1626 # partition group.
1627 LIST_SUFFIX = 'partition_list'
1628 for line in lines:
1629 if line.startswith('#') or '=' not in line:
1630 output_list.append(line)
1631 continue
1632 key, value = line.strip().split('=', 1)
1633 if key == 'dynamic_partition_list' or key.endswith(LIST_SUFFIX):
1634 partitions = value.split()
1635 partitions = [partition for partition in partitions if partition
Tao Bao3e759462019-09-17 22:43:11 -07001636 not in SECONDARY_PAYLOAD_SKIPPED_IMAGES]
Tianjie Xu1c808002019-09-11 00:29:26 -07001637 output_list.append('{}={}'.format(key, ' '.join(partitions)))
Yifan Hongfe073432019-11-01 12:28:31 -07001638 elif key == 'virtual_ab' or key == "virtual_ab_retrofit":
1639 # Remove virtual_ab flag from secondary payload so that OTA client
1640 # don't use snapshots for secondary update
1641 pass
Tianjie Xu1c808002019-09-11 00:29:26 -07001642 else:
1643 output_list.append(line)
1644 return '\n'.join(output_list)
1645
Tao Baof7140c02018-01-30 17:09:24 -08001646 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1647 target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
1648
Tao Baodba59ee2018-01-09 13:21:02 -08001649 with zipfile.ZipFile(input_file, 'r') as input_zip:
1650 infolist = input_zip.infolist()
Tao Bao12489802018-07-12 14:47:38 -07001651
Tao Bao0ff15de2019-03-20 11:26:06 -07001652 input_tmp = common.UnzipTemp(input_file, UNZIP_PATTERN)
Tao Baodba59ee2018-01-09 13:21:02 -08001653 for info in infolist:
Tao Baof7140c02018-01-30 17:09:24 -08001654 unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
1655 if info.filename == 'IMAGES/system_other.img':
1656 common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
1657
1658 # Primary images and friends need to be skipped explicitly.
1659 elif info.filename in ('IMAGES/system.img',
1660 'IMAGES/system.map'):
1661 pass
Tao Bao3e759462019-09-17 22:43:11 -07001662
1663 # Copy images that are not in SECONDARY_PAYLOAD_SKIPPED_IMAGES.
1664 elif info.filename.startswith(('IMAGES/', 'RADIO/')):
1665 image_name = os.path.basename(info.filename)
1666 if image_name not in ['{}.img'.format(partition) for partition in
1667 SECONDARY_PAYLOAD_SKIPPED_IMAGES]:
1668 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
Tao Baof7140c02018-01-30 17:09:24 -08001669
Tao Bao15a146a2018-02-21 16:06:59 -08001670 # Skip copying the postinstall config if requested.
1671 elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
1672 pass
1673
Tianjie Xu1c808002019-09-11 00:29:26 -07001674 elif info.filename.startswith('META/'):
1675 # Remove the unnecessary partitions for secondary images from the
1676 # ab_partitions file.
1677 if info.filename == AB_PARTITIONS:
1678 with open(unzipped_file) as f:
1679 partition_list = f.read().splitlines()
1680 partition_list = [partition for partition in partition_list if partition
Tao Bao3e759462019-09-17 22:43:11 -07001681 and partition not in SECONDARY_PAYLOAD_SKIPPED_IMAGES]
Tianjie Xu1c808002019-09-11 00:29:26 -07001682 common.ZipWriteStr(target_zip, info.filename, '\n'.join(partition_list))
1683 # Remove the unnecessary partitions from the dynamic partitions list.
1684 elif (info.filename == 'META/misc_info.txt' or
1685 info.filename == DYNAMIC_PARTITION_INFO):
1686 modified_info = GetInfoForSecondaryImages(unzipped_file)
1687 common.ZipWriteStr(target_zip, info.filename, modified_info)
1688 else:
1689 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
Tao Baof7140c02018-01-30 17:09:24 -08001690
Tao Baof7140c02018-01-30 17:09:24 -08001691 common.ZipClose(target_zip)
1692
1693 return target_file
1694
1695
Tao Bao15a146a2018-02-21 16:06:59 -08001696def GetTargetFilesZipWithoutPostinstallConfig(input_file):
1697 """Returns a target-files.zip that's not containing postinstall_config.txt.
1698
1699 This allows brillo_update_payload script to skip writing all the postinstall
1700 hooks in the generated payload. The input target-files.zip file will be
1701 duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
1702 contain the postinstall_config.txt entry, the input file will be returned.
1703
1704 Args:
1705 input_file: The input target-files.zip filename.
1706
1707 Returns:
1708 The filename of target-files.zip that doesn't contain postinstall config.
1709 """
1710 # We should only make a copy if postinstall_config entry exists.
1711 with zipfile.ZipFile(input_file, 'r') as input_zip:
1712 if POSTINSTALL_CONFIG not in input_zip.namelist():
1713 return input_file
1714
1715 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1716 shutil.copyfile(input_file, target_file)
1717 common.ZipDelete(target_file, POSTINSTALL_CONFIG)
1718 return target_file
1719
1720
Yifan Hong50e79542018-11-08 17:44:12 -08001721def GetTargetFilesZipForRetrofitDynamicPartitions(input_file,
Yifan Hongb433eba2019-03-06 12:42:53 -08001722 super_block_devices,
1723 dynamic_partition_list):
Yifan Hong50e79542018-11-08 17:44:12 -08001724 """Returns a target-files.zip for retrofitting dynamic partitions.
1725
1726 This allows brillo_update_payload to generate an OTA based on the exact
1727 bits on the block devices. Postinstall is disabled.
1728
1729 Args:
1730 input_file: The input target-files.zip filename.
1731 super_block_devices: The list of super block devices
Yifan Hongb433eba2019-03-06 12:42:53 -08001732 dynamic_partition_list: The list of dynamic partitions
Yifan Hong50e79542018-11-08 17:44:12 -08001733
1734 Returns:
1735 The filename of target-files.zip with *.img replaced with super_*.img for
1736 each block device in super_block_devices.
1737 """
1738 assert super_block_devices, "No super_block_devices are specified."
1739
1740 replace = {'OTA/super_{}.img'.format(dev): 'IMAGES/{}.img'.format(dev)
Tao Bao03fecb62018-11-28 10:59:23 -08001741 for dev in super_block_devices}
Yifan Hong50e79542018-11-08 17:44:12 -08001742
1743 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1744 shutil.copyfile(input_file, target_file)
1745
Tao Baoa3705452019-06-24 15:33:41 -07001746 with zipfile.ZipFile(input_file) as input_zip:
Yifan Hong50e79542018-11-08 17:44:12 -08001747 namelist = input_zip.namelist()
1748
Yifan Hongb433eba2019-03-06 12:42:53 -08001749 input_tmp = common.UnzipTemp(input_file, RETROFIT_DAP_UNZIP_PATTERN)
1750
1751 # Remove partitions from META/ab_partitions.txt that is in
1752 # dynamic_partition_list but not in super_block_devices so that
1753 # brillo_update_payload won't generate update for those logical partitions.
1754 ab_partitions_file = os.path.join(input_tmp, *AB_PARTITIONS.split('/'))
1755 with open(ab_partitions_file) as f:
1756 ab_partitions_lines = f.readlines()
1757 ab_partitions = [line.strip() for line in ab_partitions_lines]
1758 # Assert that all super_block_devices are in ab_partitions
1759 super_device_not_updated = [partition for partition in super_block_devices
1760 if partition not in ab_partitions]
1761 assert not super_device_not_updated, \
1762 "{} is in super_block_devices but not in {}".format(
1763 super_device_not_updated, AB_PARTITIONS)
1764 # ab_partitions -= (dynamic_partition_list - super_block_devices)
1765 new_ab_partitions = common.MakeTempFile(prefix="ab_partitions", suffix=".txt")
1766 with open(new_ab_partitions, 'w') as f:
1767 for partition in ab_partitions:
1768 if (partition in dynamic_partition_list and
1769 partition not in super_block_devices):
Tao Bao59cf0c52019-06-25 10:04:24 -07001770 logger.info("Dropping %s from ab_partitions.txt", partition)
1771 continue
Yifan Hongb433eba2019-03-06 12:42:53 -08001772 f.write(partition + "\n")
1773 to_delete = [AB_PARTITIONS]
1774
Yifan Hong50e79542018-11-08 17:44:12 -08001775 # Always skip postinstall for a retrofit update.
Yifan Hongb433eba2019-03-06 12:42:53 -08001776 to_delete += [POSTINSTALL_CONFIG]
Yifan Hong50e79542018-11-08 17:44:12 -08001777
1778 # Delete dynamic_partitions_info.txt so that brillo_update_payload thinks this
1779 # is a regular update on devices without dynamic partitions support.
1780 to_delete += [DYNAMIC_PARTITION_INFO]
1781
Tao Bao03fecb62018-11-28 10:59:23 -08001782 # Remove the existing partition images as well as the map files.
Tao Bao59cf0c52019-06-25 10:04:24 -07001783 to_delete += list(replace.values())
Tao Bao03fecb62018-11-28 10:59:23 -08001784 to_delete += ['IMAGES/{}.map'.format(dev) for dev in super_block_devices]
Yifan Hong50e79542018-11-08 17:44:12 -08001785
1786 common.ZipDelete(target_file, to_delete)
1787
Yifan Hong50e79542018-11-08 17:44:12 -08001788 target_zip = zipfile.ZipFile(target_file, 'a', allowZip64=True)
1789
1790 # Write super_{foo}.img as {foo}.img.
1791 for src, dst in replace.items():
1792 assert src in namelist, \
Tao Bao59cf0c52019-06-25 10:04:24 -07001793 'Missing {} in {}; {} cannot be written'.format(src, input_file, dst)
Yifan Hong50e79542018-11-08 17:44:12 -08001794 unzipped_file = os.path.join(input_tmp, *src.split('/'))
1795 common.ZipWrite(target_zip, unzipped_file, arcname=dst)
1796
Yifan Hongb433eba2019-03-06 12:42:53 -08001797 # Write new ab_partitions.txt file
1798 common.ZipWrite(target_zip, new_ab_partitions, arcname=AB_PARTITIONS)
1799
Yifan Hong50e79542018-11-08 17:44:12 -08001800 common.ZipClose(target_zip)
1801
1802 return target_file
1803
1804
Tao Baof0c4aa22018-04-30 20:29:30 -07001805def GenerateAbOtaPackage(target_file, output_file, source_file=None):
Tao Baofe5b69a2018-03-02 09:47:43 -08001806 """Generates an Android OTA package that has A/B update payload."""
Tao Baodea0f8b2016-06-20 17:55:06 -07001807 # Stage the output zip package for package signing.
Tao Bao491d7e22018-02-21 13:17:22 -08001808 if not OPTIONS.no_signing:
1809 staging_file = common.MakeTempFile(suffix='.zip')
1810 else:
1811 staging_file = output_file
Tao Baoa652c002018-03-01 19:31:38 -08001812 output_zip = zipfile.ZipFile(staging_file, "w",
Tao Baoc098e9e2016-01-07 13:03:56 -08001813 compression=zipfile.ZIP_DEFLATED)
1814
Tao Bao481bab82017-12-21 11:23:09 -08001815 if source_file is not None:
Tao Bao1c320f82019-10-04 23:25:12 -07001816 target_info = common.BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1817 source_info = common.BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Tao Bao481bab82017-12-21 11:23:09 -08001818 else:
Tao Bao1c320f82019-10-04 23:25:12 -07001819 target_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Tao Bao481bab82017-12-21 11:23:09 -08001820 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001821
Tao Bao481bab82017-12-21 11:23:09 -08001822 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001823 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001824
Yifan Hong50e79542018-11-08 17:44:12 -08001825 if OPTIONS.retrofit_dynamic_partitions:
1826 target_file = GetTargetFilesZipForRetrofitDynamicPartitions(
Yifan Hongb433eba2019-03-06 12:42:53 -08001827 target_file, target_info.get("super_block_devices").strip().split(),
1828 target_info.get("dynamic_partition_list").strip().split())
Yifan Hong50e79542018-11-08 17:44:12 -08001829 elif OPTIONS.skip_postinstall:
Tao Bao15a146a2018-02-21 16:06:59 -08001830 target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
1831
Tao Bao40b18822018-01-30 18:19:04 -08001832 # Generate payload.
1833 payload = Payload()
1834
1835 # Enforce a max timestamp this payload can be applied on top of.
Tao Baoff1b86e2017-10-03 14:17:57 -07001836 if OPTIONS.downgrade:
Tao Bao2a12ed72018-01-22 11:35:00 -08001837 max_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baoff1b86e2017-10-03 14:17:57 -07001838 else:
1839 max_timestamp = metadata["post-timestamp"]
Tao Bao40b18822018-01-30 18:19:04 -08001840 additional_args = ["--max_timestamp", max_timestamp]
Tao Baoc098e9e2016-01-07 13:03:56 -08001841
Tao Bao40b18822018-01-30 18:19:04 -08001842 payload.Generate(target_file, source_file, additional_args)
Tao Baoc098e9e2016-01-07 13:03:56 -08001843
Tao Bao40b18822018-01-30 18:19:04 -08001844 # Sign the payload.
Tao Baof7140c02018-01-30 17:09:24 -08001845 payload_signer = PayloadSigner()
1846 payload.Sign(payload_signer)
Tao Baoc098e9e2016-01-07 13:03:56 -08001847
Tao Bao40b18822018-01-30 18:19:04 -08001848 # Write the payload into output zip.
1849 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001850
Tao Baof7140c02018-01-30 17:09:24 -08001851 # Generate and include the secondary payload that installs secondary images
1852 # (e.g. system_other.img).
1853 if OPTIONS.include_secondary:
1854 # We always include a full payload for the secondary slot, even when
1855 # building an incremental OTA. See the comments for "--include_secondary".
Tao Bao15a146a2018-02-21 16:06:59 -08001856 secondary_target_file = GetTargetFilesZipForSecondaryImages(
1857 target_file, OPTIONS.skip_postinstall)
Tao Bao667ff572018-02-10 00:02:40 -08001858 secondary_payload = Payload(secondary=True)
Tao Baodb1fe412018-02-09 23:15:05 -08001859 secondary_payload.Generate(secondary_target_file,
1860 additional_args=additional_args)
Tao Baof7140c02018-01-30 17:09:24 -08001861 secondary_payload.Sign(payload_signer)
Tao Bao667ff572018-02-10 00:02:40 -08001862 secondary_payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08001863
Tianjie Xucfa86222016-03-07 16:31:19 -08001864 # If dm-verity is supported for the device, copy contents of care_map
1865 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001866 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08001867 if (target_info.get("verity") == "true" or
1868 target_info.get("avb_enable") == "true"):
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001869 care_map_list = [x for x in ["care_map.pb", "care_map.txt"] if
1870 "META/" + x in target_zip.namelist()]
1871
1872 # Adds care_map if either the protobuf format or the plain text one exists.
1873 if care_map_list:
1874 care_map_name = care_map_list[0]
1875 care_map_data = target_zip.read("META/" + care_map_name)
1876 # In order to support streaming, care_map needs to be packed as
Tao Bao40b18822018-01-30 18:19:04 -08001877 # ZIP_STORED.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001878 common.ZipWriteStr(output_zip, care_map_name, care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08001879 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001880 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001881 logger.warning("Cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07001882
Tao Bao21803d32017-04-19 10:16:09 -07001883 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001884
Yifan Hong9276cf02019-08-21 16:37:04 -07001885 CheckVintfIfTrebleEnabled(target_file, target_info)
1886
Tao Baofe5b69a2018-03-02 09:47:43 -08001887 # We haven't written the metadata entry yet, which will be handled in
1888 # FinalizeMetadata().
Tao Baoc96316c2017-01-24 22:10:49 -08001889 common.ZipClose(output_zip)
1890
Tao Bao85f16982018-03-08 16:28:33 -08001891 # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers
1892 # all the info of the latter. However, system updaters and OTA servers need to
1893 # take time to switch to the new flag. We keep both of the flags for
1894 # P-timeframe, and will remove StreamingPropertyFiles in later release.
Tao Baod3fc38a2018-03-08 16:09:01 -08001895 needed_property_files = (
Tao Bao85f16982018-03-08 16:28:33 -08001896 AbOtaPropertyFiles(),
Tao Baod3fc38a2018-03-08 16:09:01 -08001897 StreamingPropertyFiles(),
1898 )
1899 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Tao Baoc96316c2017-01-24 22:10:49 -08001900
Tao Baoc098e9e2016-01-07 13:03:56 -08001901
Tao Baof0c4aa22018-04-30 20:29:30 -07001902def GenerateNonAbOtaPackage(target_file, output_file, source_file=None):
1903 """Generates a non-A/B OTA package."""
1904 # Sanity check the loaded info dicts first.
1905 if OPTIONS.info_dict.get("no_recovery") == "true":
1906 raise common.ExternalError(
1907 "--- target build has specified no recovery ---")
1908
1909 # Non-A/B OTAs rely on /cache partition to store temporary files.
1910 cache_size = OPTIONS.info_dict.get("cache_size")
1911 if cache_size is None:
1912 logger.warning("--- can't determine the cache partition size ---")
1913 OPTIONS.cache_size = cache_size
1914
1915 if OPTIONS.extra_script is not None:
1916 with open(OPTIONS.extra_script) as fp:
1917 OPTIONS.extra_script = fp.read()
1918
1919 if OPTIONS.extracted_input is not None:
1920 OPTIONS.input_tmp = OPTIONS.extracted_input
1921 else:
1922 logger.info("unzipping target target-files...")
1923 OPTIONS.input_tmp = common.UnzipTemp(target_file, UNZIP_PATTERN)
1924 OPTIONS.target_tmp = OPTIONS.input_tmp
1925
1926 # If the caller explicitly specified the device-specific extensions path via
1927 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
1928 # is present in the target target_files. Otherwise, take the path of the file
1929 # from 'tool_extensions' in the info dict and look for that in the local
1930 # filesystem, relative to the current directory.
1931 if OPTIONS.device_specific is None:
1932 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1933 if os.path.exists(from_input):
1934 logger.info("(using device-specific extensions from target_files)")
1935 OPTIONS.device_specific = from_input
1936 else:
1937 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
1938
1939 if OPTIONS.device_specific is not None:
1940 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
1941
1942 # Generate a full OTA.
1943 if source_file is None:
1944 with zipfile.ZipFile(target_file) as input_zip:
1945 WriteFullOTAPackage(
1946 input_zip,
1947 output_file)
1948
1949 # Generate an incremental OTA.
1950 else:
1951 logger.info("unzipping source target-files...")
1952 OPTIONS.source_tmp = common.UnzipTemp(
1953 OPTIONS.incremental_source, UNZIP_PATTERN)
1954 with zipfile.ZipFile(target_file) as input_zip, \
1955 zipfile.ZipFile(source_file) as source_zip:
1956 WriteBlockIncrementalOTAPackage(
1957 input_zip,
1958 source_zip,
1959 output_file)
1960
1961
Doug Zongkereef39442009-04-02 12:14:19 -07001962def main(argv):
1963
1964 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07001965 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07001966 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001967 elif o in ("-i", "--incremental_from"):
1968 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001969 elif o == "--full_radio":
1970 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001971 elif o == "--full_bootloader":
1972 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08001973 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001974 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08001975 elif o == "--downgrade":
1976 OPTIONS.downgrade = True
1977 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08001978 elif o == "--override_timestamp":
Tao Baofaa8e0b2018-04-12 14:31:43 -07001979 OPTIONS.downgrade = True
Michael Runge6e836112014-04-15 17:40:21 -07001980 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001981 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08001982 elif o == "--oem_no_mount":
1983 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001984 elif o in ("-e", "--extra_script"):
1985 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001986 elif o in ("-t", "--worker_threads"):
1987 if a.isdigit():
1988 OPTIONS.worker_threads = int(a)
1989 else:
1990 raise ValueError("Cannot parse value %r for option %r - only "
1991 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001992 elif o in ("-2", "--two_step"):
1993 OPTIONS.two_step = True
Tao Baof7140c02018-01-30 17:09:24 -08001994 elif o == "--include_secondary":
1995 OPTIONS.include_secondary = True
Doug Zongker26e66192014-02-20 13:22:07 -08001996 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001997 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001998 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001999 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08002000 elif o == "--block":
2001 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002002 elif o in ("-b", "--binary"):
2003 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07002004 elif o == "--stash_threshold":
2005 try:
2006 OPTIONS.stash_threshold = float(a)
2007 except ValueError:
2008 raise ValueError("Cannot parse value %r for option %r - expecting "
2009 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08002010 elif o == "--log_diff":
2011 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002012 elif o == "--payload_signer":
2013 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002014 elif o == "--payload_signer_args":
2015 OPTIONS.payload_signer_args = shlex.split(a)
Tianjie Xu21e6deb2019-10-07 18:01:00 -07002016 elif o == "--payload_signer_maximum_signature_size":
2017 OPTIONS.payload_signer_maximum_signature_size = a
xunchang376cc7c2019-04-08 23:04:58 -07002018 elif o == "--payload_signer_key_size":
Tianjie Xu21e6deb2019-10-07 18:01:00 -07002019 # TODO(Xunchang) remove this option after cleaning up the callers.
2020 logger.warning("The option '--payload_signer_key_size' is deprecated."
2021 " Use '--payload_signer_maximum_signature_size' instead.")
2022 OPTIONS.payload_signer_maximum_signature_size = a
Dan Willemsencea5cd22017-03-21 14:44:27 -07002023 elif o == "--extracted_input_target_files":
2024 OPTIONS.extracted_input = a
Tao Bao15a146a2018-02-21 16:06:59 -08002025 elif o == "--skip_postinstall":
2026 OPTIONS.skip_postinstall = True
Yifan Hong50e79542018-11-08 17:44:12 -08002027 elif o == "--retrofit_dynamic_partitions":
2028 OPTIONS.retrofit_dynamic_partitions = True
xunchangabfa2652019-02-19 16:27:10 -08002029 elif o == "--skip_compatibility_check":
2030 OPTIONS.skip_compatibility_check = True
xunchang1cfe2512019-02-19 14:14:48 -08002031 elif o == "--output_metadata_path":
2032 OPTIONS.output_metadata_path = a
Tianjie Xu1b079832019-08-28 12:19:23 -07002033 elif o == "--disable_fec_computation":
2034 OPTIONS.disable_fec_computation = True
Doug Zongkereef39442009-04-02 12:14:19 -07002035 else:
2036 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002037 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002038
2039 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08002040 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002041 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07002042 "package_key=",
2043 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002044 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002045 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002046 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08002047 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08002048 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07002049 "extra_script=",
2050 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002051 "two_step",
Tao Baof7140c02018-01-30 17:09:24 -08002052 "include_secondary",
Dan Albert8b72aef2015-03-23 19:13:21 -07002053 "no_signing",
2054 "block",
2055 "binary=",
2056 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08002057 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002058 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07002059 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002060 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002061 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002062 "payload_signer_args=",
Tianjie Xu21e6deb2019-10-07 18:01:00 -07002063 "payload_signer_maximum_signature_size=",
xunchang376cc7c2019-04-08 23:04:58 -07002064 "payload_signer_key_size=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07002065 "extracted_input_target_files=",
Tao Bao15a146a2018-02-21 16:06:59 -08002066 "skip_postinstall",
Yifan Hong50e79542018-11-08 17:44:12 -08002067 "retrofit_dynamic_partitions",
xunchangabfa2652019-02-19 16:27:10 -08002068 "skip_compatibility_check",
xunchang1cfe2512019-02-19 14:14:48 -08002069 "output_metadata_path=",
Tianjie Xu1b079832019-08-28 12:19:23 -07002070 "disable_fec_computation",
Dan Albert8b72aef2015-03-23 19:13:21 -07002071 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002072
2073 if len(args) != 2:
2074 common.Usage(__doc__)
2075 sys.exit(1)
2076
Tao Bao32fcdab2018-10-12 10:30:39 -07002077 common.InitLogging()
2078
Tao Bao5d182562016-02-23 11:38:39 -08002079 if OPTIONS.downgrade:
Tao Bao5d182562016-02-23 11:38:39 -08002080 # We should only allow downgrading incrementals (as opposed to full).
2081 # Otherwise the device may go back from arbitrary build with this full
2082 # OTA package.
2083 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002084 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08002085
Tao Bao2db13852018-01-08 22:28:57 -08002086 # Load the build info dicts from the zip directly or the extracted input
2087 # directory. We don't need to unzip the entire target-files zips, because they
2088 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
2089 # When loading the info dicts, we don't need to provide the second parameter
2090 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
2091 # some properties with their actual paths, such as 'selinux_fc',
2092 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07002093 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08002094 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07002095 else:
Tao Bao2db13852018-01-08 22:28:57 -08002096 with zipfile.ZipFile(args[0], 'r') as input_zip:
2097 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08002098
Tao Bao32fcdab2018-10-12 10:30:39 -07002099 logger.info("--- target info ---")
2100 common.DumpInfoDict(OPTIONS.info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002101
2102 # Load the source build dict if applicable.
2103 if OPTIONS.incremental_source is not None:
2104 OPTIONS.target_info_dict = OPTIONS.info_dict
2105 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
2106 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2107
Tao Bao32fcdab2018-10-12 10:30:39 -07002108 logger.info("--- source info ---")
2109 common.DumpInfoDict(OPTIONS.source_info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002110
2111 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08002112 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
2113
Yifan Hong50e79542018-11-08 17:44:12 -08002114 # Assume retrofitting dynamic partitions when base build does not set
Yifan Hong50611032018-11-20 14:27:38 -08002115 # use_dynamic_partitions but target build does.
Yifan Hong50e79542018-11-08 17:44:12 -08002116 if (OPTIONS.source_info_dict and
Yifan Hong50611032018-11-20 14:27:38 -08002117 OPTIONS.source_info_dict.get("use_dynamic_partitions") != "true" and
2118 OPTIONS.target_info_dict.get("use_dynamic_partitions") == "true"):
Yifan Hong50e79542018-11-08 17:44:12 -08002119 if OPTIONS.target_info_dict.get("dynamic_partition_retrofit") != "true":
2120 raise common.ExternalError(
2121 "Expect to generate incremental OTA for retrofitting dynamic "
2122 "partitions, but dynamic_partition_retrofit is not set in target "
2123 "build.")
2124 logger.info("Implicitly generating retrofit incremental OTA.")
2125 OPTIONS.retrofit_dynamic_partitions = True
2126
2127 # Skip postinstall for retrofitting dynamic partitions.
2128 if OPTIONS.retrofit_dynamic_partitions:
2129 OPTIONS.skip_postinstall = True
2130
Tao Baoc098e9e2016-01-07 13:03:56 -08002131 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2132
Christian Oderf63e2cd2017-05-01 22:30:15 +02002133 # Use the default key to sign the package if not specified with package_key.
2134 # package_keys are needed on ab_updates, so always define them if an
2135 # ab_update is getting created.
2136 if not OPTIONS.no_signing or ab_update:
2137 if OPTIONS.package_key is None:
2138 OPTIONS.package_key = OPTIONS.info_dict.get(
2139 "default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -07002140 "build/make/target/product/security/testkey")
Christian Oderf63e2cd2017-05-01 22:30:15 +02002141 # Get signing keys
2142 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
2143
Tao Baoc098e9e2016-01-07 13:03:56 -08002144 if ab_update:
Tao Baof0c4aa22018-04-30 20:29:30 -07002145 GenerateAbOtaPackage(
Tao Baoc098e9e2016-01-07 13:03:56 -08002146 target_file=args[0],
2147 output_file=args[1],
2148 source_file=OPTIONS.incremental_source)
2149
Dan Willemsencea5cd22017-03-21 14:44:27 -07002150 else:
Tao Baof0c4aa22018-04-30 20:29:30 -07002151 GenerateNonAbOtaPackage(
2152 target_file=args[0],
2153 output_file=args[1],
2154 source_file=OPTIONS.incremental_source)
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002155
Tao Baof0c4aa22018-04-30 20:29:30 -07002156 # Post OTA generation works.
2157 if OPTIONS.incremental_source is not None and OPTIONS.log_diff:
2158 logger.info("Generating diff logs...")
2159 logger.info("Unzipping target-files for diffing...")
2160 target_dir = common.UnzipTemp(args[0], TARGET_DIFFING_UNZIP_PATTERN)
2161 source_dir = common.UnzipTemp(
2162 OPTIONS.incremental_source, TARGET_DIFFING_UNZIP_PATTERN)
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002163
Tao Baof0c4aa22018-04-30 20:29:30 -07002164 with open(OPTIONS.log_diff, 'w') as out_file:
2165 import target_files_diff
2166 target_files_diff.recursiveDiff(
2167 '', source_dir, target_dir, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07002168
Tao Bao32fcdab2018-10-12 10:30:39 -07002169 logger.info("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002170
2171
2172if __name__ == '__main__':
2173 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002174 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002175 main(sys.argv[1:])
Tao Bao32fcdab2018-10-12 10:30:39 -07002176 except common.ExternalError:
2177 logger.exception("\n ERROR:\n")
Doug Zongkereef39442009-04-02 12:14:19 -07002178 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002179 finally:
2180 common.Cleanup()