blob: ad001d13c76a5e75c57e9c9f98ee56d66c97206a [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
Tianjie Xu9afb2212020-05-10 21:48:15 +0000196import copy
197import itertools
Tao Bao32fcdab2018-10-12 10:30:39 -0700198import logging
Doug Zongkerfc44a512014-08-26 13:10:25 -0700199import multiprocessing
Tao Bao2dd1c482017-02-03 16:49:39 -0800200import os.path
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700201import shlex
Tao Bao15a146a2018-02-21 16:06:59 -0800202import shutil
Tao Bao85f16982018-03-08 16:28:33 -0800203import struct
Tao Bao481bab82017-12-21 11:23:09 -0800204import sys
Doug Zongkereef39442009-04-02 12:14:19 -0700205import zipfile
206
Yifan Hong9276cf02019-08-21 16:37:04 -0700207import check_target_files_vintf
Doug Zongkereef39442009-04-02 12:14:19 -0700208import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700209import edify_generator
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700210import verity_utils
Doug Zongkereef39442009-04-02 12:14:19 -0700211
Tao Bao481bab82017-12-21 11:23:09 -0800212if sys.hexversion < 0x02070000:
213 print("Python 2.7 or newer is required.", file=sys.stderr)
214 sys.exit(1)
215
Tao Bao32fcdab2018-10-12 10:30:39 -0700216logger = logging.getLogger(__name__)
Tao Bao481bab82017-12-21 11:23:09 -0800217
Doug Zongkereef39442009-04-02 12:14:19 -0700218OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700219OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700220OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700221OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700222OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700223OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800224OPTIONS.downgrade = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700225OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700226OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
227if OPTIONS.worker_threads == 0:
228 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800229OPTIONS.two_step = False
Tao Baof7140c02018-01-30 17:09:24 -0800230OPTIONS.include_secondary = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900231OPTIONS.no_signing = False
Tao Bao457cbf62017-03-06 09:56:01 -0800232OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -0800233OPTIONS.updater_binary = None
Tianjie Xu9afb2212020-05-10 21:48:15 +0000234OPTIONS.oem_dicts = None
Michael Runge6e836112014-04-15 17:40:21 -0700235OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800236OPTIONS.oem_no_mount = False
Tao Bao43078aa2015-04-21 14:32:35 -0700237OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700238OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700239# Stash size cannot exceed cache_size * threshold.
240OPTIONS.cache_size = None
241OPTIONS.stash_threshold = 0.8
Tao Baod62c6032015-11-30 09:40:20 -0800242OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700243OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700244OPTIONS.payload_signer_args = []
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700245OPTIONS.payload_signer_maximum_signature_size = None
Tao Bao5f8ff932017-03-21 22:35:00 -0700246OPTIONS.extracted_input = None
Christian Oderf63e2cd2017-05-01 22:30:15 +0200247OPTIONS.key_passwords = []
Tao Bao15a146a2018-02-21 16:06:59 -0800248OPTIONS.skip_postinstall = False
Yifan Hong50e79542018-11-08 17:44:12 -0800249OPTIONS.retrofit_dynamic_partitions = False
xunchangabfa2652019-02-19 16:27:10 -0800250OPTIONS.skip_compatibility_check = False
xunchang1cfe2512019-02-19 14:14:48 -0800251OPTIONS.output_metadata_path = None
Tianjie Xu1b079832019-08-28 12:19:23 -0700252OPTIONS.disable_fec_computation = False
Tianjie Xu9afb2212020-05-10 21:48:15 +0000253OPTIONS.boot_variable_values = None
Tao Bao15a146a2018-02-21 16:06:59 -0800254
Tao Bao8dcf7382015-05-21 14:09:49 -0700255
Tao Bao2dd1c482017-02-03 16:49:39 -0800256METADATA_NAME = 'META-INF/com/android/metadata'
Tao Bao15a146a2018-02-21 16:06:59 -0800257POSTINSTALL_CONFIG = 'META/postinstall_config.txt'
Yifan Hong50e79542018-11-08 17:44:12 -0800258DYNAMIC_PARTITION_INFO = 'META/dynamic_partitions_info.txt'
Yifan Hongb433eba2019-03-06 12:42:53 -0800259AB_PARTITIONS = 'META/ab_partitions.txt'
Tao Bao04808502019-07-25 23:11:41 -0700260UNZIP_PATTERN = ['IMAGES/*', 'META/*', 'OTA/*', 'RADIO/*']
Tao Baof0c4aa22018-04-30 20:29:30 -0700261# Files to be unzipped for target diffing purpose.
262TARGET_DIFFING_UNZIP_PATTERN = ['BOOT', 'RECOVERY', 'SYSTEM/*', 'VENDOR/*',
263 'PRODUCT/*', 'SYSTEM_EXT/*', 'ODM/*']
Yifan Hongb433eba2019-03-06 12:42:53 -0800264RETROFIT_DAP_UNZIP_PATTERN = ['OTA/super_*.img', AB_PARTITIONS]
Tao Bao3e759462019-09-17 22:43:11 -0700265
266# Images to be excluded from secondary payload. We essentially only keep
267# 'system_other' and bootloader partitions.
268SECONDARY_PAYLOAD_SKIPPED_IMAGES = [
269 'boot', 'dtbo', 'modem', 'odm', 'product', 'radio', 'recovery',
Tianjiec3850642020-05-13 14:47:31 -0700270 'system_ext', 'vbmeta', 'vbmeta_system', 'vbmeta_vendor', 'vendor',
271 'vendor_boot']
Tao Bao6b0b2f92017-03-05 11:38:11 -0800272
Tao Bao2dd1c482017-02-03 16:49:39 -0800273
Tao Baofabe0832018-01-17 15:52:28 -0800274class PayloadSigner(object):
275 """A class that wraps the payload signing works.
276
277 When generating a Payload, hashes of the payload and metadata files will be
278 signed with the device key, either by calling an external payload signer or
279 by calling openssl with the package key. This class provides a unified
280 interface, so that callers can just call PayloadSigner.Sign().
281
282 If an external payload signer has been specified (OPTIONS.payload_signer), it
283 calls the signer with the provided args (OPTIONS.payload_signer_args). Note
284 that the signing key should be provided as part of the payload_signer_args.
285 Otherwise without an external signer, it uses the package key
286 (OPTIONS.package_key) and calls openssl for the signing works.
287 """
288
289 def __init__(self):
290 if OPTIONS.payload_signer is None:
291 # Prepare the payload signing key.
292 private_key = OPTIONS.package_key + OPTIONS.private_key_suffix
293 pw = OPTIONS.key_passwords[OPTIONS.package_key]
294
295 cmd = ["openssl", "pkcs8", "-in", private_key, "-inform", "DER"]
296 cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"])
297 signing_key = common.MakeTempFile(prefix="key-", suffix=".key")
298 cmd.extend(["-out", signing_key])
Tao Baobec89c12018-10-15 11:53:28 -0700299 common.RunAndCheckOutput(cmd, verbose=False)
Tao Baofabe0832018-01-17 15:52:28 -0800300
301 self.signer = "openssl"
302 self.signer_args = ["pkeyutl", "-sign", "-inkey", signing_key,
303 "-pkeyopt", "digest:sha256"]
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700304 self.maximum_signature_size = self._GetMaximumSignatureSizeInBytes(
305 signing_key)
Tao Baofabe0832018-01-17 15:52:28 -0800306 else:
307 self.signer = OPTIONS.payload_signer
308 self.signer_args = OPTIONS.payload_signer_args
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700309 if OPTIONS.payload_signer_maximum_signature_size:
310 self.maximum_signature_size = int(
311 OPTIONS.payload_signer_maximum_signature_size)
xunchang376cc7c2019-04-08 23:04:58 -0700312 else:
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700313 # The legacy config uses RSA2048 keys.
314 logger.warning("The maximum signature size for payload signer is not"
315 " set, default to 256 bytes.")
316 self.maximum_signature_size = 256
xunchang376cc7c2019-04-08 23:04:58 -0700317
318 @staticmethod
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700319 def _GetMaximumSignatureSizeInBytes(signing_key):
320 out_signature_size_file = common.MakeTempFile("signature_size")
321 cmd = ["delta_generator", "--out_maximum_signature_size_file={}".format(
322 out_signature_size_file), "--private_key={}".format(signing_key)]
323 common.RunAndCheckOutput(cmd)
324 with open(out_signature_size_file) as f:
325 signature_size = f.read().rstrip()
Luca Stefani88e1a142020-03-27 14:05:12 +0100326 logger.info("%s outputs the maximum signature size: %s", cmd[0],
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700327 signature_size)
328 return int(signature_size)
Tao Baofabe0832018-01-17 15:52:28 -0800329
330 def Sign(self, in_file):
331 """Signs the given input file. Returns the output filename."""
332 out_file = common.MakeTempFile(prefix="signed-", suffix=".bin")
333 cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file]
Tao Bao718faed2019-08-02 13:24:19 -0700334 common.RunAndCheckOutput(cmd)
Tao Baofabe0832018-01-17 15:52:28 -0800335 return out_file
336
337
Tao Bao40b18822018-01-30 18:19:04 -0800338class Payload(object):
339 """Manages the creation and the signing of an A/B OTA Payload."""
340
341 PAYLOAD_BIN = 'payload.bin'
342 PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
Tao Baof7140c02018-01-30 17:09:24 -0800343 SECONDARY_PAYLOAD_BIN = 'secondary/payload.bin'
344 SECONDARY_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt'
Tao Bao40b18822018-01-30 18:19:04 -0800345
Tao Bao667ff572018-02-10 00:02:40 -0800346 def __init__(self, secondary=False):
347 """Initializes a Payload instance.
348
349 Args:
350 secondary: Whether it's generating a secondary payload (default: False).
351 """
Tao Bao40b18822018-01-30 18:19:04 -0800352 self.payload_file = None
353 self.payload_properties = None
Tao Bao667ff572018-02-10 00:02:40 -0800354 self.secondary = secondary
Tao Bao40b18822018-01-30 18:19:04 -0800355
Tao Baof0c4aa22018-04-30 20:29:30 -0700356 def _Run(self, cmd): # pylint: disable=no-self-use
Tao Bao718faed2019-08-02 13:24:19 -0700357 # Don't pipe (buffer) the output if verbose is set. Let
358 # brillo_update_payload write to stdout/stderr directly, so its progress can
359 # be monitored.
360 if OPTIONS.verbose:
361 common.RunAndCheckOutput(cmd, stdout=None, stderr=None)
362 else:
363 common.RunAndCheckOutput(cmd)
364
Tao Bao40b18822018-01-30 18:19:04 -0800365 def Generate(self, target_file, source_file=None, additional_args=None):
366 """Generates a payload from the given target-files zip(s).
367
368 Args:
369 target_file: The filename of the target build target-files zip.
370 source_file: The filename of the source build target-files zip; or None if
371 generating a full OTA.
372 additional_args: A list of additional args that should be passed to
373 brillo_update_payload script; or None.
374 """
375 if additional_args is None:
376 additional_args = []
377
378 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
379 cmd = ["brillo_update_payload", "generate",
380 "--payload", payload_file,
381 "--target_image", target_file]
382 if source_file is not None:
383 cmd.extend(["--source_image", source_file])
Tianjie Xu1b079832019-08-28 12:19:23 -0700384 if OPTIONS.disable_fec_computation:
385 cmd.extend(["--disable_fec_computation", "true"])
Tao Bao40b18822018-01-30 18:19:04 -0800386 cmd.extend(additional_args)
Tao Bao718faed2019-08-02 13:24:19 -0700387 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800388
389 self.payload_file = payload_file
390 self.payload_properties = None
391
392 def Sign(self, payload_signer):
393 """Generates and signs the hashes of the payload and metadata.
394
395 Args:
396 payload_signer: A PayloadSigner() instance that serves the signing work.
397
398 Raises:
399 AssertionError: On any failure when calling brillo_update_payload script.
400 """
401 assert isinstance(payload_signer, PayloadSigner)
402
403 # 1. Generate hashes of the payload and metadata files.
404 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
405 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
406 cmd = ["brillo_update_payload", "hash",
407 "--unsigned_payload", self.payload_file,
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700408 "--signature_size", str(payload_signer.maximum_signature_size),
Tao Bao40b18822018-01-30 18:19:04 -0800409 "--metadata_hash_file", metadata_sig_file,
410 "--payload_hash_file", payload_sig_file]
Tao Bao718faed2019-08-02 13:24:19 -0700411 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800412
413 # 2. Sign the hashes.
414 signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
415 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
416
417 # 3. Insert the signatures back into the payload file.
418 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
419 suffix=".bin")
420 cmd = ["brillo_update_payload", "sign",
421 "--unsigned_payload", self.payload_file,
422 "--payload", signed_payload_file,
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700423 "--signature_size", str(payload_signer.maximum_signature_size),
Tao Bao40b18822018-01-30 18:19:04 -0800424 "--metadata_signature_file", signed_metadata_sig_file,
425 "--payload_signature_file", signed_payload_sig_file]
Tao Bao718faed2019-08-02 13:24:19 -0700426 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800427
428 # 4. Dump the signed payload properties.
429 properties_file = common.MakeTempFile(prefix="payload-properties-",
430 suffix=".txt")
431 cmd = ["brillo_update_payload", "properties",
432 "--payload", signed_payload_file,
433 "--properties_file", properties_file]
Tao Bao718faed2019-08-02 13:24:19 -0700434 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800435
Tao Bao667ff572018-02-10 00:02:40 -0800436 if self.secondary:
437 with open(properties_file, "a") as f:
438 f.write("SWITCH_SLOT_ON_REBOOT=0\n")
439
Tao Bao40b18822018-01-30 18:19:04 -0800440 if OPTIONS.wipe_user_data:
441 with open(properties_file, "a") as f:
442 f.write("POWERWASH=1\n")
443
444 self.payload_file = signed_payload_file
445 self.payload_properties = properties_file
446
Tao Bao667ff572018-02-10 00:02:40 -0800447 def WriteToZip(self, output_zip):
Tao Bao40b18822018-01-30 18:19:04 -0800448 """Writes the payload to the given zip.
449
450 Args:
451 output_zip: The output ZipFile instance.
452 """
453 assert self.payload_file is not None
454 assert self.payload_properties is not None
455
Tao Bao667ff572018-02-10 00:02:40 -0800456 if self.secondary:
Tao Baof7140c02018-01-30 17:09:24 -0800457 payload_arcname = Payload.SECONDARY_PAYLOAD_BIN
458 payload_properties_arcname = Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT
459 else:
460 payload_arcname = Payload.PAYLOAD_BIN
461 payload_properties_arcname = Payload.PAYLOAD_PROPERTIES_TXT
462
Tao Bao40b18822018-01-30 18:19:04 -0800463 # Add the signed payload file and properties into the zip. In order to
464 # support streaming, we pack them as ZIP_STORED. So these entries can be
465 # read directly with the offset and length pairs.
Tao Baof7140c02018-01-30 17:09:24 -0800466 common.ZipWrite(output_zip, self.payload_file, arcname=payload_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800467 compress_type=zipfile.ZIP_STORED)
468 common.ZipWrite(output_zip, self.payload_properties,
Tao Baof7140c02018-01-30 17:09:24 -0800469 arcname=payload_properties_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800470 compress_type=zipfile.ZIP_STORED)
471
472
Doug Zongkereef39442009-04-02 12:14:19 -0700473def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200474 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700475
Doug Zongker951495f2009-08-14 12:44:19 -0700476 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
477 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700478
479
Tao Bao481bab82017-12-21 11:23:09 -0800480def _LoadOemDicts(oem_source):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800481 """Returns the list of loaded OEM properties dict."""
Tao Bao481bab82017-12-21 11:23:09 -0800482 if not oem_source:
483 return None
484
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800485 oem_dicts = []
Tao Bao481bab82017-12-21 11:23:09 -0800486 for oem_file in oem_source:
487 with open(oem_file) as fp:
488 oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines()))
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800489 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700490
Doug Zongkereef39442009-04-02 12:14:19 -0700491
Tao Baod42e97e2016-11-30 12:11:57 -0800492def _WriteRecoveryImageToBoot(script, output_zip):
493 """Find and write recovery image to /boot in two-step OTA.
494
495 In two-step OTAs, we write recovery image to /boot as the first step so that
496 we can reboot to there and install a new recovery image to /recovery.
497 A special "recovery-two-step.img" will be preferred, which encodes the correct
498 path of "/boot". Otherwise the device may show "device is corrupt" message
499 when booting into /boot.
500
501 Fall back to using the regular recovery.img if the two-step recovery image
502 doesn't exist. Note that rebuilding the special image at this point may be
503 infeasible, because we don't have the desired boot signer and keys when
504 calling ota_from_target_files.py.
505 """
506
507 recovery_two_step_img_name = "recovery-two-step.img"
508 recovery_two_step_img_path = os.path.join(
Tao Bao04808502019-07-25 23:11:41 -0700509 OPTIONS.input_tmp, "OTA", recovery_two_step_img_name)
Tao Baod42e97e2016-11-30 12:11:57 -0800510 if os.path.exists(recovery_two_step_img_path):
Tao Bao04808502019-07-25 23:11:41 -0700511 common.ZipWrite(
512 output_zip,
513 recovery_two_step_img_path,
514 arcname=recovery_two_step_img_name)
Tao Bao32fcdab2018-10-12 10:30:39 -0700515 logger.info(
516 "two-step package: using %s in stage 1/3", recovery_two_step_img_name)
Tao Baod42e97e2016-11-30 12:11:57 -0800517 script.WriteRawImage("/boot", recovery_two_step_img_name)
518 else:
Tao Bao32fcdab2018-10-12 10:30:39 -0700519 logger.info("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800520 # The "recovery.img" entry has been written into package earlier.
521 script.WriteRawImage("/boot", "recovery.img")
522
523
Bill Peckhame868aec2019-09-17 17:06:47 -0700524def HasRecoveryPatch(target_files_zip, info_dict):
525 board_uses_vendorimage = info_dict.get("board_uses_vendorimage") == "true"
526
527 if board_uses_vendorimage:
528 target_files_dir = "VENDOR"
529 else:
530 target_files_dir = "SYSTEM/vendor"
531
532 patch = "%s/recovery-from-boot.p" % target_files_dir
533 img = "%s/etc/recovery.img" %target_files_dir
534
Tao Baof2cffbd2015-07-22 12:33:18 -0700535 namelist = [name for name in target_files_zip.namelist()]
Bill Peckhame868aec2019-09-17 17:06:47 -0700536 return (patch in namelist or img in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700537
Tao Bao457cbf62017-03-06 09:56:01 -0800538
Yifan Hong51d37562019-04-23 17:06:46 -0700539def HasPartition(target_files_zip, partition):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700540 try:
Yifan Hong51d37562019-04-23 17:06:46 -0700541 target_files_zip.getinfo(partition.upper() + "/")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700542 return True
543 except KeyError:
544 return False
545
Tao Bao457cbf62017-03-06 09:56:01 -0800546
Yifan Hong9276cf02019-08-21 16:37:04 -0700547def HasTrebleEnabled(target_files, target_info):
548 def HasVendorPartition(target_files):
549 if os.path.isdir(target_files):
550 return os.path.isdir(os.path.join(target_files, "VENDOR"))
551 if zipfile.is_zipfile(target_files):
552 return HasPartition(zipfile.ZipFile(target_files), "vendor")
553 raise ValueError("Unknown target_files argument")
Yifan Hong51d37562019-04-23 17:06:46 -0700554
Yifan Hong9276cf02019-08-21 16:37:04 -0700555 return (HasVendorPartition(target_files) and
Tao Bao481bab82017-12-21 11:23:09 -0800556 target_info.GetBuildProp("ro.treble.enabled") == "true")
Tao Baobcd1d162017-08-26 13:10:26 -0700557
558
Tao Bao481bab82017-12-21 11:23:09 -0800559def WriteFingerprintAssertion(script, target_info, source_info):
560 source_oem_props = source_info.oem_props
561 target_oem_props = target_info.oem_props
Michael Runge6e836112014-04-15 17:40:21 -0700562
Tao Bao481bab82017-12-21 11:23:09 -0800563 if source_oem_props is None and target_oem_props is None:
564 script.AssertSomeFingerprint(
565 source_info.fingerprint, target_info.fingerprint)
566 elif source_oem_props is not None and target_oem_props is not None:
567 script.AssertSomeThumbprint(
568 target_info.GetBuildProp("ro.build.thumbprint"),
569 source_info.GetBuildProp("ro.build.thumbprint"))
570 elif source_oem_props is None and target_oem_props is not None:
571 script.AssertFingerprintOrThumbprint(
572 source_info.fingerprint,
573 target_info.GetBuildProp("ro.build.thumbprint"))
574 else:
575 script.AssertFingerprintOrThumbprint(
576 target_info.fingerprint,
577 source_info.GetBuildProp("ro.build.thumbprint"))
Doug Zongker73ef8252009-07-23 15:12:53 -0700578
Doug Zongkerfc44a512014-08-26 13:10:25 -0700579
Yifan Hong9276cf02019-08-21 16:37:04 -0700580def CheckVintfIfTrebleEnabled(target_files, target_info):
581 """Checks compatibility info of the input target files.
Tao Bao21803d32017-04-19 10:16:09 -0700582
Yifan Hong9276cf02019-08-21 16:37:04 -0700583 Metadata used for compatibility verification is retrieved from target_zip.
Tao Bao21803d32017-04-19 10:16:09 -0700584
Yifan Hong9276cf02019-08-21 16:37:04 -0700585 Compatibility should only be checked for devices that have enabled
Tao Baobcd1d162017-08-26 13:10:26 -0700586 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700587
588 Args:
Yifan Hong9276cf02019-08-21 16:37:04 -0700589 target_files: Path to zip file containing the source files to be included
590 for OTA. Can also be the path to extracted directory.
Tao Bao481bab82017-12-21 11:23:09 -0800591 target_info: The BuildInfo instance that holds the target build info.
Tao Bao21803d32017-04-19 10:16:09 -0700592 """
593
Tao Baobcd1d162017-08-26 13:10:26 -0700594 # Will only proceed if the target has enabled the Treble support (as well as
595 # having a /vendor partition).
Yifan Hong9276cf02019-08-21 16:37:04 -0700596 if not HasTrebleEnabled(target_files, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700597 return
598
xunchangabfa2652019-02-19 16:27:10 -0800599 # Skip adding the compatibility package as a workaround for b/114240221. The
600 # compatibility will always fail on devices without qualified kernels.
601 if OPTIONS.skip_compatibility_check:
602 return
603
Yifan Hong9276cf02019-08-21 16:37:04 -0700604 if not check_target_files_vintf.CheckVintf(target_files, target_info):
605 raise RuntimeError("VINTF compatibility check failed")
Tao Bao21803d32017-04-19 10:16:09 -0700606
607
Tianjie Xuf67dd802019-05-20 17:50:36 -0700608def GetBlockDifferences(target_zip, source_zip, target_info, source_info,
609 device_specific):
610 """Returns a ordered dict of block differences with partition name as key."""
611
612 def GetIncrementalBlockDifferenceForPartition(name):
613 if not HasPartition(source_zip, name):
614 raise RuntimeError("can't generate incremental that adds {}".format(name))
615
616 partition_src = common.GetUserImage(name, OPTIONS.source_tmp, source_zip,
617 info_dict=source_info,
618 allow_shared_blocks=allow_shared_blocks)
619
620 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
621 name, 4096, target_info)
622 partition_tgt = common.GetUserImage(name, OPTIONS.target_tmp, target_zip,
623 info_dict=target_info,
624 allow_shared_blocks=allow_shared_blocks,
625 hashtree_info_generator=
626 hashtree_info_generator)
627
628 # Check the first block of the source system partition for remount R/W only
629 # if the filesystem is ext4.
630 partition_source_info = source_info["fstab"]["/" + name]
631 check_first_block = partition_source_info.fs_type == "ext4"
632 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
633 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
634 # b) the blocks listed in block map may not contain all the bytes for a
635 # given file (because they're rounded to be 4K-aligned).
636 partition_target_info = target_info["fstab"]["/" + name]
637 disable_imgdiff = (partition_source_info.fs_type == "squashfs" or
638 partition_target_info.fs_type == "squashfs")
Xindong Xu2a7aaa62020-03-13 15:59:22 +0800639 return common.BlockDifference(name, partition_tgt, partition_src,
Tianjie Xuf67dd802019-05-20 17:50:36 -0700640 check_first_block,
641 version=blockimgdiff_version,
642 disable_imgdiff=disable_imgdiff)
643
644 if source_zip:
645 # See notes in common.GetUserImage()
646 allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or
647 target_info.get('ext4_share_dup_blocks') == "true")
648 blockimgdiff_version = max(
649 int(i) for i in target_info.get(
650 "blockimgdiff_versions", "1").split(","))
651 assert blockimgdiff_version >= 3
652
653 block_diff_dict = collections.OrderedDict()
654 partition_names = ["system", "vendor", "product", "odm", "system_ext"]
655 for partition in partition_names:
656 if not HasPartition(target_zip, partition):
657 continue
658 # Full OTA update.
659 if not source_zip:
660 tgt = common.GetUserImage(partition, OPTIONS.input_tmp, target_zip,
661 info_dict=target_info,
662 reset_file_map=True)
663 block_diff_dict[partition] = common.BlockDifference(partition, tgt,
664 src=None)
665 # Incremental OTA update.
666 else:
667 block_diff_dict[partition] = GetIncrementalBlockDifferenceForPartition(
668 partition)
669 assert "system" in block_diff_dict
670
671 # Get the block diffs from the device specific script. If there is a
672 # duplicate block diff for a partition, ignore the diff in the generic script
673 # and use the one in the device specific script instead.
674 if source_zip:
675 device_specific_diffs = device_specific.IncrementalOTA_GetBlockDifferences()
676 function_name = "IncrementalOTA_GetBlockDifferences"
677 else:
678 device_specific_diffs = device_specific.FullOTA_GetBlockDifferences()
679 function_name = "FullOTA_GetBlockDifferences"
680
681 if device_specific_diffs:
682 assert all(isinstance(diff, common.BlockDifference)
683 for diff in device_specific_diffs), \
684 "{} is not returning a list of BlockDifference objects".format(
685 function_name)
686 for diff in device_specific_diffs:
687 if diff.partition in block_diff_dict:
688 logger.warning("Duplicate block difference found. Device specific block"
689 " diff for partition '%s' overrides the one in generic"
690 " script.", diff.partition)
691 block_diff_dict[diff.partition] = diff
692
693 return block_diff_dict
694
695
Tao Bao491d7e22018-02-21 13:17:22 -0800696def WriteFullOTAPackage(input_zip, output_file):
Tao Bao1c320f82019-10-04 23:25:12 -0700697 target_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700698
Tao Bao481bab82017-12-21 11:23:09 -0800699 # We don't know what version it will be installed on top of. We expect the API
700 # just won't change very often. Similarly for fstab, it might have changed in
701 # the target build.
702 target_api_version = target_info["recovery_api_version"]
703 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700704
Tao Bao481bab82017-12-21 11:23:09 -0800705 if target_info.oem_props and not OPTIONS.oem_no_mount:
706 target_info.WriteMountOemScript(script)
707
Tao Baodf3a48b2018-01-10 16:30:43 -0800708 metadata = GetPackageMetadata(target_info)
Doug Zongker2ea21062010-04-28 16:05:21 -0700709
Tao Bao491d7e22018-02-21 13:17:22 -0800710 if not OPTIONS.no_signing:
711 staging_file = common.MakeTempFile(suffix='.zip')
712 else:
713 staging_file = output_file
714
715 output_zip = zipfile.ZipFile(
716 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
717
Doug Zongker05d3dea2009-06-22 11:32:31 -0700718 device_specific = common.DeviceSpecificParams(
719 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800720 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700721 output_zip=output_zip,
722 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700723 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700724 metadata=metadata,
725 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700726
Bill Peckhame868aec2019-09-17 17:06:47 -0700727 assert HasRecoveryPatch(input_zip, info_dict=OPTIONS.info_dict)
Doug Zongkerc9253822014-02-04 12:17:58 -0800728
Tao Bao481bab82017-12-21 11:23:09 -0800729 # Assertions (e.g. downgrade check, device properties check).
730 ts = target_info.GetBuildProp("ro.build.date.utc")
731 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700732 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700733
Tao Bao481bab82017-12-21 11:23:09 -0800734 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700735 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800736
Tianjie Xuf67dd802019-05-20 17:50:36 -0700737 block_diff_dict = GetBlockDifferences(target_zip=input_zip, source_zip=None,
738 target_info=target_info,
739 source_info=None,
740 device_specific=device_specific)
741
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800742 # Two-step package strategy (in chronological order, which is *not*
743 # the order in which the generated script has things):
744 #
745 # if stage is not "2/3" or "3/3":
746 # write recovery image to boot partition
747 # set stage to "2/3"
748 # reboot to boot partition and restart recovery
749 # else if stage is "2/3":
750 # write recovery image to recovery partition
751 # set stage to "3/3"
752 # reboot to recovery partition and restart recovery
753 # else:
754 # (stage must be "3/3")
755 # set stage to ""
756 # do normal full package installation:
757 # wipe and install system, boot image, etc.
758 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700759 # complete script normally
760 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800761
762 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
763 OPTIONS.input_tmp, "RECOVERY")
764 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800765 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800766 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800767 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800768 assert fs.fs_type.upper() == "EMMC", \
769 "two-step packages only supported on devices with EMMC /misc partitions"
770 bcb_dev = {"bcb_dev": fs.device}
771 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
772 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700773if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800774""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800775
776 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
777 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800778 script.WriteRawImage("/recovery", "recovery.img")
779 script.AppendExtra("""
780set_stage("%(bcb_dev)s", "3/3");
781reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700782else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800783""" % bcb_dev)
784
Tao Baod42e97e2016-11-30 12:11:57 -0800785 # Stage 3/3: Make changes.
786 script.Comment("Stage 3/3")
787
Tao Bao6c55a8a2015-04-08 15:30:27 -0700788 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800789 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700790
Doug Zongkere5ff5902012-01-17 10:55:37 -0800791 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700792
Tianjie Xuf67dd802019-05-20 17:50:36 -0700793 # All other partitions as well as the data wipe use 10% of the progress, and
794 # the update of the system partition takes the remaining progress.
795 system_progress = 0.9 - (len(block_diff_dict) - 1) * 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700796 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800797 system_progress -= 0.1
Tianjie Xuf67dd802019-05-20 17:50:36 -0700798 progress_dict = {partition: 0.1 for partition in block_diff_dict}
799 progress_dict["system"] = system_progress
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700800
Yifan Hong10c530d2018-12-27 17:34:18 -0800801 if target_info.get('use_dynamic_partitions') == "true":
802 # Use empty source_info_dict to indicate that all partitions / groups must
803 # be re-added.
804 dynamic_partitions_diff = common.DynamicPartitionsDifference(
805 info_dict=OPTIONS.info_dict,
Tianjie Xuf67dd802019-05-20 17:50:36 -0700806 block_diffs=block_diff_dict.values(),
Yifan Hong10c530d2018-12-27 17:34:18 -0800807 progress_dict=progress_dict)
808 dynamic_partitions_diff.WriteScript(script, output_zip,
809 write_verify_script=OPTIONS.verify)
810 else:
Tianjie Xuf67dd802019-05-20 17:50:36 -0700811 for block_diff in block_diff_dict.values():
Yifan Hong10c530d2018-12-27 17:34:18 -0800812 block_diff.WriteScript(script, output_zip,
813 progress=progress_dict.get(block_diff.partition),
814 write_verify_script=OPTIONS.verify)
Doug Zongker73ef8252009-07-23 15:12:53 -0700815
Yifan Hong9276cf02019-08-21 16:37:04 -0700816 CheckVintfIfTrebleEnabled(OPTIONS.input_tmp, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700817
Yifan Hong10c530d2018-12-27 17:34:18 -0800818 boot_img = common.GetBootableImage(
819 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Tao Bao481bab82017-12-21 11:23:09 -0800820 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700821 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700822
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700823 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700824
Tianjie Xuf67dd802019-05-20 17:50:36 -0700825 script.ShowProgress(0.1, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700826 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700827
Doug Zongker1c390a22009-05-14 19:06:36 -0700828 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700829 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700830
Doug Zongker14833602010-02-02 13:12:04 -0800831 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800832
Doug Zongker922206e2014-03-04 13:16:24 -0800833 if OPTIONS.wipe_user_data:
834 script.ShowProgress(0.1, 10)
835 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700836
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800837 if OPTIONS.two_step:
838 script.AppendExtra("""
839set_stage("%(bcb_dev)s", "");
840""" % bcb_dev)
841 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800842
843 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
844 script.Comment("Stage 1/3")
845 _WriteRecoveryImageToBoot(script, output_zip)
846
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800847 script.AppendExtra("""
848set_stage("%(bcb_dev)s", "2/3");
849reboot_now("%(bcb_dev)s", "");
850endif;
851endif;
852""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800853
Tao Bao5d182562016-02-23 11:38:39 -0800854 script.SetProgress(1)
855 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800856 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -0800857
858 # We haven't written the metadata entry, which will be done in
859 # FinalizeMetadata.
860 common.ZipClose(output_zip)
861
862 needed_property_files = (
863 NonAbOtaPropertyFiles(),
864 )
865 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Doug Zongker2ea21062010-04-28 16:05:21 -0700866
Doug Zongkerfc44a512014-08-26 13:10:25 -0700867
xunchang1cfe2512019-02-19 14:14:48 -0800868def WriteMetadata(metadata, output):
869 """Writes the metadata to the zip archive or a file.
870
871 Args:
872 metadata: The metadata dict for the package.
873 output: A ZipFile object or a string of the output file path.
874 """
875
Tao Bao59cf0c52019-06-25 10:04:24 -0700876 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.items())])
xunchang1cfe2512019-02-19 14:14:48 -0800877 if isinstance(output, zipfile.ZipFile):
878 common.ZipWriteStr(output, METADATA_NAME, value,
879 compress_type=zipfile.ZIP_STORED)
880 return
881
882 with open(output, 'w') as f:
883 f.write(value)
Doug Zongkereef39442009-04-02 12:14:19 -0700884
Doug Zongkerfc44a512014-08-26 13:10:25 -0700885
Tao Bao481bab82017-12-21 11:23:09 -0800886def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -0800887 # Only incremental OTAs are allowed to reach here.
888 assert OPTIONS.incremental_source is not None
889
Tao Bao481bab82017-12-21 11:23:09 -0800890 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
891 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Bao59cf0c52019-06-25 10:04:24 -0700892 is_downgrade = int(post_timestamp) < int(pre_timestamp)
Tao Baob31892e2017-02-07 11:21:17 -0800893
894 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800895 if not is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700896 raise RuntimeError(
897 "--downgrade or --override_timestamp specified but no downgrade "
898 "detected: pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800899 metadata["ota-downgrade"] = "yes"
Tao Baob31892e2017-02-07 11:21:17 -0800900 else:
901 if is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700902 raise RuntimeError(
903 "Downgrade detected based on timestamp check: pre: %s, post: %s. "
904 "Need to specify --override_timestamp OR --downgrade to allow "
905 "building the incremental." % (pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800906
907
Tao Baodf3a48b2018-01-10 16:30:43 -0800908def GetPackageMetadata(target_info, source_info=None):
909 """Generates and returns the metadata dict.
910
911 It generates a dict() that contains the info to be written into an OTA
912 package (META-INF/com/android/metadata). It also handles the detection of
Tao Baofaa8e0b2018-04-12 14:31:43 -0700913 downgrade / data wipe based on the global options.
Tao Baodf3a48b2018-01-10 16:30:43 -0800914
915 Args:
916 target_info: The BuildInfo instance that holds the target build info.
917 source_info: The BuildInfo instance that holds the source build info, or
918 None if generating full OTA.
919
920 Returns:
921 A dict to be written into package metadata entry.
922 """
Tao Bao1c320f82019-10-04 23:25:12 -0700923 assert isinstance(target_info, common.BuildInfo)
924 assert source_info is None or isinstance(source_info, common.BuildInfo)
Tao Baodf3a48b2018-01-10 16:30:43 -0800925
926 metadata = {
927 'post-build' : target_info.fingerprint,
928 'post-build-incremental' : target_info.GetBuildProp(
929 'ro.build.version.incremental'),
Tao Bao35dc2552018-02-01 13:18:00 -0800930 'post-sdk-level' : target_info.GetBuildProp(
931 'ro.build.version.sdk'),
932 'post-security-patch-level' : target_info.GetBuildProp(
933 'ro.build.version.security_patch'),
Tao Baodf3a48b2018-01-10 16:30:43 -0800934 }
935
936 if target_info.is_ab:
937 metadata['ota-type'] = 'AB'
938 metadata['ota-required-cache'] = '0'
939 else:
940 metadata['ota-type'] = 'BLOCK'
941
942 if OPTIONS.wipe_user_data:
943 metadata['ota-wipe'] = 'yes'
944
Tao Bao393eeb42019-03-06 16:00:38 -0800945 if OPTIONS.retrofit_dynamic_partitions:
946 metadata['ota-retrofit-dynamic-partitions'] = 'yes'
947
Tao Baodf3a48b2018-01-10 16:30:43 -0800948 is_incremental = source_info is not None
949 if is_incremental:
950 metadata['pre-build'] = source_info.fingerprint
951 metadata['pre-build-incremental'] = source_info.GetBuildProp(
952 'ro.build.version.incremental')
953 metadata['pre-device'] = source_info.device
954 else:
955 metadata['pre-device'] = target_info.device
956
Tao Baofaa8e0b2018-04-12 14:31:43 -0700957 # Use the actual post-timestamp, even for a downgrade case.
958 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
959
960 # Detect downgrades and set up downgrade flags accordingly.
Tao Baodf3a48b2018-01-10 16:30:43 -0800961 if is_incremental:
962 HandleDowngradeMetadata(metadata, target_info, source_info)
Tao Baodf3a48b2018-01-10 16:30:43 -0800963
964 return metadata
965
966
Tao Baod3fc38a2018-03-08 16:09:01 -0800967class PropertyFiles(object):
968 """A class that computes the property-files string for an OTA package.
969
970 A property-files string is a comma-separated string that contains the
971 offset/size info for an OTA package. The entries, which must be ZIP_STORED,
972 can be fetched directly with the package URL along with the offset/size info.
973 These strings can be used for streaming A/B OTAs, or allowing an updater to
974 download package metadata entry directly, without paying the cost of
975 downloading entire package.
Tao Baofe5b69a2018-03-02 09:47:43 -0800976
Tao Baocc8e2662018-03-01 19:30:00 -0800977 Computing the final property-files string requires two passes. Because doing
978 the whole package signing (with signapk.jar) will possibly reorder the ZIP
979 entries, which may in turn invalidate earlier computed ZIP entry offset/size
980 values.
981
982 This class provides functions to be called for each pass. The general flow is
983 as follows.
984
Tao Baod3fc38a2018-03-08 16:09:01 -0800985 property_files = PropertyFiles()
Tao Baocc8e2662018-03-01 19:30:00 -0800986 # The first pass, which writes placeholders before doing initial signing.
987 property_files.Compute()
988 SignOutput()
989
990 # The second pass, by replacing the placeholders with actual data.
991 property_files.Finalize()
992 SignOutput()
993
994 And the caller can additionally verify the final result.
995
996 property_files.Verify()
Tao Baofe5b69a2018-03-02 09:47:43 -0800997 """
998
Tao Baocc8e2662018-03-01 19:30:00 -0800999 def __init__(self):
Tao Baod3fc38a2018-03-08 16:09:01 -08001000 self.name = None
1001 self.required = ()
1002 self.optional = ()
Tao Baofe5b69a2018-03-02 09:47:43 -08001003
Tao Baocc8e2662018-03-01 19:30:00 -08001004 def Compute(self, input_zip):
1005 """Computes and returns a property-files string with placeholders.
Tao Baofe5b69a2018-03-02 09:47:43 -08001006
Tao Baocc8e2662018-03-01 19:30:00 -08001007 We reserve extra space for the offset and size of the metadata entry itself,
1008 although we don't know the final values until the package gets signed.
Tao Baofe5b69a2018-03-02 09:47:43 -08001009
Tao Baocc8e2662018-03-01 19:30:00 -08001010 Args:
1011 input_zip: The input ZIP file.
Tao Baofe5b69a2018-03-02 09:47:43 -08001012
Tao Baocc8e2662018-03-01 19:30:00 -08001013 Returns:
1014 A string with placeholders for the metadata offset/size info, e.g.
1015 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1016 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001017 return self.GetPropertyFilesString(input_zip, reserve_space=True)
Tao Baofe5b69a2018-03-02 09:47:43 -08001018
Tao Baod2ce2ed2018-03-16 12:59:42 -07001019 class InsufficientSpaceException(Exception):
1020 pass
1021
Tao Baocc8e2662018-03-01 19:30:00 -08001022 def Finalize(self, input_zip, reserved_length):
1023 """Finalizes a property-files string with actual METADATA offset/size info.
1024
1025 The input ZIP file has been signed, with the ZIP entries in the desired
1026 place (signapk.jar will possibly reorder the ZIP entries). Now we compute
1027 the ZIP entry offsets and construct the property-files string with actual
1028 data. Note that during this process, we must pad the property-files string
1029 to the reserved length, so that the METADATA entry size remains the same.
1030 Otherwise the entries' offsets and sizes may change again.
1031
1032 Args:
1033 input_zip: The input ZIP file.
1034 reserved_length: The reserved length of the property-files string during
1035 the call to Compute(). The final string must be no more than this
1036 size.
1037
1038 Returns:
1039 A property-files string including the metadata offset/size info, e.g.
1040 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379 ".
1041
1042 Raises:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001043 InsufficientSpaceException: If the reserved length is insufficient to hold
1044 the final string.
Tao Baocc8e2662018-03-01 19:30:00 -08001045 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001046 result = self.GetPropertyFilesString(input_zip, reserve_space=False)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001047 if len(result) > reserved_length:
1048 raise self.InsufficientSpaceException(
1049 'Insufficient reserved space: reserved={}, actual={}'.format(
1050 reserved_length, len(result)))
1051
Tao Baocc8e2662018-03-01 19:30:00 -08001052 result += ' ' * (reserved_length - len(result))
1053 return result
1054
1055 def Verify(self, input_zip, expected):
1056 """Verifies the input ZIP file contains the expected property-files string.
1057
1058 Args:
1059 input_zip: The input ZIP file.
1060 expected: The property-files string that's computed from Finalize().
1061
1062 Raises:
1063 AssertionError: On finding a mismatch.
1064 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001065 actual = self.GetPropertyFilesString(input_zip)
Tao Baocc8e2662018-03-01 19:30:00 -08001066 assert actual == expected, \
1067 "Mismatching streaming metadata: {} vs {}.".format(actual, expected)
1068
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001069 def GetPropertyFilesString(self, zip_file, reserve_space=False):
1070 """
1071 Constructs the property-files string per request.
1072
1073 Args:
1074 zip_file: The input ZIP file.
1075 reserved_length: The reserved length of the property-files string.
1076
1077 Returns:
1078 A property-files string including the metadata offset/size info, e.g.
1079 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1080 """
Tao Baocc8e2662018-03-01 19:30:00 -08001081
1082 def ComputeEntryOffsetSize(name):
1083 """Computes the zip entry offset and size."""
1084 info = zip_file.getinfo(name)
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001085 offset = info.header_offset
1086 offset += zipfile.sizeFileHeader
1087 offset += len(info.extra) + len(info.filename)
Tao Baocc8e2662018-03-01 19:30:00 -08001088 size = info.file_size
1089 return '%s:%d:%d' % (os.path.basename(name), offset, size)
1090
1091 tokens = []
Tao Bao85f16982018-03-08 16:28:33 -08001092 tokens.extend(self._GetPrecomputed(zip_file))
Tao Baocc8e2662018-03-01 19:30:00 -08001093 for entry in self.required:
1094 tokens.append(ComputeEntryOffsetSize(entry))
1095 for entry in self.optional:
1096 if entry in zip_file.namelist():
1097 tokens.append(ComputeEntryOffsetSize(entry))
1098
1099 # 'META-INF/com/android/metadata' is required. We don't know its actual
1100 # offset and length (as well as the values for other entries). So we reserve
Tao Baod2ce2ed2018-03-16 12:59:42 -07001101 # 15-byte as a placeholder ('offset:length'), which is sufficient to cover
1102 # the space for metadata entry. Because 'offset' allows a max of 10-digit
1103 # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the
1104 # reserved space serves the metadata entry only.
Tao Baocc8e2662018-03-01 19:30:00 -08001105 if reserve_space:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001106 tokens.append('metadata:' + ' ' * 15)
Tao Baocc8e2662018-03-01 19:30:00 -08001107 else:
1108 tokens.append(ComputeEntryOffsetSize(METADATA_NAME))
1109
1110 return ','.join(tokens)
Tao Baofe5b69a2018-03-02 09:47:43 -08001111
Tao Bao85f16982018-03-08 16:28:33 -08001112 def _GetPrecomputed(self, input_zip):
1113 """Computes the additional tokens to be included into the property-files.
1114
1115 This applies to tokens without actual ZIP entries, such as
1116 payload_metadadata.bin. We want to expose the offset/size to updaters, so
1117 that they can download the payload metadata directly with the info.
1118
1119 Args:
1120 input_zip: The input zip file.
1121
1122 Returns:
1123 A list of strings (tokens) to be added to the property-files string.
1124 """
1125 # pylint: disable=no-self-use
1126 # pylint: disable=unused-argument
1127 return []
1128
Tao Baofe5b69a2018-03-02 09:47:43 -08001129
Tao Baod3fc38a2018-03-08 16:09:01 -08001130class StreamingPropertyFiles(PropertyFiles):
1131 """A subclass for computing the property-files for streaming A/B OTAs."""
1132
1133 def __init__(self):
1134 super(StreamingPropertyFiles, self).__init__()
1135 self.name = 'ota-streaming-property-files'
1136 self.required = (
1137 # payload.bin and payload_properties.txt must exist.
1138 'payload.bin',
1139 'payload_properties.txt',
1140 )
1141 self.optional = (
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001142 # care_map is available only if dm-verity is enabled.
1143 'care_map.pb',
Tao Baod3fc38a2018-03-08 16:09:01 -08001144 'care_map.txt',
1145 # compatibility.zip is available only if target supports Treble.
1146 'compatibility.zip',
1147 )
1148
1149
Tao Bao85f16982018-03-08 16:28:33 -08001150class AbOtaPropertyFiles(StreamingPropertyFiles):
1151 """The property-files for A/B OTA that includes payload_metadata.bin info.
1152
1153 Since P, we expose one more token (aka property-file), in addition to the ones
1154 for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'.
1155 'payload_metadata.bin' is the header part of a payload ('payload.bin'), which
1156 doesn't exist as a separate ZIP entry, but can be used to verify if the
1157 payload can be applied on the given device.
1158
1159 For backward compatibility, we keep both of the 'ota-streaming-property-files'
1160 and the newly added 'ota-property-files' in P. The new token will only be
1161 available in 'ota-property-files'.
1162 """
1163
1164 def __init__(self):
1165 super(AbOtaPropertyFiles, self).__init__()
1166 self.name = 'ota-property-files'
1167
1168 def _GetPrecomputed(self, input_zip):
1169 offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip)
1170 return ['payload_metadata.bin:{}:{}'.format(offset, size)]
1171
1172 @staticmethod
1173 def _GetPayloadMetadataOffsetAndSize(input_zip):
1174 """Computes the offset and size of the payload metadata for a given package.
1175
1176 (From system/update_engine/update_metadata.proto)
1177 A delta update file contains all the deltas needed to update a system from
1178 one specific version to another specific version. The update format is
1179 represented by this struct pseudocode:
1180
1181 struct delta_update_file {
1182 char magic[4] = "CrAU";
1183 uint64 file_format_version;
1184 uint64 manifest_size; // Size of protobuf DeltaArchiveManifest
1185
1186 // Only present if format_version > 1:
1187 uint32 metadata_signature_size;
1188
1189 // The Bzip2 compressed DeltaArchiveManifest
1190 char manifest[metadata_signature_size];
1191
1192 // The signature of the metadata (from the beginning of the payload up to
1193 // this location, not including the signature itself). This is a
1194 // serialized Signatures message.
1195 char medatada_signature_message[metadata_signature_size];
1196
1197 // Data blobs for files, no specific format. The specific offset
1198 // and length of each data blob is recorded in the DeltaArchiveManifest.
1199 struct {
1200 char data[];
1201 } blobs[];
1202
1203 // These two are not signed:
1204 uint64 payload_signatures_message_size;
1205 char payload_signatures_message[];
1206 };
1207
1208 'payload-metadata.bin' contains all the bytes from the beginning of the
1209 payload, till the end of 'medatada_signature_message'.
1210 """
1211 payload_info = input_zip.getinfo('payload.bin')
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001212 payload_offset = payload_info.header_offset
1213 payload_offset += zipfile.sizeFileHeader
1214 payload_offset += len(payload_info.extra) + len(payload_info.filename)
Tao Bao85f16982018-03-08 16:28:33 -08001215 payload_size = payload_info.file_size
1216
Tao Bao59cf0c52019-06-25 10:04:24 -07001217 with input_zip.open('payload.bin') as payload_fp:
Tao Bao85f16982018-03-08 16:28:33 -08001218 header_bin = payload_fp.read(24)
1219
1220 # network byte order (big-endian)
1221 header = struct.unpack("!IQQL", header_bin)
1222
1223 # 'CrAU'
1224 magic = header[0]
1225 assert magic == 0x43724155, "Invalid magic: {:x}".format(magic)
1226
1227 manifest_size = header[2]
1228 metadata_signature_size = header[3]
1229 metadata_total = 24 + manifest_size + metadata_signature_size
1230 assert metadata_total < payload_size
1231
1232 return (payload_offset, metadata_total)
1233
1234
Tao Bao491d7e22018-02-21 13:17:22 -08001235class NonAbOtaPropertyFiles(PropertyFiles):
1236 """The property-files for non-A/B OTA.
1237
1238 For non-A/B OTA, the property-files string contains the info for METADATA
1239 entry, with which a system updater can be fetched the package metadata prior
1240 to downloading the entire package.
1241 """
1242
1243 def __init__(self):
1244 super(NonAbOtaPropertyFiles, self).__init__()
1245 self.name = 'ota-property-files'
1246
1247
Tao Baod3fc38a2018-03-08 16:09:01 -08001248def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
Tao Baofe5b69a2018-03-02 09:47:43 -08001249 """Finalizes the metadata and signs an A/B OTA package.
1250
1251 In order to stream an A/B OTA package, we need 'ota-streaming-property-files'
1252 that contains the offsets and sizes for the ZIP entries. An example
1253 property-files string is as follows.
1254
1255 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379"
1256
1257 OTA server can pass down this string, in addition to the package URL, to the
1258 system update client. System update client can then fetch individual ZIP
1259 entries (ZIP_STORED) directly at the given offset of the URL.
1260
1261 Args:
1262 metadata: The metadata dict for the package.
1263 input_file: The input ZIP filename that doesn't contain the package METADATA
1264 entry yet.
1265 output_file: The final output ZIP filename.
Tao Baod3fc38a2018-03-08 16:09:01 -08001266 needed_property_files: The list of PropertyFiles' to be generated.
Tao Baofe5b69a2018-03-02 09:47:43 -08001267 """
Tao Baofe5b69a2018-03-02 09:47:43 -08001268
Tao Baod2ce2ed2018-03-16 12:59:42 -07001269 def ComputeAllPropertyFiles(input_file, needed_property_files):
1270 # Write the current metadata entry with placeholders.
1271 with zipfile.ZipFile(input_file) as input_zip:
1272 for property_files in needed_property_files:
1273 metadata[property_files.name] = property_files.Compute(input_zip)
1274 namelist = input_zip.namelist()
Tao Baofe5b69a2018-03-02 09:47:43 -08001275
Tao Baod2ce2ed2018-03-16 12:59:42 -07001276 if METADATA_NAME in namelist:
1277 common.ZipDelete(input_file, METADATA_NAME)
1278 output_zip = zipfile.ZipFile(input_file, 'a')
1279 WriteMetadata(metadata, output_zip)
1280 common.ZipClose(output_zip)
1281
1282 if OPTIONS.no_signing:
1283 return input_file
1284
Tao Bao491d7e22018-02-21 13:17:22 -08001285 prelim_signing = common.MakeTempFile(suffix='.zip')
1286 SignOutput(input_file, prelim_signing)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001287 return prelim_signing
Tao Baofe5b69a2018-03-02 09:47:43 -08001288
Tao Baod2ce2ed2018-03-16 12:59:42 -07001289 def FinalizeAllPropertyFiles(prelim_signing, needed_property_files):
1290 with zipfile.ZipFile(prelim_signing) as prelim_signing_zip:
1291 for property_files in needed_property_files:
1292 metadata[property_files.name] = property_files.Finalize(
1293 prelim_signing_zip, len(metadata[property_files.name]))
1294
1295 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP
1296 # entries, as well as padding the entry headers. We do a preliminary signing
1297 # (with an incomplete metadata entry) to allow that to happen. Then compute
1298 # the ZIP entry offsets, write back the final metadata and do the final
1299 # signing.
1300 prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files)
1301 try:
1302 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
1303 except PropertyFiles.InsufficientSpaceException:
1304 # Even with the preliminary signing, the entry orders may change
1305 # dramatically, which leads to insufficiently reserved space during the
1306 # first call to ComputeAllPropertyFiles(). In that case, we redo all the
1307 # preliminary signing works, based on the already ordered ZIP entries, to
1308 # address the issue.
1309 prelim_signing = ComputeAllPropertyFiles(
1310 prelim_signing, needed_property_files)
1311 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
Tao Baofe5b69a2018-03-02 09:47:43 -08001312
1313 # Replace the METADATA entry.
1314 common.ZipDelete(prelim_signing, METADATA_NAME)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001315 output_zip = zipfile.ZipFile(prelim_signing, 'a')
Tao Baofe5b69a2018-03-02 09:47:43 -08001316 WriteMetadata(metadata, output_zip)
1317 common.ZipClose(output_zip)
1318
1319 # Re-sign the package after updating the metadata entry.
Tao Bao491d7e22018-02-21 13:17:22 -08001320 if OPTIONS.no_signing:
1321 output_file = prelim_signing
1322 else:
1323 SignOutput(prelim_signing, output_file)
Tao Baofe5b69a2018-03-02 09:47:43 -08001324
1325 # Reopen the final signed zip to double check the streaming metadata.
Tao Baod2ce2ed2018-03-16 12:59:42 -07001326 with zipfile.ZipFile(output_file) as output_zip:
Tao Baod3fc38a2018-03-08 16:09:01 -08001327 for property_files in needed_property_files:
1328 property_files.Verify(output_zip, metadata[property_files.name].strip())
Tao Baofe5b69a2018-03-02 09:47:43 -08001329
xunchang1cfe2512019-02-19 14:14:48 -08001330 # If requested, dump the metadata to a separate file.
1331 output_metadata_path = OPTIONS.output_metadata_path
1332 if output_metadata_path:
1333 WriteMetadata(metadata, output_metadata_path)
1334
Tao Baofe5b69a2018-03-02 09:47:43 -08001335
Tao Bao491d7e22018-02-21 13:17:22 -08001336def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
Tao Bao1c320f82019-10-04 23:25:12 -07001337 target_info = common.BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1338 source_info = common.BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -08001339
Tao Bao481bab82017-12-21 11:23:09 -08001340 target_api_version = target_info["recovery_api_version"]
1341 source_api_version = source_info["recovery_api_version"]
1342 if source_api_version == 0:
Tao Bao32fcdab2018-10-12 10:30:39 -07001343 logger.warning(
1344 "Generating edify script for a source that can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001345
Tao Bao481bab82017-12-21 11:23:09 -08001346 script = edify_generator.EdifyGenerator(
1347 source_api_version, target_info, fstab=source_info["fstab"])
1348
1349 if target_info.oem_props or source_info.oem_props:
1350 if not OPTIONS.oem_no_mount:
1351 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -07001352
Tao Baodf3a48b2018-01-10 16:30:43 -08001353 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -08001354
Tao Bao491d7e22018-02-21 13:17:22 -08001355 if not OPTIONS.no_signing:
1356 staging_file = common.MakeTempFile(suffix='.zip')
1357 else:
1358 staging_file = output_file
1359
1360 output_zip = zipfile.ZipFile(
1361 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
1362
Geremy Condra36bd3652014-02-06 19:45:10 -08001363 device_specific = common.DeviceSpecificParams(
1364 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001365 source_version=source_api_version,
Yifan Hong8a66a712019-04-04 15:37:57 -07001366 source_tmp=OPTIONS.source_tmp,
Geremy Condra36bd3652014-02-06 19:45:10 -08001367 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001368 target_version=target_api_version,
Yifan Hong8a66a712019-04-04 15:37:57 -07001369 target_tmp=OPTIONS.target_tmp,
Geremy Condra36bd3652014-02-06 19:45:10 -08001370 output_zip=output_zip,
1371 script=script,
1372 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -08001373 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001374
Geremy Condra36bd3652014-02-06 19:45:10 -08001375 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001376 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001377 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001378 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001379 updating_boot = (not OPTIONS.two_step and
1380 (source_boot.data != target_boot.data))
1381
Geremy Condra36bd3652014-02-06 19:45:10 -08001382 target_recovery = common.GetBootableImage(
1383 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -08001384
Tianjie Xuf67dd802019-05-20 17:50:36 -07001385 block_diff_dict = GetBlockDifferences(target_zip=target_zip,
1386 source_zip=source_zip,
1387 target_info=target_info,
1388 source_info=source_info,
1389 device_specific=device_specific)
Geremy Condra36bd3652014-02-06 19:45:10 -08001390
Yifan Hong9276cf02019-08-21 16:37:04 -07001391 CheckVintfIfTrebleEnabled(OPTIONS.target_tmp, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001392
Tao Bao481bab82017-12-21 11:23:09 -08001393 # Assertions (e.g. device properties check).
1394 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -08001395 device_specific.IncrementalOTA_Assertions()
1396
1397 # Two-step incremental package strategy (in chronological order,
1398 # which is *not* the order in which the generated script has
1399 # things):
1400 #
1401 # if stage is not "2/3" or "3/3":
1402 # do verification on current system
1403 # write recovery image to boot partition
1404 # set stage to "2/3"
1405 # reboot to boot partition and restart recovery
1406 # else if stage is "2/3":
1407 # write recovery image to recovery partition
1408 # set stage to "3/3"
1409 # reboot to recovery partition and restart recovery
1410 # else:
1411 # (stage must be "3/3")
1412 # perform update:
1413 # patch system files, etc.
1414 # force full install of new boot image
1415 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001416 # complete script normally
1417 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001418
1419 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001420 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001421 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001422 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001423 assert fs.fs_type.upper() == "EMMC", \
1424 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001425 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001426 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1427 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001428if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001429""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001430
1431 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1432 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001433 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001434 script.WriteRawImage("/recovery", "recovery.img")
1435 script.AppendExtra("""
1436set_stage("%(bcb_dev)s", "3/3");
1437reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001438else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001439""" % bcb_dev)
1440
Tao Baod42e97e2016-11-30 12:11:57 -08001441 # Stage 1/3: (a) Verify the current system.
1442 script.Comment("Stage 1/3")
1443
Tao Bao6c55a8a2015-04-08 15:30:27 -07001444 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001445 script.Print("Source: {}".format(source_info.fingerprint))
1446 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001447
Geremy Condra36bd3652014-02-06 19:45:10 -08001448 script.Print("Verifying current system...")
1449
1450 device_specific.IncrementalOTA_VerifyBegin()
1451
Tao Bao481bab82017-12-21 11:23:09 -08001452 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001453
Tao Baod8d14be2016-02-04 14:26:02 -08001454 # Check the required cache size (i.e. stashed blocks).
Tianjie Xuf67dd802019-05-20 17:50:36 -07001455 required_cache_sizes = [diff.required_cache for diff in
1456 block_diff_dict.values()]
Geremy Condra36bd3652014-02-06 19:45:10 -08001457 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -08001458 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001459 d = common.Difference(target_boot, source_boot)
1460 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001461 if d is None:
1462 include_full_boot = True
1463 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1464 else:
1465 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001466
Tao Bao32fcdab2018-10-12 10:30:39 -07001467 logger.info(
1468 "boot target: %d source: %d diff: %d", target_boot.size,
1469 source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -08001470
Tao Bao51216552018-08-26 11:53:15 -07001471 common.ZipWriteStr(output_zip, "boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001472
Tao Bao51216552018-08-26 11:53:15 -07001473 script.PatchPartitionCheck(
1474 "{}:{}:{}:{}".format(
1475 boot_type, boot_device, target_boot.size, target_boot.sha1),
1476 "{}:{}:{}:{}".format(
1477 boot_type, boot_device, source_boot.size, source_boot.sha1))
1478
Tianjie Xuf67dd802019-05-20 17:50:36 -07001479 required_cache_sizes.append(target_boot.size)
Tao Baod8d14be2016-02-04 14:26:02 -08001480
Tianjie Xuf67dd802019-05-20 17:50:36 -07001481 if required_cache_sizes:
1482 script.CacheFreeSpaceCheck(max(required_cache_sizes))
1483
1484 # Verify the existing partitions.
1485 for diff in block_diff_dict.values():
1486 diff.WriteVerifyScript(script, touched_blocks_only=True)
Geremy Condra36bd3652014-02-06 19:45:10 -08001487
1488 device_specific.IncrementalOTA_VerifyEnd()
1489
1490 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001491 # Stage 1/3: (b) Write recovery image to /boot.
1492 _WriteRecoveryImageToBoot(script, output_zip)
1493
Geremy Condra36bd3652014-02-06 19:45:10 -08001494 script.AppendExtra("""
1495set_stage("%(bcb_dev)s", "2/3");
1496reboot_now("%(bcb_dev)s", "");
1497else
1498""" % bcb_dev)
1499
Tao Baod42e97e2016-11-30 12:11:57 -08001500 # Stage 3/3: Make changes.
1501 script.Comment("Stage 3/3")
1502
Geremy Condra36bd3652014-02-06 19:45:10 -08001503 script.Comment("---- start making changes here ----")
1504
1505 device_specific.IncrementalOTA_InstallBegin()
1506
Tianjie Xuf67dd802019-05-20 17:50:36 -07001507 progress_dict = {partition: 0.1 for partition in block_diff_dict}
1508 progress_dict["system"] = 1 - len(block_diff_dict) * 0.1
Yifan Hong10c530d2018-12-27 17:34:18 -08001509
1510 if OPTIONS.source_info_dict.get("use_dynamic_partitions") == "true":
1511 if OPTIONS.target_info_dict.get("use_dynamic_partitions") != "true":
1512 raise RuntimeError(
1513 "can't generate incremental that disables dynamic partitions")
1514 dynamic_partitions_diff = common.DynamicPartitionsDifference(
1515 info_dict=OPTIONS.target_info_dict,
1516 source_info_dict=OPTIONS.source_info_dict,
Tianjie Xuf67dd802019-05-20 17:50:36 -07001517 block_diffs=block_diff_dict.values(),
Yifan Hong10c530d2018-12-27 17:34:18 -08001518 progress_dict=progress_dict)
1519 dynamic_partitions_diff.WriteScript(
1520 script, output_zip, write_verify_script=OPTIONS.verify)
1521 else:
Tianjie Xuf67dd802019-05-20 17:50:36 -07001522 for block_diff in block_diff_dict.values():
Yifan Hong10c530d2018-12-27 17:34:18 -08001523 block_diff.WriteScript(script, output_zip,
1524 progress=progress_dict.get(block_diff.partition),
1525 write_verify_script=OPTIONS.verify)
Geremy Condra36bd3652014-02-06 19:45:10 -08001526
1527 if OPTIONS.two_step:
1528 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1529 script.WriteRawImage("/boot", "boot.img")
Tao Bao32fcdab2018-10-12 10:30:39 -07001530 logger.info("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001531
1532 if not OPTIONS.two_step:
1533 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001534 if include_full_boot:
Tao Bao32fcdab2018-10-12 10:30:39 -07001535 logger.info("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001536 script.Print("Installing boot image...")
1537 script.WriteRawImage("/boot", "boot.img")
1538 else:
1539 # Produce the boot image by applying a patch to the current
1540 # contents of the boot partition, and write it back to the
1541 # partition.
Tao Bao32fcdab2018-10-12 10:30:39 -07001542 logger.info("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001543 script.Print("Patching boot image...")
1544 script.ShowProgress(0.1, 10)
Tao Bao51216552018-08-26 11:53:15 -07001545 script.PatchPartition(
1546 '{}:{}:{}:{}'.format(
1547 boot_type, boot_device, target_boot.size, target_boot.sha1),
1548 '{}:{}:{}:{}'.format(
1549 boot_type, boot_device, source_boot.size, source_boot.sha1),
1550 'boot.img.p')
Geremy Condra36bd3652014-02-06 19:45:10 -08001551 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001552 logger.info("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001553
1554 # Do device-specific installation (eg, write radio image).
1555 device_specific.IncrementalOTA_InstallEnd()
1556
1557 if OPTIONS.extra_script is not None:
1558 script.AppendExtra(OPTIONS.extra_script)
1559
Doug Zongker922206e2014-03-04 13:16:24 -08001560 if OPTIONS.wipe_user_data:
1561 script.Print("Erasing user data...")
1562 script.FormatPartition("/data")
1563
Geremy Condra36bd3652014-02-06 19:45:10 -08001564 if OPTIONS.two_step:
1565 script.AppendExtra("""
1566set_stage("%(bcb_dev)s", "");
1567endif;
1568endif;
1569""" % bcb_dev)
1570
1571 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001572 # For downgrade OTAs, we prefer to use the update-binary in the source
1573 # build that is actually newer than the one in the target build.
1574 if OPTIONS.downgrade:
1575 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1576 else:
1577 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001578 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001579
1580 # We haven't written the metadata entry yet, which will be handled in
1581 # FinalizeMetadata().
1582 common.ZipClose(output_zip)
1583
1584 # Sign the generated zip package unless no_signing is specified.
1585 needed_property_files = (
1586 NonAbOtaPropertyFiles(),
1587 )
1588 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Geremy Condra36bd3652014-02-06 19:45:10 -08001589
Doug Zongker32b527d2014-03-04 10:03:02 -08001590
Tao Bao15a146a2018-02-21 16:06:59 -08001591def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
Tao Baof7140c02018-01-30 17:09:24 -08001592 """Returns a target-files.zip file for generating secondary payload.
1593
1594 Although the original target-files.zip already contains secondary slot
1595 images (i.e. IMAGES/system_other.img), we need to rename the files to the
1596 ones without _other suffix. Note that we cannot instead modify the names in
1597 META/ab_partitions.txt, because there are no matching partitions on device.
1598
1599 For the partitions that don't have secondary images, the ones for primary
1600 slot will be used. This is to ensure that we always have valid boot, vbmeta,
1601 bootloader images in the inactive slot.
1602
1603 Args:
1604 input_file: The input target-files.zip file.
Tao Bao15a146a2018-02-21 16:06:59 -08001605 skip_postinstall: Whether to skip copying the postinstall config file.
Tao Baof7140c02018-01-30 17:09:24 -08001606
1607 Returns:
1608 The filename of the target-files.zip for generating secondary payload.
1609 """
Tianjie Xu1c808002019-09-11 00:29:26 -07001610
1611 def GetInfoForSecondaryImages(info_file):
1612 """Updates info file for secondary payload generation.
1613
1614 Scan each line in the info file, and remove the unwanted partitions from
1615 the dynamic partition list in the related properties. e.g.
1616 "super_google_dynamic_partitions_partition_list=system vendor product"
1617 will become "super_google_dynamic_partitions_partition_list=system".
1618
1619 Args:
1620 info_file: The input info file. e.g. misc_info.txt.
1621
1622 Returns:
1623 A string of the updated info content.
1624 """
1625
1626 output_list = []
1627 with open(info_file) as f:
1628 lines = f.read().splitlines()
1629
1630 # The suffix in partition_list variables that follows the name of the
1631 # partition group.
1632 LIST_SUFFIX = 'partition_list'
1633 for line in lines:
1634 if line.startswith('#') or '=' not in line:
1635 output_list.append(line)
1636 continue
1637 key, value = line.strip().split('=', 1)
1638 if key == 'dynamic_partition_list' or key.endswith(LIST_SUFFIX):
1639 partitions = value.split()
1640 partitions = [partition for partition in partitions if partition
Tao Bao3e759462019-09-17 22:43:11 -07001641 not in SECONDARY_PAYLOAD_SKIPPED_IMAGES]
Tianjie Xu1c808002019-09-11 00:29:26 -07001642 output_list.append('{}={}'.format(key, ' '.join(partitions)))
Yifan Hongfe073432019-11-01 12:28:31 -07001643 elif key == 'virtual_ab' or key == "virtual_ab_retrofit":
1644 # Remove virtual_ab flag from secondary payload so that OTA client
1645 # don't use snapshots for secondary update
1646 pass
Tianjie Xu1c808002019-09-11 00:29:26 -07001647 else:
1648 output_list.append(line)
1649 return '\n'.join(output_list)
1650
Tao Baof7140c02018-01-30 17:09:24 -08001651 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1652 target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
1653
Tao Baodba59ee2018-01-09 13:21:02 -08001654 with zipfile.ZipFile(input_file, 'r') as input_zip:
1655 infolist = input_zip.infolist()
Tao Bao12489802018-07-12 14:47:38 -07001656
Tao Bao0ff15de2019-03-20 11:26:06 -07001657 input_tmp = common.UnzipTemp(input_file, UNZIP_PATTERN)
Tao Baodba59ee2018-01-09 13:21:02 -08001658 for info in infolist:
Tao Baof7140c02018-01-30 17:09:24 -08001659 unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
1660 if info.filename == 'IMAGES/system_other.img':
1661 common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
1662
1663 # Primary images and friends need to be skipped explicitly.
1664 elif info.filename in ('IMAGES/system.img',
1665 'IMAGES/system.map'):
1666 pass
Tao Bao3e759462019-09-17 22:43:11 -07001667
1668 # Copy images that are not in SECONDARY_PAYLOAD_SKIPPED_IMAGES.
1669 elif info.filename.startswith(('IMAGES/', 'RADIO/')):
1670 image_name = os.path.basename(info.filename)
1671 if image_name not in ['{}.img'.format(partition) for partition in
1672 SECONDARY_PAYLOAD_SKIPPED_IMAGES]:
1673 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
Tao Baof7140c02018-01-30 17:09:24 -08001674
Tao Bao15a146a2018-02-21 16:06:59 -08001675 # Skip copying the postinstall config if requested.
1676 elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
1677 pass
1678
Tianjie Xu1c808002019-09-11 00:29:26 -07001679 elif info.filename.startswith('META/'):
1680 # Remove the unnecessary partitions for secondary images from the
1681 # ab_partitions file.
1682 if info.filename == AB_PARTITIONS:
1683 with open(unzipped_file) as f:
1684 partition_list = f.read().splitlines()
1685 partition_list = [partition for partition in partition_list if partition
Tao Bao3e759462019-09-17 22:43:11 -07001686 and partition not in SECONDARY_PAYLOAD_SKIPPED_IMAGES]
Tianjie Xu1c808002019-09-11 00:29:26 -07001687 common.ZipWriteStr(target_zip, info.filename, '\n'.join(partition_list))
1688 # Remove the unnecessary partitions from the dynamic partitions list.
1689 elif (info.filename == 'META/misc_info.txt' or
1690 info.filename == DYNAMIC_PARTITION_INFO):
1691 modified_info = GetInfoForSecondaryImages(unzipped_file)
1692 common.ZipWriteStr(target_zip, info.filename, modified_info)
1693 else:
1694 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
Tao Baof7140c02018-01-30 17:09:24 -08001695
Tao Baof7140c02018-01-30 17:09:24 -08001696 common.ZipClose(target_zip)
1697
1698 return target_file
1699
1700
Tao Bao15a146a2018-02-21 16:06:59 -08001701def GetTargetFilesZipWithoutPostinstallConfig(input_file):
1702 """Returns a target-files.zip that's not containing postinstall_config.txt.
1703
1704 This allows brillo_update_payload script to skip writing all the postinstall
1705 hooks in the generated payload. The input target-files.zip file will be
1706 duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
1707 contain the postinstall_config.txt entry, the input file will be returned.
1708
1709 Args:
1710 input_file: The input target-files.zip filename.
1711
1712 Returns:
1713 The filename of target-files.zip that doesn't contain postinstall config.
1714 """
1715 # We should only make a copy if postinstall_config entry exists.
1716 with zipfile.ZipFile(input_file, 'r') as input_zip:
1717 if POSTINSTALL_CONFIG not in input_zip.namelist():
1718 return input_file
1719
1720 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1721 shutil.copyfile(input_file, target_file)
1722 common.ZipDelete(target_file, POSTINSTALL_CONFIG)
1723 return target_file
1724
1725
Yifan Hong50e79542018-11-08 17:44:12 -08001726def GetTargetFilesZipForRetrofitDynamicPartitions(input_file,
Yifan Hongb433eba2019-03-06 12:42:53 -08001727 super_block_devices,
1728 dynamic_partition_list):
Yifan Hong50e79542018-11-08 17:44:12 -08001729 """Returns a target-files.zip for retrofitting dynamic partitions.
1730
1731 This allows brillo_update_payload to generate an OTA based on the exact
1732 bits on the block devices. Postinstall is disabled.
1733
1734 Args:
1735 input_file: The input target-files.zip filename.
1736 super_block_devices: The list of super block devices
Yifan Hongb433eba2019-03-06 12:42:53 -08001737 dynamic_partition_list: The list of dynamic partitions
Yifan Hong50e79542018-11-08 17:44:12 -08001738
1739 Returns:
1740 The filename of target-files.zip with *.img replaced with super_*.img for
1741 each block device in super_block_devices.
1742 """
1743 assert super_block_devices, "No super_block_devices are specified."
1744
1745 replace = {'OTA/super_{}.img'.format(dev): 'IMAGES/{}.img'.format(dev)
Tao Bao03fecb62018-11-28 10:59:23 -08001746 for dev in super_block_devices}
Yifan Hong50e79542018-11-08 17:44:12 -08001747
1748 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1749 shutil.copyfile(input_file, target_file)
1750
Tao Baoa3705452019-06-24 15:33:41 -07001751 with zipfile.ZipFile(input_file) as input_zip:
Yifan Hong50e79542018-11-08 17:44:12 -08001752 namelist = input_zip.namelist()
1753
Yifan Hongb433eba2019-03-06 12:42:53 -08001754 input_tmp = common.UnzipTemp(input_file, RETROFIT_DAP_UNZIP_PATTERN)
1755
1756 # Remove partitions from META/ab_partitions.txt that is in
1757 # dynamic_partition_list but not in super_block_devices so that
1758 # brillo_update_payload won't generate update for those logical partitions.
1759 ab_partitions_file = os.path.join(input_tmp, *AB_PARTITIONS.split('/'))
1760 with open(ab_partitions_file) as f:
1761 ab_partitions_lines = f.readlines()
1762 ab_partitions = [line.strip() for line in ab_partitions_lines]
1763 # Assert that all super_block_devices are in ab_partitions
1764 super_device_not_updated = [partition for partition in super_block_devices
1765 if partition not in ab_partitions]
1766 assert not super_device_not_updated, \
1767 "{} is in super_block_devices but not in {}".format(
1768 super_device_not_updated, AB_PARTITIONS)
1769 # ab_partitions -= (dynamic_partition_list - super_block_devices)
1770 new_ab_partitions = common.MakeTempFile(prefix="ab_partitions", suffix=".txt")
1771 with open(new_ab_partitions, 'w') as f:
1772 for partition in ab_partitions:
1773 if (partition in dynamic_partition_list and
1774 partition not in super_block_devices):
Tao Bao59cf0c52019-06-25 10:04:24 -07001775 logger.info("Dropping %s from ab_partitions.txt", partition)
1776 continue
Yifan Hongb433eba2019-03-06 12:42:53 -08001777 f.write(partition + "\n")
1778 to_delete = [AB_PARTITIONS]
1779
Yifan Hong50e79542018-11-08 17:44:12 -08001780 # Always skip postinstall for a retrofit update.
Yifan Hongb433eba2019-03-06 12:42:53 -08001781 to_delete += [POSTINSTALL_CONFIG]
Yifan Hong50e79542018-11-08 17:44:12 -08001782
1783 # Delete dynamic_partitions_info.txt so that brillo_update_payload thinks this
1784 # is a regular update on devices without dynamic partitions support.
1785 to_delete += [DYNAMIC_PARTITION_INFO]
1786
Tao Bao03fecb62018-11-28 10:59:23 -08001787 # Remove the existing partition images as well as the map files.
Tao Bao59cf0c52019-06-25 10:04:24 -07001788 to_delete += list(replace.values())
Tao Bao03fecb62018-11-28 10:59:23 -08001789 to_delete += ['IMAGES/{}.map'.format(dev) for dev in super_block_devices]
Yifan Hong50e79542018-11-08 17:44:12 -08001790
1791 common.ZipDelete(target_file, to_delete)
1792
Yifan Hong50e79542018-11-08 17:44:12 -08001793 target_zip = zipfile.ZipFile(target_file, 'a', allowZip64=True)
1794
1795 # Write super_{foo}.img as {foo}.img.
1796 for src, dst in replace.items():
1797 assert src in namelist, \
Tao Bao59cf0c52019-06-25 10:04:24 -07001798 'Missing {} in {}; {} cannot be written'.format(src, input_file, dst)
Yifan Hong50e79542018-11-08 17:44:12 -08001799 unzipped_file = os.path.join(input_tmp, *src.split('/'))
1800 common.ZipWrite(target_zip, unzipped_file, arcname=dst)
1801
Yifan Hongb433eba2019-03-06 12:42:53 -08001802 # Write new ab_partitions.txt file
1803 common.ZipWrite(target_zip, new_ab_partitions, arcname=AB_PARTITIONS)
1804
Yifan Hong50e79542018-11-08 17:44:12 -08001805 common.ZipClose(target_zip)
1806
1807 return target_file
1808
1809
Tao Baof0c4aa22018-04-30 20:29:30 -07001810def GenerateAbOtaPackage(target_file, output_file, source_file=None):
Tao Baofe5b69a2018-03-02 09:47:43 -08001811 """Generates an Android OTA package that has A/B update payload."""
Tao Baodea0f8b2016-06-20 17:55:06 -07001812 # Stage the output zip package for package signing.
Tao Bao491d7e22018-02-21 13:17:22 -08001813 if not OPTIONS.no_signing:
1814 staging_file = common.MakeTempFile(suffix='.zip')
1815 else:
1816 staging_file = output_file
Tao Baoa652c002018-03-01 19:31:38 -08001817 output_zip = zipfile.ZipFile(staging_file, "w",
Tao Baoc098e9e2016-01-07 13:03:56 -08001818 compression=zipfile.ZIP_DEFLATED)
1819
Tao Bao481bab82017-12-21 11:23:09 -08001820 if source_file is not None:
Tao Bao1c320f82019-10-04 23:25:12 -07001821 target_info = common.BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1822 source_info = common.BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Tao Bao481bab82017-12-21 11:23:09 -08001823 else:
Tao Bao1c320f82019-10-04 23:25:12 -07001824 target_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Tao Bao481bab82017-12-21 11:23:09 -08001825 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001826
Tao Bao481bab82017-12-21 11:23:09 -08001827 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001828 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001829
Yifan Hong50e79542018-11-08 17:44:12 -08001830 if OPTIONS.retrofit_dynamic_partitions:
1831 target_file = GetTargetFilesZipForRetrofitDynamicPartitions(
Yifan Hongb433eba2019-03-06 12:42:53 -08001832 target_file, target_info.get("super_block_devices").strip().split(),
1833 target_info.get("dynamic_partition_list").strip().split())
Yifan Hong50e79542018-11-08 17:44:12 -08001834 elif OPTIONS.skip_postinstall:
Tao Bao15a146a2018-02-21 16:06:59 -08001835 target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
1836
Tao Bao40b18822018-01-30 18:19:04 -08001837 # Generate payload.
1838 payload = Payload()
1839
1840 # Enforce a max timestamp this payload can be applied on top of.
Tao Baoff1b86e2017-10-03 14:17:57 -07001841 if OPTIONS.downgrade:
Tao Bao2a12ed72018-01-22 11:35:00 -08001842 max_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baoff1b86e2017-10-03 14:17:57 -07001843 else:
1844 max_timestamp = metadata["post-timestamp"]
Tao Bao40b18822018-01-30 18:19:04 -08001845 additional_args = ["--max_timestamp", max_timestamp]
Tao Baoc098e9e2016-01-07 13:03:56 -08001846
Tao Bao40b18822018-01-30 18:19:04 -08001847 payload.Generate(target_file, source_file, additional_args)
Tao Baoc098e9e2016-01-07 13:03:56 -08001848
Tao Bao40b18822018-01-30 18:19:04 -08001849 # Sign the payload.
Tao Baof7140c02018-01-30 17:09:24 -08001850 payload_signer = PayloadSigner()
1851 payload.Sign(payload_signer)
Tao Baoc098e9e2016-01-07 13:03:56 -08001852
Tao Bao40b18822018-01-30 18:19:04 -08001853 # Write the payload into output zip.
1854 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001855
Tao Baof7140c02018-01-30 17:09:24 -08001856 # Generate and include the secondary payload that installs secondary images
1857 # (e.g. system_other.img).
1858 if OPTIONS.include_secondary:
1859 # We always include a full payload for the secondary slot, even when
1860 # building an incremental OTA. See the comments for "--include_secondary".
Tao Bao15a146a2018-02-21 16:06:59 -08001861 secondary_target_file = GetTargetFilesZipForSecondaryImages(
1862 target_file, OPTIONS.skip_postinstall)
Tao Bao667ff572018-02-10 00:02:40 -08001863 secondary_payload = Payload(secondary=True)
Tao Baodb1fe412018-02-09 23:15:05 -08001864 secondary_payload.Generate(secondary_target_file,
1865 additional_args=additional_args)
Tao Baof7140c02018-01-30 17:09:24 -08001866 secondary_payload.Sign(payload_signer)
Tao Bao667ff572018-02-10 00:02:40 -08001867 secondary_payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08001868
Tianjie Xucfa86222016-03-07 16:31:19 -08001869 # If dm-verity is supported for the device, copy contents of care_map
1870 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001871 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08001872 if (target_info.get("verity") == "true" or
1873 target_info.get("avb_enable") == "true"):
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001874 care_map_list = [x for x in ["care_map.pb", "care_map.txt"] if
1875 "META/" + x in target_zip.namelist()]
1876
1877 # Adds care_map if either the protobuf format or the plain text one exists.
1878 if care_map_list:
1879 care_map_name = care_map_list[0]
1880 care_map_data = target_zip.read("META/" + care_map_name)
1881 # In order to support streaming, care_map needs to be packed as
Tao Bao40b18822018-01-30 18:19:04 -08001882 # ZIP_STORED.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001883 common.ZipWriteStr(output_zip, care_map_name, care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08001884 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001885 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001886 logger.warning("Cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07001887
Tao Bao21803d32017-04-19 10:16:09 -07001888 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001889
Yifan Hong9276cf02019-08-21 16:37:04 -07001890 CheckVintfIfTrebleEnabled(target_file, target_info)
1891
Tao Baofe5b69a2018-03-02 09:47:43 -08001892 # We haven't written the metadata entry yet, which will be handled in
1893 # FinalizeMetadata().
Tao Baoc96316c2017-01-24 22:10:49 -08001894 common.ZipClose(output_zip)
1895
Tao Bao85f16982018-03-08 16:28:33 -08001896 # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers
1897 # all the info of the latter. However, system updaters and OTA servers need to
1898 # take time to switch to the new flag. We keep both of the flags for
1899 # P-timeframe, and will remove StreamingPropertyFiles in later release.
Tao Baod3fc38a2018-03-08 16:09:01 -08001900 needed_property_files = (
Tao Bao85f16982018-03-08 16:28:33 -08001901 AbOtaPropertyFiles(),
Tao Baod3fc38a2018-03-08 16:09:01 -08001902 StreamingPropertyFiles(),
1903 )
1904 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Tao Baoc96316c2017-01-24 22:10:49 -08001905
Tao Baoc098e9e2016-01-07 13:03:56 -08001906
Tao Baof0c4aa22018-04-30 20:29:30 -07001907def GenerateNonAbOtaPackage(target_file, output_file, source_file=None):
1908 """Generates a non-A/B OTA package."""
1909 # Sanity check the loaded info dicts first.
1910 if OPTIONS.info_dict.get("no_recovery") == "true":
1911 raise common.ExternalError(
1912 "--- target build has specified no recovery ---")
1913
1914 # Non-A/B OTAs rely on /cache partition to store temporary files.
1915 cache_size = OPTIONS.info_dict.get("cache_size")
1916 if cache_size is None:
1917 logger.warning("--- can't determine the cache partition size ---")
1918 OPTIONS.cache_size = cache_size
1919
1920 if OPTIONS.extra_script is not None:
1921 with open(OPTIONS.extra_script) as fp:
1922 OPTIONS.extra_script = fp.read()
1923
1924 if OPTIONS.extracted_input is not None:
1925 OPTIONS.input_tmp = OPTIONS.extracted_input
1926 else:
1927 logger.info("unzipping target target-files...")
1928 OPTIONS.input_tmp = common.UnzipTemp(target_file, UNZIP_PATTERN)
1929 OPTIONS.target_tmp = OPTIONS.input_tmp
1930
1931 # If the caller explicitly specified the device-specific extensions path via
1932 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
1933 # is present in the target target_files. Otherwise, take the path of the file
1934 # from 'tool_extensions' in the info dict and look for that in the local
1935 # filesystem, relative to the current directory.
1936 if OPTIONS.device_specific is None:
1937 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1938 if os.path.exists(from_input):
1939 logger.info("(using device-specific extensions from target_files)")
1940 OPTIONS.device_specific = from_input
1941 else:
1942 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
1943
1944 if OPTIONS.device_specific is not None:
1945 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
1946
1947 # Generate a full OTA.
1948 if source_file is None:
1949 with zipfile.ZipFile(target_file) as input_zip:
1950 WriteFullOTAPackage(
1951 input_zip,
1952 output_file)
1953
1954 # Generate an incremental OTA.
1955 else:
1956 logger.info("unzipping source target-files...")
1957 OPTIONS.source_tmp = common.UnzipTemp(
1958 OPTIONS.incremental_source, UNZIP_PATTERN)
1959 with zipfile.ZipFile(target_file) as input_zip, \
1960 zipfile.ZipFile(source_file) as source_zip:
1961 WriteBlockIncrementalOTAPackage(
1962 input_zip,
1963 source_zip,
1964 output_file)
1965
1966
Tianjie Xu9afb2212020-05-10 21:48:15 +00001967def CalculateRuntimeFingerprints():
1968 """Returns a set of runtime fingerprints based on the boot variables."""
1969
1970 build_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
1971 fingerprints = {build_info.fingerprint}
1972
1973 if not OPTIONS.boot_variable_values:
1974 return fingerprints
1975
1976 # Calculate all possible combinations of the values for the boot variables.
1977 keys = OPTIONS.boot_variable_values.keys()
1978 value_list = OPTIONS.boot_variable_values.values()
1979 combinations = [dict(zip(keys, values))
1980 for values in itertools.product(*value_list)]
1981 for placeholder_values in combinations:
1982 # Reload the info_dict as some build properties may change their values
1983 # based on the value of ro.boot* properties.
1984 info_dict = copy.deepcopy(OPTIONS.info_dict)
1985 for partition in common.PARTITIONS_WITH_CARE_MAP:
1986 partition_prop_key = "{}.build.prop".format(partition)
1987 old_props = info_dict[partition_prop_key]
1988 info_dict[partition_prop_key] = common.PartitionBuildProps.FromInputFile(
1989 old_props.input_file, partition, placeholder_values)
1990 info_dict["build.prop"] = info_dict["system.build.prop"]
1991
1992 build_info = common.BuildInfo(info_dict, OPTIONS.oem_dicts)
1993 fingerprints.add(build_info.fingerprint)
1994 return fingerprints
1995
1996
Doug Zongkereef39442009-04-02 12:14:19 -07001997def main(argv):
1998
1999 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07002000 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07002001 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07002002 elif o in ("-i", "--incremental_from"):
2003 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07002004 elif o == "--full_radio":
2005 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07002006 elif o == "--full_bootloader":
2007 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08002008 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002009 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08002010 elif o == "--downgrade":
2011 OPTIONS.downgrade = True
2012 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08002013 elif o == "--override_timestamp":
Tao Baofaa8e0b2018-04-12 14:31:43 -07002014 OPTIONS.downgrade = True
Michael Runge6e836112014-04-15 17:40:21 -07002015 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08002016 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08002017 elif o == "--oem_no_mount":
2018 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07002019 elif o in ("-e", "--extra_script"):
2020 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02002021 elif o in ("-t", "--worker_threads"):
2022 if a.isdigit():
2023 OPTIONS.worker_threads = int(a)
2024 else:
2025 raise ValueError("Cannot parse value %r for option %r - only "
2026 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002027 elif o in ("-2", "--two_step"):
2028 OPTIONS.two_step = True
Tao Baof7140c02018-01-30 17:09:24 -08002029 elif o == "--include_secondary":
2030 OPTIONS.include_secondary = True
Doug Zongker26e66192014-02-20 13:22:07 -08002031 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002032 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07002033 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07002034 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08002035 elif o == "--block":
2036 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002037 elif o in ("-b", "--binary"):
2038 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07002039 elif o == "--stash_threshold":
2040 try:
2041 OPTIONS.stash_threshold = float(a)
2042 except ValueError:
2043 raise ValueError("Cannot parse value %r for option %r - expecting "
2044 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08002045 elif o == "--log_diff":
2046 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002047 elif o == "--payload_signer":
2048 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002049 elif o == "--payload_signer_args":
2050 OPTIONS.payload_signer_args = shlex.split(a)
Tianjie Xu21e6deb2019-10-07 18:01:00 -07002051 elif o == "--payload_signer_maximum_signature_size":
2052 OPTIONS.payload_signer_maximum_signature_size = a
xunchang376cc7c2019-04-08 23:04:58 -07002053 elif o == "--payload_signer_key_size":
Tianjie Xu21e6deb2019-10-07 18:01:00 -07002054 # TODO(Xunchang) remove this option after cleaning up the callers.
2055 logger.warning("The option '--payload_signer_key_size' is deprecated."
2056 " Use '--payload_signer_maximum_signature_size' instead.")
2057 OPTIONS.payload_signer_maximum_signature_size = a
Dan Willemsencea5cd22017-03-21 14:44:27 -07002058 elif o == "--extracted_input_target_files":
2059 OPTIONS.extracted_input = a
Tao Bao15a146a2018-02-21 16:06:59 -08002060 elif o == "--skip_postinstall":
2061 OPTIONS.skip_postinstall = True
Yifan Hong50e79542018-11-08 17:44:12 -08002062 elif o == "--retrofit_dynamic_partitions":
2063 OPTIONS.retrofit_dynamic_partitions = True
xunchangabfa2652019-02-19 16:27:10 -08002064 elif o == "--skip_compatibility_check":
2065 OPTIONS.skip_compatibility_check = True
xunchang1cfe2512019-02-19 14:14:48 -08002066 elif o == "--output_metadata_path":
2067 OPTIONS.output_metadata_path = a
Tianjie Xu1b079832019-08-28 12:19:23 -07002068 elif o == "--disable_fec_computation":
2069 OPTIONS.disable_fec_computation = True
Doug Zongkereef39442009-04-02 12:14:19 -07002070 else:
2071 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002072 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002073
2074 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08002075 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002076 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07002077 "package_key=",
2078 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002079 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002080 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002081 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08002082 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08002083 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07002084 "extra_script=",
2085 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002086 "two_step",
Tao Baof7140c02018-01-30 17:09:24 -08002087 "include_secondary",
Dan Albert8b72aef2015-03-23 19:13:21 -07002088 "no_signing",
2089 "block",
2090 "binary=",
2091 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08002092 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002093 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07002094 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002095 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002096 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002097 "payload_signer_args=",
Tianjie Xu21e6deb2019-10-07 18:01:00 -07002098 "payload_signer_maximum_signature_size=",
xunchang376cc7c2019-04-08 23:04:58 -07002099 "payload_signer_key_size=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07002100 "extracted_input_target_files=",
Tao Bao15a146a2018-02-21 16:06:59 -08002101 "skip_postinstall",
Yifan Hong50e79542018-11-08 17:44:12 -08002102 "retrofit_dynamic_partitions",
xunchangabfa2652019-02-19 16:27:10 -08002103 "skip_compatibility_check",
xunchang1cfe2512019-02-19 14:14:48 -08002104 "output_metadata_path=",
Tianjie Xu1b079832019-08-28 12:19:23 -07002105 "disable_fec_computation",
Dan Albert8b72aef2015-03-23 19:13:21 -07002106 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002107
2108 if len(args) != 2:
2109 common.Usage(__doc__)
2110 sys.exit(1)
2111
Tao Bao32fcdab2018-10-12 10:30:39 -07002112 common.InitLogging()
2113
Tao Bao5d182562016-02-23 11:38:39 -08002114 if OPTIONS.downgrade:
Tao Bao5d182562016-02-23 11:38:39 -08002115 # We should only allow downgrading incrementals (as opposed to full).
2116 # Otherwise the device may go back from arbitrary build with this full
2117 # OTA package.
2118 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002119 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08002120
Tao Bao2db13852018-01-08 22:28:57 -08002121 # Load the build info dicts from the zip directly or the extracted input
2122 # directory. We don't need to unzip the entire target-files zips, because they
2123 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
2124 # When loading the info dicts, we don't need to provide the second parameter
2125 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
2126 # some properties with their actual paths, such as 'selinux_fc',
2127 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07002128 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08002129 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07002130 else:
Tao Bao2db13852018-01-08 22:28:57 -08002131 with zipfile.ZipFile(args[0], 'r') as input_zip:
2132 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08002133
Tao Bao32fcdab2018-10-12 10:30:39 -07002134 logger.info("--- target info ---")
2135 common.DumpInfoDict(OPTIONS.info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002136
2137 # Load the source build dict if applicable.
2138 if OPTIONS.incremental_source is not None:
2139 OPTIONS.target_info_dict = OPTIONS.info_dict
2140 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
2141 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2142
Tao Bao32fcdab2018-10-12 10:30:39 -07002143 logger.info("--- source info ---")
2144 common.DumpInfoDict(OPTIONS.source_info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002145
2146 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08002147 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
2148
Yifan Hong50e79542018-11-08 17:44:12 -08002149 # Assume retrofitting dynamic partitions when base build does not set
Yifan Hong50611032018-11-20 14:27:38 -08002150 # use_dynamic_partitions but target build does.
Yifan Hong50e79542018-11-08 17:44:12 -08002151 if (OPTIONS.source_info_dict and
Yifan Hong50611032018-11-20 14:27:38 -08002152 OPTIONS.source_info_dict.get("use_dynamic_partitions") != "true" and
2153 OPTIONS.target_info_dict.get("use_dynamic_partitions") == "true"):
Yifan Hong50e79542018-11-08 17:44:12 -08002154 if OPTIONS.target_info_dict.get("dynamic_partition_retrofit") != "true":
2155 raise common.ExternalError(
2156 "Expect to generate incremental OTA for retrofitting dynamic "
2157 "partitions, but dynamic_partition_retrofit is not set in target "
2158 "build.")
2159 logger.info("Implicitly generating retrofit incremental OTA.")
2160 OPTIONS.retrofit_dynamic_partitions = True
2161
2162 # Skip postinstall for retrofitting dynamic partitions.
2163 if OPTIONS.retrofit_dynamic_partitions:
2164 OPTIONS.skip_postinstall = True
2165
Tao Baoc098e9e2016-01-07 13:03:56 -08002166 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2167
Christian Oderf63e2cd2017-05-01 22:30:15 +02002168 # Use the default key to sign the package if not specified with package_key.
2169 # package_keys are needed on ab_updates, so always define them if an
2170 # ab_update is getting created.
2171 if not OPTIONS.no_signing or ab_update:
2172 if OPTIONS.package_key is None:
2173 OPTIONS.package_key = OPTIONS.info_dict.get(
2174 "default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -07002175 "build/make/target/product/security/testkey")
Christian Oderf63e2cd2017-05-01 22:30:15 +02002176 # Get signing keys
2177 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
2178
Tao Baoc098e9e2016-01-07 13:03:56 -08002179 if ab_update:
Tao Baof0c4aa22018-04-30 20:29:30 -07002180 GenerateAbOtaPackage(
Tao Baoc098e9e2016-01-07 13:03:56 -08002181 target_file=args[0],
2182 output_file=args[1],
2183 source_file=OPTIONS.incremental_source)
2184
Dan Willemsencea5cd22017-03-21 14:44:27 -07002185 else:
Tao Baof0c4aa22018-04-30 20:29:30 -07002186 GenerateNonAbOtaPackage(
2187 target_file=args[0],
2188 output_file=args[1],
2189 source_file=OPTIONS.incremental_source)
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002190
Tao Baof0c4aa22018-04-30 20:29:30 -07002191 # Post OTA generation works.
2192 if OPTIONS.incremental_source is not None and OPTIONS.log_diff:
2193 logger.info("Generating diff logs...")
2194 logger.info("Unzipping target-files for diffing...")
2195 target_dir = common.UnzipTemp(args[0], TARGET_DIFFING_UNZIP_PATTERN)
2196 source_dir = common.UnzipTemp(
2197 OPTIONS.incremental_source, TARGET_DIFFING_UNZIP_PATTERN)
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002198
Tao Baof0c4aa22018-04-30 20:29:30 -07002199 with open(OPTIONS.log_diff, 'w') as out_file:
2200 import target_files_diff
2201 target_files_diff.recursiveDiff(
2202 '', source_dir, target_dir, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07002203
Tao Bao32fcdab2018-10-12 10:30:39 -07002204 logger.info("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002205
2206
2207if __name__ == '__main__':
2208 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002209 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002210 main(sys.argv[1:])
Tao Bao32fcdab2018-10-12 10:30:39 -07002211 except common.ExternalError:
2212 logger.exception("\n ERROR:\n")
Doug Zongkereef39442009-04-02 12:14:19 -07002213 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002214 finally:
2215 common.Cleanup()