blob: 47ad3d8a142e68fbaa65a295059f682758a025f5 [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',
270 'system_ext', 'vbmeta', 'vbmeta_system', 'vbmeta_vendor', 'vendor']
Tao Bao6b0b2f92017-03-05 11:38:11 -0800271
Tao Bao2dd1c482017-02-03 16:49:39 -0800272
Tao Baofabe0832018-01-17 15:52:28 -0800273class PayloadSigner(object):
274 """A class that wraps the payload signing works.
275
276 When generating a Payload, hashes of the payload and metadata files will be
277 signed with the device key, either by calling an external payload signer or
278 by calling openssl with the package key. This class provides a unified
279 interface, so that callers can just call PayloadSigner.Sign().
280
281 If an external payload signer has been specified (OPTIONS.payload_signer), it
282 calls the signer with the provided args (OPTIONS.payload_signer_args). Note
283 that the signing key should be provided as part of the payload_signer_args.
284 Otherwise without an external signer, it uses the package key
285 (OPTIONS.package_key) and calls openssl for the signing works.
286 """
287
288 def __init__(self):
289 if OPTIONS.payload_signer is None:
290 # Prepare the payload signing key.
291 private_key = OPTIONS.package_key + OPTIONS.private_key_suffix
292 pw = OPTIONS.key_passwords[OPTIONS.package_key]
293
294 cmd = ["openssl", "pkcs8", "-in", private_key, "-inform", "DER"]
295 cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"])
296 signing_key = common.MakeTempFile(prefix="key-", suffix=".key")
297 cmd.extend(["-out", signing_key])
Tao Baobec89c12018-10-15 11:53:28 -0700298 common.RunAndCheckOutput(cmd, verbose=False)
Tao Baofabe0832018-01-17 15:52:28 -0800299
300 self.signer = "openssl"
301 self.signer_args = ["pkeyutl", "-sign", "-inkey", signing_key,
302 "-pkeyopt", "digest:sha256"]
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700303 self.maximum_signature_size = self._GetMaximumSignatureSizeInBytes(
304 signing_key)
Tao Baofabe0832018-01-17 15:52:28 -0800305 else:
306 self.signer = OPTIONS.payload_signer
307 self.signer_args = OPTIONS.payload_signer_args
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700308 if OPTIONS.payload_signer_maximum_signature_size:
309 self.maximum_signature_size = int(
310 OPTIONS.payload_signer_maximum_signature_size)
xunchang376cc7c2019-04-08 23:04:58 -0700311 else:
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700312 # The legacy config uses RSA2048 keys.
313 logger.warning("The maximum signature size for payload signer is not"
314 " set, default to 256 bytes.")
315 self.maximum_signature_size = 256
xunchang376cc7c2019-04-08 23:04:58 -0700316
317 @staticmethod
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700318 def _GetMaximumSignatureSizeInBytes(signing_key):
319 out_signature_size_file = common.MakeTempFile("signature_size")
320 cmd = ["delta_generator", "--out_maximum_signature_size_file={}".format(
321 out_signature_size_file), "--private_key={}".format(signing_key)]
322 common.RunAndCheckOutput(cmd)
323 with open(out_signature_size_file) as f:
324 signature_size = f.read().rstrip()
Luca Stefani88e1a142020-03-27 14:05:12 +0100325 logger.info("%s outputs the maximum signature size: %s", cmd[0],
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700326 signature_size)
327 return int(signature_size)
Tao Baofabe0832018-01-17 15:52:28 -0800328
329 def Sign(self, in_file):
330 """Signs the given input file. Returns the output filename."""
331 out_file = common.MakeTempFile(prefix="signed-", suffix=".bin")
332 cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file]
Tao Bao718faed2019-08-02 13:24:19 -0700333 common.RunAndCheckOutput(cmd)
Tao Baofabe0832018-01-17 15:52:28 -0800334 return out_file
335
336
Tao Bao40b18822018-01-30 18:19:04 -0800337class Payload(object):
338 """Manages the creation and the signing of an A/B OTA Payload."""
339
340 PAYLOAD_BIN = 'payload.bin'
341 PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
Tao Baof7140c02018-01-30 17:09:24 -0800342 SECONDARY_PAYLOAD_BIN = 'secondary/payload.bin'
343 SECONDARY_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt'
Tao Bao40b18822018-01-30 18:19:04 -0800344
Tao Bao667ff572018-02-10 00:02:40 -0800345 def __init__(self, secondary=False):
346 """Initializes a Payload instance.
347
348 Args:
349 secondary: Whether it's generating a secondary payload (default: False).
350 """
Tao Bao40b18822018-01-30 18:19:04 -0800351 self.payload_file = None
352 self.payload_properties = None
Tao Bao667ff572018-02-10 00:02:40 -0800353 self.secondary = secondary
Tao Bao40b18822018-01-30 18:19:04 -0800354
Tao Baof0c4aa22018-04-30 20:29:30 -0700355 def _Run(self, cmd): # pylint: disable=no-self-use
Tao Bao718faed2019-08-02 13:24:19 -0700356 # Don't pipe (buffer) the output if verbose is set. Let
357 # brillo_update_payload write to stdout/stderr directly, so its progress can
358 # be monitored.
359 if OPTIONS.verbose:
360 common.RunAndCheckOutput(cmd, stdout=None, stderr=None)
361 else:
362 common.RunAndCheckOutput(cmd)
363
Tao Bao40b18822018-01-30 18:19:04 -0800364 def Generate(self, target_file, source_file=None, additional_args=None):
365 """Generates a payload from the given target-files zip(s).
366
367 Args:
368 target_file: The filename of the target build target-files zip.
369 source_file: The filename of the source build target-files zip; or None if
370 generating a full OTA.
371 additional_args: A list of additional args that should be passed to
372 brillo_update_payload script; or None.
373 """
374 if additional_args is None:
375 additional_args = []
376
377 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
378 cmd = ["brillo_update_payload", "generate",
379 "--payload", payload_file,
380 "--target_image", target_file]
381 if source_file is not None:
382 cmd.extend(["--source_image", source_file])
Tianjie Xu1b079832019-08-28 12:19:23 -0700383 if OPTIONS.disable_fec_computation:
384 cmd.extend(["--disable_fec_computation", "true"])
Tao Bao40b18822018-01-30 18:19:04 -0800385 cmd.extend(additional_args)
Tao Bao718faed2019-08-02 13:24:19 -0700386 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800387
388 self.payload_file = payload_file
389 self.payload_properties = None
390
391 def Sign(self, payload_signer):
392 """Generates and signs the hashes of the payload and metadata.
393
394 Args:
395 payload_signer: A PayloadSigner() instance that serves the signing work.
396
397 Raises:
398 AssertionError: On any failure when calling brillo_update_payload script.
399 """
400 assert isinstance(payload_signer, PayloadSigner)
401
402 # 1. Generate hashes of the payload and metadata files.
403 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
404 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
405 cmd = ["brillo_update_payload", "hash",
406 "--unsigned_payload", self.payload_file,
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700407 "--signature_size", str(payload_signer.maximum_signature_size),
Tao Bao40b18822018-01-30 18:19:04 -0800408 "--metadata_hash_file", metadata_sig_file,
409 "--payload_hash_file", payload_sig_file]
Tao Bao718faed2019-08-02 13:24:19 -0700410 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800411
412 # 2. Sign the hashes.
413 signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
414 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
415
416 # 3. Insert the signatures back into the payload file.
417 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
418 suffix=".bin")
419 cmd = ["brillo_update_payload", "sign",
420 "--unsigned_payload", self.payload_file,
421 "--payload", signed_payload_file,
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700422 "--signature_size", str(payload_signer.maximum_signature_size),
Tao Bao40b18822018-01-30 18:19:04 -0800423 "--metadata_signature_file", signed_metadata_sig_file,
424 "--payload_signature_file", signed_payload_sig_file]
Tao Bao718faed2019-08-02 13:24:19 -0700425 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800426
427 # 4. Dump the signed payload properties.
428 properties_file = common.MakeTempFile(prefix="payload-properties-",
429 suffix=".txt")
430 cmd = ["brillo_update_payload", "properties",
431 "--payload", signed_payload_file,
432 "--properties_file", properties_file]
Tao Bao718faed2019-08-02 13:24:19 -0700433 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800434
Tao Bao667ff572018-02-10 00:02:40 -0800435 if self.secondary:
436 with open(properties_file, "a") as f:
437 f.write("SWITCH_SLOT_ON_REBOOT=0\n")
438
Tao Bao40b18822018-01-30 18:19:04 -0800439 if OPTIONS.wipe_user_data:
440 with open(properties_file, "a") as f:
441 f.write("POWERWASH=1\n")
442
443 self.payload_file = signed_payload_file
444 self.payload_properties = properties_file
445
Tao Bao667ff572018-02-10 00:02:40 -0800446 def WriteToZip(self, output_zip):
Tao Bao40b18822018-01-30 18:19:04 -0800447 """Writes the payload to the given zip.
448
449 Args:
450 output_zip: The output ZipFile instance.
451 """
452 assert self.payload_file is not None
453 assert self.payload_properties is not None
454
Tao Bao667ff572018-02-10 00:02:40 -0800455 if self.secondary:
Tao Baof7140c02018-01-30 17:09:24 -0800456 payload_arcname = Payload.SECONDARY_PAYLOAD_BIN
457 payload_properties_arcname = Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT
458 else:
459 payload_arcname = Payload.PAYLOAD_BIN
460 payload_properties_arcname = Payload.PAYLOAD_PROPERTIES_TXT
461
Tao Bao40b18822018-01-30 18:19:04 -0800462 # Add the signed payload file and properties into the zip. In order to
463 # support streaming, we pack them as ZIP_STORED. So these entries can be
464 # read directly with the offset and length pairs.
Tao Baof7140c02018-01-30 17:09:24 -0800465 common.ZipWrite(output_zip, self.payload_file, arcname=payload_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800466 compress_type=zipfile.ZIP_STORED)
467 common.ZipWrite(output_zip, self.payload_properties,
Tao Baof7140c02018-01-30 17:09:24 -0800468 arcname=payload_properties_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800469 compress_type=zipfile.ZIP_STORED)
470
471
Doug Zongkereef39442009-04-02 12:14:19 -0700472def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200473 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700474
Doug Zongker951495f2009-08-14 12:44:19 -0700475 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
476 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700477
478
Tao Bao481bab82017-12-21 11:23:09 -0800479def _LoadOemDicts(oem_source):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800480 """Returns the list of loaded OEM properties dict."""
Tao Bao481bab82017-12-21 11:23:09 -0800481 if not oem_source:
482 return None
483
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800484 oem_dicts = []
Tao Bao481bab82017-12-21 11:23:09 -0800485 for oem_file in oem_source:
486 with open(oem_file) as fp:
487 oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines()))
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800488 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700489
Doug Zongkereef39442009-04-02 12:14:19 -0700490
Tao Baod42e97e2016-11-30 12:11:57 -0800491def _WriteRecoveryImageToBoot(script, output_zip):
492 """Find and write recovery image to /boot in two-step OTA.
493
494 In two-step OTAs, we write recovery image to /boot as the first step so that
495 we can reboot to there and install a new recovery image to /recovery.
496 A special "recovery-two-step.img" will be preferred, which encodes the correct
497 path of "/boot". Otherwise the device may show "device is corrupt" message
498 when booting into /boot.
499
500 Fall back to using the regular recovery.img if the two-step recovery image
501 doesn't exist. Note that rebuilding the special image at this point may be
502 infeasible, because we don't have the desired boot signer and keys when
503 calling ota_from_target_files.py.
504 """
505
506 recovery_two_step_img_name = "recovery-two-step.img"
507 recovery_two_step_img_path = os.path.join(
Tao Bao04808502019-07-25 23:11:41 -0700508 OPTIONS.input_tmp, "OTA", recovery_two_step_img_name)
Tao Baod42e97e2016-11-30 12:11:57 -0800509 if os.path.exists(recovery_two_step_img_path):
Tao Bao04808502019-07-25 23:11:41 -0700510 common.ZipWrite(
511 output_zip,
512 recovery_two_step_img_path,
513 arcname=recovery_two_step_img_name)
Tao Bao32fcdab2018-10-12 10:30:39 -0700514 logger.info(
515 "two-step package: using %s in stage 1/3", recovery_two_step_img_name)
Tao Baod42e97e2016-11-30 12:11:57 -0800516 script.WriteRawImage("/boot", recovery_two_step_img_name)
517 else:
Tao Bao32fcdab2018-10-12 10:30:39 -0700518 logger.info("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800519 # The "recovery.img" entry has been written into package earlier.
520 script.WriteRawImage("/boot", "recovery.img")
521
522
Bill Peckhame868aec2019-09-17 17:06:47 -0700523def HasRecoveryPatch(target_files_zip, info_dict):
524 board_uses_vendorimage = info_dict.get("board_uses_vendorimage") == "true"
525
526 if board_uses_vendorimage:
527 target_files_dir = "VENDOR"
528 else:
529 target_files_dir = "SYSTEM/vendor"
530
531 patch = "%s/recovery-from-boot.p" % target_files_dir
532 img = "%s/etc/recovery.img" %target_files_dir
533
Tao Baof2cffbd2015-07-22 12:33:18 -0700534 namelist = [name for name in target_files_zip.namelist()]
Bill Peckhame868aec2019-09-17 17:06:47 -0700535 return (patch in namelist or img in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700536
Tao Bao457cbf62017-03-06 09:56:01 -0800537
Yifan Hong51d37562019-04-23 17:06:46 -0700538def HasPartition(target_files_zip, partition):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700539 try:
Yifan Hong51d37562019-04-23 17:06:46 -0700540 target_files_zip.getinfo(partition.upper() + "/")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700541 return True
542 except KeyError:
543 return False
544
Tao Bao457cbf62017-03-06 09:56:01 -0800545
Yifan Hong9276cf02019-08-21 16:37:04 -0700546def HasTrebleEnabled(target_files, target_info):
547 def HasVendorPartition(target_files):
548 if os.path.isdir(target_files):
549 return os.path.isdir(os.path.join(target_files, "VENDOR"))
550 if zipfile.is_zipfile(target_files):
551 return HasPartition(zipfile.ZipFile(target_files), "vendor")
552 raise ValueError("Unknown target_files argument")
Yifan Hong51d37562019-04-23 17:06:46 -0700553
Yifan Hong9276cf02019-08-21 16:37:04 -0700554 return (HasVendorPartition(target_files) and
Tao Bao481bab82017-12-21 11:23:09 -0800555 target_info.GetBuildProp("ro.treble.enabled") == "true")
Tao Baobcd1d162017-08-26 13:10:26 -0700556
557
Tao Bao481bab82017-12-21 11:23:09 -0800558def WriteFingerprintAssertion(script, target_info, source_info):
559 source_oem_props = source_info.oem_props
560 target_oem_props = target_info.oem_props
Michael Runge6e836112014-04-15 17:40:21 -0700561
Tao Bao481bab82017-12-21 11:23:09 -0800562 if source_oem_props is None and target_oem_props is None:
563 script.AssertSomeFingerprint(
564 source_info.fingerprint, target_info.fingerprint)
565 elif source_oem_props is not None and target_oem_props is not None:
566 script.AssertSomeThumbprint(
567 target_info.GetBuildProp("ro.build.thumbprint"),
568 source_info.GetBuildProp("ro.build.thumbprint"))
569 elif source_oem_props is None and target_oem_props is not None:
570 script.AssertFingerprintOrThumbprint(
571 source_info.fingerprint,
572 target_info.GetBuildProp("ro.build.thumbprint"))
573 else:
574 script.AssertFingerprintOrThumbprint(
575 target_info.fingerprint,
576 source_info.GetBuildProp("ro.build.thumbprint"))
Doug Zongker73ef8252009-07-23 15:12:53 -0700577
Doug Zongkerfc44a512014-08-26 13:10:25 -0700578
Yifan Hong9276cf02019-08-21 16:37:04 -0700579def CheckVintfIfTrebleEnabled(target_files, target_info):
580 """Checks compatibility info of the input target files.
Tao Bao21803d32017-04-19 10:16:09 -0700581
Yifan Hong9276cf02019-08-21 16:37:04 -0700582 Metadata used for compatibility verification is retrieved from target_zip.
Tao Bao21803d32017-04-19 10:16:09 -0700583
Yifan Hong9276cf02019-08-21 16:37:04 -0700584 Compatibility should only be checked for devices that have enabled
Tao Baobcd1d162017-08-26 13:10:26 -0700585 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700586
587 Args:
Yifan Hong9276cf02019-08-21 16:37:04 -0700588 target_files: Path to zip file containing the source files to be included
589 for OTA. Can also be the path to extracted directory.
Tao Bao481bab82017-12-21 11:23:09 -0800590 target_info: The BuildInfo instance that holds the target build info.
Tao Bao21803d32017-04-19 10:16:09 -0700591 """
592
Tao Baobcd1d162017-08-26 13:10:26 -0700593 # Will only proceed if the target has enabled the Treble support (as well as
594 # having a /vendor partition).
Yifan Hong9276cf02019-08-21 16:37:04 -0700595 if not HasTrebleEnabled(target_files, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700596 return
597
xunchangabfa2652019-02-19 16:27:10 -0800598 # Skip adding the compatibility package as a workaround for b/114240221. The
599 # compatibility will always fail on devices without qualified kernels.
600 if OPTIONS.skip_compatibility_check:
601 return
602
Yifan Hong9276cf02019-08-21 16:37:04 -0700603 if not check_target_files_vintf.CheckVintf(target_files, target_info):
604 raise RuntimeError("VINTF compatibility check failed")
Tao Bao21803d32017-04-19 10:16:09 -0700605
606
Tianjie Xuf67dd802019-05-20 17:50:36 -0700607def GetBlockDifferences(target_zip, source_zip, target_info, source_info,
608 device_specific):
609 """Returns a ordered dict of block differences with partition name as key."""
610
611 def GetIncrementalBlockDifferenceForPartition(name):
612 if not HasPartition(source_zip, name):
613 raise RuntimeError("can't generate incremental that adds {}".format(name))
614
615 partition_src = common.GetUserImage(name, OPTIONS.source_tmp, source_zip,
616 info_dict=source_info,
617 allow_shared_blocks=allow_shared_blocks)
618
619 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
620 name, 4096, target_info)
621 partition_tgt = common.GetUserImage(name, OPTIONS.target_tmp, target_zip,
622 info_dict=target_info,
623 allow_shared_blocks=allow_shared_blocks,
624 hashtree_info_generator=
625 hashtree_info_generator)
626
627 # Check the first block of the source system partition for remount R/W only
628 # if the filesystem is ext4.
629 partition_source_info = source_info["fstab"]["/" + name]
630 check_first_block = partition_source_info.fs_type == "ext4"
631 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
632 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
633 # b) the blocks listed in block map may not contain all the bytes for a
634 # given file (because they're rounded to be 4K-aligned).
635 partition_target_info = target_info["fstab"]["/" + name]
636 disable_imgdiff = (partition_source_info.fs_type == "squashfs" or
637 partition_target_info.fs_type == "squashfs")
Xindong Xu2a7aaa62020-03-13 15:59:22 +0800638 return common.BlockDifference(name, partition_tgt, partition_src,
Tianjie Xuf67dd802019-05-20 17:50:36 -0700639 check_first_block,
640 version=blockimgdiff_version,
641 disable_imgdiff=disable_imgdiff)
642
643 if source_zip:
644 # See notes in common.GetUserImage()
645 allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or
646 target_info.get('ext4_share_dup_blocks') == "true")
647 blockimgdiff_version = max(
648 int(i) for i in target_info.get(
649 "blockimgdiff_versions", "1").split(","))
650 assert blockimgdiff_version >= 3
651
652 block_diff_dict = collections.OrderedDict()
653 partition_names = ["system", "vendor", "product", "odm", "system_ext"]
654 for partition in partition_names:
655 if not HasPartition(target_zip, partition):
656 continue
657 # Full OTA update.
658 if not source_zip:
659 tgt = common.GetUserImage(partition, OPTIONS.input_tmp, target_zip,
660 info_dict=target_info,
661 reset_file_map=True)
662 block_diff_dict[partition] = common.BlockDifference(partition, tgt,
663 src=None)
664 # Incremental OTA update.
665 else:
666 block_diff_dict[partition] = GetIncrementalBlockDifferenceForPartition(
667 partition)
668 assert "system" in block_diff_dict
669
670 # Get the block diffs from the device specific script. If there is a
671 # duplicate block diff for a partition, ignore the diff in the generic script
672 # and use the one in the device specific script instead.
673 if source_zip:
674 device_specific_diffs = device_specific.IncrementalOTA_GetBlockDifferences()
675 function_name = "IncrementalOTA_GetBlockDifferences"
676 else:
677 device_specific_diffs = device_specific.FullOTA_GetBlockDifferences()
678 function_name = "FullOTA_GetBlockDifferences"
679
680 if device_specific_diffs:
681 assert all(isinstance(diff, common.BlockDifference)
682 for diff in device_specific_diffs), \
683 "{} is not returning a list of BlockDifference objects".format(
684 function_name)
685 for diff in device_specific_diffs:
686 if diff.partition in block_diff_dict:
687 logger.warning("Duplicate block difference found. Device specific block"
688 " diff for partition '%s' overrides the one in generic"
689 " script.", diff.partition)
690 block_diff_dict[diff.partition] = diff
691
692 return block_diff_dict
693
694
Tao Bao491d7e22018-02-21 13:17:22 -0800695def WriteFullOTAPackage(input_zip, output_file):
Tao Bao1c320f82019-10-04 23:25:12 -0700696 target_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700697
Tao Bao481bab82017-12-21 11:23:09 -0800698 # We don't know what version it will be installed on top of. We expect the API
699 # just won't change very often. Similarly for fstab, it might have changed in
700 # the target build.
701 target_api_version = target_info["recovery_api_version"]
702 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700703
Tao Bao481bab82017-12-21 11:23:09 -0800704 if target_info.oem_props and not OPTIONS.oem_no_mount:
705 target_info.WriteMountOemScript(script)
706
Tao Baodf3a48b2018-01-10 16:30:43 -0800707 metadata = GetPackageMetadata(target_info)
Doug Zongker2ea21062010-04-28 16:05:21 -0700708
Tao Bao491d7e22018-02-21 13:17:22 -0800709 if not OPTIONS.no_signing:
710 staging_file = common.MakeTempFile(suffix='.zip')
711 else:
712 staging_file = output_file
713
714 output_zip = zipfile.ZipFile(
715 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
716
Doug Zongker05d3dea2009-06-22 11:32:31 -0700717 device_specific = common.DeviceSpecificParams(
718 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800719 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700720 output_zip=output_zip,
721 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700722 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700723 metadata=metadata,
724 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700725
Bill Peckhame868aec2019-09-17 17:06:47 -0700726 assert HasRecoveryPatch(input_zip, info_dict=OPTIONS.info_dict)
Doug Zongkerc9253822014-02-04 12:17:58 -0800727
Tao Bao481bab82017-12-21 11:23:09 -0800728 # Assertions (e.g. downgrade check, device properties check).
729 ts = target_info.GetBuildProp("ro.build.date.utc")
730 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700731 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700732
Tao Bao481bab82017-12-21 11:23:09 -0800733 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700734 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800735
Tianjie Xuf67dd802019-05-20 17:50:36 -0700736 block_diff_dict = GetBlockDifferences(target_zip=input_zip, source_zip=None,
737 target_info=target_info,
738 source_info=None,
739 device_specific=device_specific)
740
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800741 # Two-step package strategy (in chronological order, which is *not*
742 # the order in which the generated script has things):
743 #
744 # if stage is not "2/3" or "3/3":
745 # write recovery image to boot partition
746 # set stage to "2/3"
747 # reboot to boot partition and restart recovery
748 # else if stage is "2/3":
749 # write recovery image to recovery partition
750 # set stage to "3/3"
751 # reboot to recovery partition and restart recovery
752 # else:
753 # (stage must be "3/3")
754 # set stage to ""
755 # do normal full package installation:
756 # wipe and install system, boot image, etc.
757 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700758 # complete script normally
759 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800760
761 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
762 OPTIONS.input_tmp, "RECOVERY")
763 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800764 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800765 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800766 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800767 assert fs.fs_type.upper() == "EMMC", \
768 "two-step packages only supported on devices with EMMC /misc partitions"
769 bcb_dev = {"bcb_dev": fs.device}
770 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
771 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700772if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800773""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800774
775 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
776 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800777 script.WriteRawImage("/recovery", "recovery.img")
778 script.AppendExtra("""
779set_stage("%(bcb_dev)s", "3/3");
780reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700781else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800782""" % bcb_dev)
783
Tao Baod42e97e2016-11-30 12:11:57 -0800784 # Stage 3/3: Make changes.
785 script.Comment("Stage 3/3")
786
Tao Bao6c55a8a2015-04-08 15:30:27 -0700787 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800788 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700789
Doug Zongkere5ff5902012-01-17 10:55:37 -0800790 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700791
Tianjie Xuf67dd802019-05-20 17:50:36 -0700792 # All other partitions as well as the data wipe use 10% of the progress, and
793 # the update of the system partition takes the remaining progress.
794 system_progress = 0.9 - (len(block_diff_dict) - 1) * 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700795 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800796 system_progress -= 0.1
Tianjie Xuf67dd802019-05-20 17:50:36 -0700797 progress_dict = {partition: 0.1 for partition in block_diff_dict}
798 progress_dict["system"] = system_progress
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700799
Yifan Hong10c530d2018-12-27 17:34:18 -0800800 if target_info.get('use_dynamic_partitions') == "true":
801 # Use empty source_info_dict to indicate that all partitions / groups must
802 # be re-added.
803 dynamic_partitions_diff = common.DynamicPartitionsDifference(
804 info_dict=OPTIONS.info_dict,
Tianjie Xuf67dd802019-05-20 17:50:36 -0700805 block_diffs=block_diff_dict.values(),
Yifan Hong10c530d2018-12-27 17:34:18 -0800806 progress_dict=progress_dict)
807 dynamic_partitions_diff.WriteScript(script, output_zip,
808 write_verify_script=OPTIONS.verify)
809 else:
Tianjie Xuf67dd802019-05-20 17:50:36 -0700810 for block_diff in block_diff_dict.values():
Yifan Hong10c530d2018-12-27 17:34:18 -0800811 block_diff.WriteScript(script, output_zip,
812 progress=progress_dict.get(block_diff.partition),
813 write_verify_script=OPTIONS.verify)
Doug Zongker73ef8252009-07-23 15:12:53 -0700814
Yifan Hong9276cf02019-08-21 16:37:04 -0700815 CheckVintfIfTrebleEnabled(OPTIONS.input_tmp, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700816
Yifan Hong10c530d2018-12-27 17:34:18 -0800817 boot_img = common.GetBootableImage(
818 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Tao Bao481bab82017-12-21 11:23:09 -0800819 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700820 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700821
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700822 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700823
Tianjie Xuf67dd802019-05-20 17:50:36 -0700824 script.ShowProgress(0.1, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700825 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700826
Doug Zongker1c390a22009-05-14 19:06:36 -0700827 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700828 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700829
Doug Zongker14833602010-02-02 13:12:04 -0800830 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800831
Doug Zongker922206e2014-03-04 13:16:24 -0800832 if OPTIONS.wipe_user_data:
833 script.ShowProgress(0.1, 10)
834 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700835
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800836 if OPTIONS.two_step:
837 script.AppendExtra("""
838set_stage("%(bcb_dev)s", "");
839""" % bcb_dev)
840 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800841
842 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
843 script.Comment("Stage 1/3")
844 _WriteRecoveryImageToBoot(script, output_zip)
845
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800846 script.AppendExtra("""
847set_stage("%(bcb_dev)s", "2/3");
848reboot_now("%(bcb_dev)s", "");
849endif;
850endif;
851""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800852
Tao Bao5d182562016-02-23 11:38:39 -0800853 script.SetProgress(1)
854 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800855 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -0800856
857 # We haven't written the metadata entry, which will be done in
858 # FinalizeMetadata.
859 common.ZipClose(output_zip)
860
861 needed_property_files = (
862 NonAbOtaPropertyFiles(),
863 )
864 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Doug Zongker2ea21062010-04-28 16:05:21 -0700865
Doug Zongkerfc44a512014-08-26 13:10:25 -0700866
xunchang1cfe2512019-02-19 14:14:48 -0800867def WriteMetadata(metadata, output):
868 """Writes the metadata to the zip archive or a file.
869
870 Args:
871 metadata: The metadata dict for the package.
872 output: A ZipFile object or a string of the output file path.
873 """
874
Tao Bao59cf0c52019-06-25 10:04:24 -0700875 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.items())])
xunchang1cfe2512019-02-19 14:14:48 -0800876 if isinstance(output, zipfile.ZipFile):
877 common.ZipWriteStr(output, METADATA_NAME, value,
878 compress_type=zipfile.ZIP_STORED)
879 return
880
881 with open(output, 'w') as f:
882 f.write(value)
Doug Zongkereef39442009-04-02 12:14:19 -0700883
Doug Zongkerfc44a512014-08-26 13:10:25 -0700884
Tao Bao481bab82017-12-21 11:23:09 -0800885def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -0800886 # Only incremental OTAs are allowed to reach here.
887 assert OPTIONS.incremental_source is not None
888
Tao Bao481bab82017-12-21 11:23:09 -0800889 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
890 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Bao59cf0c52019-06-25 10:04:24 -0700891 is_downgrade = int(post_timestamp) < int(pre_timestamp)
Tao Baob31892e2017-02-07 11:21:17 -0800892
893 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800894 if not is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700895 raise RuntimeError(
896 "--downgrade or --override_timestamp specified but no downgrade "
897 "detected: pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800898 metadata["ota-downgrade"] = "yes"
Tao Baob31892e2017-02-07 11:21:17 -0800899 else:
900 if is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700901 raise RuntimeError(
902 "Downgrade detected based on timestamp check: pre: %s, post: %s. "
903 "Need to specify --override_timestamp OR --downgrade to allow "
904 "building the incremental." % (pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800905
906
Tao Baodf3a48b2018-01-10 16:30:43 -0800907def GetPackageMetadata(target_info, source_info=None):
908 """Generates and returns the metadata dict.
909
910 It generates a dict() that contains the info to be written into an OTA
911 package (META-INF/com/android/metadata). It also handles the detection of
Tao Baofaa8e0b2018-04-12 14:31:43 -0700912 downgrade / data wipe based on the global options.
Tao Baodf3a48b2018-01-10 16:30:43 -0800913
914 Args:
915 target_info: The BuildInfo instance that holds the target build info.
916 source_info: The BuildInfo instance that holds the source build info, or
917 None if generating full OTA.
918
919 Returns:
920 A dict to be written into package metadata entry.
921 """
Tao Bao1c320f82019-10-04 23:25:12 -0700922 assert isinstance(target_info, common.BuildInfo)
923 assert source_info is None or isinstance(source_info, common.BuildInfo)
Tao Baodf3a48b2018-01-10 16:30:43 -0800924
925 metadata = {
926 'post-build' : target_info.fingerprint,
927 'post-build-incremental' : target_info.GetBuildProp(
928 'ro.build.version.incremental'),
Tao Bao35dc2552018-02-01 13:18:00 -0800929 'post-sdk-level' : target_info.GetBuildProp(
930 'ro.build.version.sdk'),
931 'post-security-patch-level' : target_info.GetBuildProp(
932 'ro.build.version.security_patch'),
Tao Baodf3a48b2018-01-10 16:30:43 -0800933 }
934
935 if target_info.is_ab:
936 metadata['ota-type'] = 'AB'
937 metadata['ota-required-cache'] = '0'
938 else:
939 metadata['ota-type'] = 'BLOCK'
940
941 if OPTIONS.wipe_user_data:
942 metadata['ota-wipe'] = 'yes'
943
Tao Bao393eeb42019-03-06 16:00:38 -0800944 if OPTIONS.retrofit_dynamic_partitions:
945 metadata['ota-retrofit-dynamic-partitions'] = 'yes'
946
Tao Baodf3a48b2018-01-10 16:30:43 -0800947 is_incremental = source_info is not None
948 if is_incremental:
949 metadata['pre-build'] = source_info.fingerprint
950 metadata['pre-build-incremental'] = source_info.GetBuildProp(
951 'ro.build.version.incremental')
952 metadata['pre-device'] = source_info.device
953 else:
954 metadata['pre-device'] = target_info.device
955
Tao Baofaa8e0b2018-04-12 14:31:43 -0700956 # Use the actual post-timestamp, even for a downgrade case.
957 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
958
959 # Detect downgrades and set up downgrade flags accordingly.
Tao Baodf3a48b2018-01-10 16:30:43 -0800960 if is_incremental:
961 HandleDowngradeMetadata(metadata, target_info, source_info)
Tao Baodf3a48b2018-01-10 16:30:43 -0800962
963 return metadata
964
965
Tao Baod3fc38a2018-03-08 16:09:01 -0800966class PropertyFiles(object):
967 """A class that computes the property-files string for an OTA package.
968
969 A property-files string is a comma-separated string that contains the
970 offset/size info for an OTA package. The entries, which must be ZIP_STORED,
971 can be fetched directly with the package URL along with the offset/size info.
972 These strings can be used for streaming A/B OTAs, or allowing an updater to
973 download package metadata entry directly, without paying the cost of
974 downloading entire package.
Tao Baofe5b69a2018-03-02 09:47:43 -0800975
Tao Baocc8e2662018-03-01 19:30:00 -0800976 Computing the final property-files string requires two passes. Because doing
977 the whole package signing (with signapk.jar) will possibly reorder the ZIP
978 entries, which may in turn invalidate earlier computed ZIP entry offset/size
979 values.
980
981 This class provides functions to be called for each pass. The general flow is
982 as follows.
983
Tao Baod3fc38a2018-03-08 16:09:01 -0800984 property_files = PropertyFiles()
Tao Baocc8e2662018-03-01 19:30:00 -0800985 # The first pass, which writes placeholders before doing initial signing.
986 property_files.Compute()
987 SignOutput()
988
989 # The second pass, by replacing the placeholders with actual data.
990 property_files.Finalize()
991 SignOutput()
992
993 And the caller can additionally verify the final result.
994
995 property_files.Verify()
Tao Baofe5b69a2018-03-02 09:47:43 -0800996 """
997
Tao Baocc8e2662018-03-01 19:30:00 -0800998 def __init__(self):
Tao Baod3fc38a2018-03-08 16:09:01 -0800999 self.name = None
1000 self.required = ()
1001 self.optional = ()
Tao Baofe5b69a2018-03-02 09:47:43 -08001002
Tao Baocc8e2662018-03-01 19:30:00 -08001003 def Compute(self, input_zip):
1004 """Computes and returns a property-files string with placeholders.
Tao Baofe5b69a2018-03-02 09:47:43 -08001005
Tao Baocc8e2662018-03-01 19:30:00 -08001006 We reserve extra space for the offset and size of the metadata entry itself,
1007 although we don't know the final values until the package gets signed.
Tao Baofe5b69a2018-03-02 09:47:43 -08001008
Tao Baocc8e2662018-03-01 19:30:00 -08001009 Args:
1010 input_zip: The input ZIP file.
Tao Baofe5b69a2018-03-02 09:47:43 -08001011
Tao Baocc8e2662018-03-01 19:30:00 -08001012 Returns:
1013 A string with placeholders for the metadata offset/size info, e.g.
1014 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1015 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001016 return self.GetPropertyFilesString(input_zip, reserve_space=True)
Tao Baofe5b69a2018-03-02 09:47:43 -08001017
Tao Baod2ce2ed2018-03-16 12:59:42 -07001018 class InsufficientSpaceException(Exception):
1019 pass
1020
Tao Baocc8e2662018-03-01 19:30:00 -08001021 def Finalize(self, input_zip, reserved_length):
1022 """Finalizes a property-files string with actual METADATA offset/size info.
1023
1024 The input ZIP file has been signed, with the ZIP entries in the desired
1025 place (signapk.jar will possibly reorder the ZIP entries). Now we compute
1026 the ZIP entry offsets and construct the property-files string with actual
1027 data. Note that during this process, we must pad the property-files string
1028 to the reserved length, so that the METADATA entry size remains the same.
1029 Otherwise the entries' offsets and sizes may change again.
1030
1031 Args:
1032 input_zip: The input ZIP file.
1033 reserved_length: The reserved length of the property-files string during
1034 the call to Compute(). The final string must be no more than this
1035 size.
1036
1037 Returns:
1038 A property-files string including the metadata offset/size info, e.g.
1039 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379 ".
1040
1041 Raises:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001042 InsufficientSpaceException: If the reserved length is insufficient to hold
1043 the final string.
Tao Baocc8e2662018-03-01 19:30:00 -08001044 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001045 result = self.GetPropertyFilesString(input_zip, reserve_space=False)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001046 if len(result) > reserved_length:
1047 raise self.InsufficientSpaceException(
1048 'Insufficient reserved space: reserved={}, actual={}'.format(
1049 reserved_length, len(result)))
1050
Tao Baocc8e2662018-03-01 19:30:00 -08001051 result += ' ' * (reserved_length - len(result))
1052 return result
1053
1054 def Verify(self, input_zip, expected):
1055 """Verifies the input ZIP file contains the expected property-files string.
1056
1057 Args:
1058 input_zip: The input ZIP file.
1059 expected: The property-files string that's computed from Finalize().
1060
1061 Raises:
1062 AssertionError: On finding a mismatch.
1063 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001064 actual = self.GetPropertyFilesString(input_zip)
Tao Baocc8e2662018-03-01 19:30:00 -08001065 assert actual == expected, \
1066 "Mismatching streaming metadata: {} vs {}.".format(actual, expected)
1067
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001068 def GetPropertyFilesString(self, zip_file, reserve_space=False):
1069 """
1070 Constructs the property-files string per request.
1071
1072 Args:
1073 zip_file: The input ZIP file.
1074 reserved_length: The reserved length of the property-files string.
1075
1076 Returns:
1077 A property-files string including the metadata offset/size info, e.g.
1078 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1079 """
Tao Baocc8e2662018-03-01 19:30:00 -08001080
1081 def ComputeEntryOffsetSize(name):
1082 """Computes the zip entry offset and size."""
1083 info = zip_file.getinfo(name)
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001084 offset = info.header_offset
1085 offset += zipfile.sizeFileHeader
1086 offset += len(info.extra) + len(info.filename)
Tao Baocc8e2662018-03-01 19:30:00 -08001087 size = info.file_size
1088 return '%s:%d:%d' % (os.path.basename(name), offset, size)
1089
1090 tokens = []
Tao Bao85f16982018-03-08 16:28:33 -08001091 tokens.extend(self._GetPrecomputed(zip_file))
Tao Baocc8e2662018-03-01 19:30:00 -08001092 for entry in self.required:
1093 tokens.append(ComputeEntryOffsetSize(entry))
1094 for entry in self.optional:
1095 if entry in zip_file.namelist():
1096 tokens.append(ComputeEntryOffsetSize(entry))
1097
1098 # 'META-INF/com/android/metadata' is required. We don't know its actual
1099 # offset and length (as well as the values for other entries). So we reserve
Tao Baod2ce2ed2018-03-16 12:59:42 -07001100 # 15-byte as a placeholder ('offset:length'), which is sufficient to cover
1101 # the space for metadata entry. Because 'offset' allows a max of 10-digit
1102 # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the
1103 # reserved space serves the metadata entry only.
Tao Baocc8e2662018-03-01 19:30:00 -08001104 if reserve_space:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001105 tokens.append('metadata:' + ' ' * 15)
Tao Baocc8e2662018-03-01 19:30:00 -08001106 else:
1107 tokens.append(ComputeEntryOffsetSize(METADATA_NAME))
1108
1109 return ','.join(tokens)
Tao Baofe5b69a2018-03-02 09:47:43 -08001110
Tao Bao85f16982018-03-08 16:28:33 -08001111 def _GetPrecomputed(self, input_zip):
1112 """Computes the additional tokens to be included into the property-files.
1113
1114 This applies to tokens without actual ZIP entries, such as
1115 payload_metadadata.bin. We want to expose the offset/size to updaters, so
1116 that they can download the payload metadata directly with the info.
1117
1118 Args:
1119 input_zip: The input zip file.
1120
1121 Returns:
1122 A list of strings (tokens) to be added to the property-files string.
1123 """
1124 # pylint: disable=no-self-use
1125 # pylint: disable=unused-argument
1126 return []
1127
Tao Baofe5b69a2018-03-02 09:47:43 -08001128
Tao Baod3fc38a2018-03-08 16:09:01 -08001129class StreamingPropertyFiles(PropertyFiles):
1130 """A subclass for computing the property-files for streaming A/B OTAs."""
1131
1132 def __init__(self):
1133 super(StreamingPropertyFiles, self).__init__()
1134 self.name = 'ota-streaming-property-files'
1135 self.required = (
1136 # payload.bin and payload_properties.txt must exist.
1137 'payload.bin',
1138 'payload_properties.txt',
1139 )
1140 self.optional = (
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001141 # care_map is available only if dm-verity is enabled.
1142 'care_map.pb',
Tao Baod3fc38a2018-03-08 16:09:01 -08001143 'care_map.txt',
1144 # compatibility.zip is available only if target supports Treble.
1145 'compatibility.zip',
1146 )
1147
1148
Tao Bao85f16982018-03-08 16:28:33 -08001149class AbOtaPropertyFiles(StreamingPropertyFiles):
1150 """The property-files for A/B OTA that includes payload_metadata.bin info.
1151
1152 Since P, we expose one more token (aka property-file), in addition to the ones
1153 for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'.
1154 'payload_metadata.bin' is the header part of a payload ('payload.bin'), which
1155 doesn't exist as a separate ZIP entry, but can be used to verify if the
1156 payload can be applied on the given device.
1157
1158 For backward compatibility, we keep both of the 'ota-streaming-property-files'
1159 and the newly added 'ota-property-files' in P. The new token will only be
1160 available in 'ota-property-files'.
1161 """
1162
1163 def __init__(self):
1164 super(AbOtaPropertyFiles, self).__init__()
1165 self.name = 'ota-property-files'
1166
1167 def _GetPrecomputed(self, input_zip):
1168 offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip)
1169 return ['payload_metadata.bin:{}:{}'.format(offset, size)]
1170
1171 @staticmethod
1172 def _GetPayloadMetadataOffsetAndSize(input_zip):
1173 """Computes the offset and size of the payload metadata for a given package.
1174
1175 (From system/update_engine/update_metadata.proto)
1176 A delta update file contains all the deltas needed to update a system from
1177 one specific version to another specific version. The update format is
1178 represented by this struct pseudocode:
1179
1180 struct delta_update_file {
1181 char magic[4] = "CrAU";
1182 uint64 file_format_version;
1183 uint64 manifest_size; // Size of protobuf DeltaArchiveManifest
1184
1185 // Only present if format_version > 1:
1186 uint32 metadata_signature_size;
1187
1188 // The Bzip2 compressed DeltaArchiveManifest
1189 char manifest[metadata_signature_size];
1190
1191 // The signature of the metadata (from the beginning of the payload up to
1192 // this location, not including the signature itself). This is a
1193 // serialized Signatures message.
1194 char medatada_signature_message[metadata_signature_size];
1195
1196 // Data blobs for files, no specific format. The specific offset
1197 // and length of each data blob is recorded in the DeltaArchiveManifest.
1198 struct {
1199 char data[];
1200 } blobs[];
1201
1202 // These two are not signed:
1203 uint64 payload_signatures_message_size;
1204 char payload_signatures_message[];
1205 };
1206
1207 'payload-metadata.bin' contains all the bytes from the beginning of the
1208 payload, till the end of 'medatada_signature_message'.
1209 """
1210 payload_info = input_zip.getinfo('payload.bin')
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001211 payload_offset = payload_info.header_offset
1212 payload_offset += zipfile.sizeFileHeader
1213 payload_offset += len(payload_info.extra) + len(payload_info.filename)
Tao Bao85f16982018-03-08 16:28:33 -08001214 payload_size = payload_info.file_size
1215
Tao Bao59cf0c52019-06-25 10:04:24 -07001216 with input_zip.open('payload.bin') as payload_fp:
Tao Bao85f16982018-03-08 16:28:33 -08001217 header_bin = payload_fp.read(24)
1218
1219 # network byte order (big-endian)
1220 header = struct.unpack("!IQQL", header_bin)
1221
1222 # 'CrAU'
1223 magic = header[0]
1224 assert magic == 0x43724155, "Invalid magic: {:x}".format(magic)
1225
1226 manifest_size = header[2]
1227 metadata_signature_size = header[3]
1228 metadata_total = 24 + manifest_size + metadata_signature_size
1229 assert metadata_total < payload_size
1230
1231 return (payload_offset, metadata_total)
1232
1233
Tao Bao491d7e22018-02-21 13:17:22 -08001234class NonAbOtaPropertyFiles(PropertyFiles):
1235 """The property-files for non-A/B OTA.
1236
1237 For non-A/B OTA, the property-files string contains the info for METADATA
1238 entry, with which a system updater can be fetched the package metadata prior
1239 to downloading the entire package.
1240 """
1241
1242 def __init__(self):
1243 super(NonAbOtaPropertyFiles, self).__init__()
1244 self.name = 'ota-property-files'
1245
1246
Tao Baod3fc38a2018-03-08 16:09:01 -08001247def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
Tao Baofe5b69a2018-03-02 09:47:43 -08001248 """Finalizes the metadata and signs an A/B OTA package.
1249
1250 In order to stream an A/B OTA package, we need 'ota-streaming-property-files'
1251 that contains the offsets and sizes for the ZIP entries. An example
1252 property-files string is as follows.
1253
1254 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379"
1255
1256 OTA server can pass down this string, in addition to the package URL, to the
1257 system update client. System update client can then fetch individual ZIP
1258 entries (ZIP_STORED) directly at the given offset of the URL.
1259
1260 Args:
1261 metadata: The metadata dict for the package.
1262 input_file: The input ZIP filename that doesn't contain the package METADATA
1263 entry yet.
1264 output_file: The final output ZIP filename.
Tao Baod3fc38a2018-03-08 16:09:01 -08001265 needed_property_files: The list of PropertyFiles' to be generated.
Tao Baofe5b69a2018-03-02 09:47:43 -08001266 """
Tao Baofe5b69a2018-03-02 09:47:43 -08001267
Tao Baod2ce2ed2018-03-16 12:59:42 -07001268 def ComputeAllPropertyFiles(input_file, needed_property_files):
1269 # Write the current metadata entry with placeholders.
1270 with zipfile.ZipFile(input_file) as input_zip:
1271 for property_files in needed_property_files:
1272 metadata[property_files.name] = property_files.Compute(input_zip)
1273 namelist = input_zip.namelist()
Tao Baofe5b69a2018-03-02 09:47:43 -08001274
Tao Baod2ce2ed2018-03-16 12:59:42 -07001275 if METADATA_NAME in namelist:
1276 common.ZipDelete(input_file, METADATA_NAME)
1277 output_zip = zipfile.ZipFile(input_file, 'a')
1278 WriteMetadata(metadata, output_zip)
1279 common.ZipClose(output_zip)
1280
1281 if OPTIONS.no_signing:
1282 return input_file
1283
Tao Bao491d7e22018-02-21 13:17:22 -08001284 prelim_signing = common.MakeTempFile(suffix='.zip')
1285 SignOutput(input_file, prelim_signing)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001286 return prelim_signing
Tao Baofe5b69a2018-03-02 09:47:43 -08001287
Tao Baod2ce2ed2018-03-16 12:59:42 -07001288 def FinalizeAllPropertyFiles(prelim_signing, needed_property_files):
1289 with zipfile.ZipFile(prelim_signing) as prelim_signing_zip:
1290 for property_files in needed_property_files:
1291 metadata[property_files.name] = property_files.Finalize(
1292 prelim_signing_zip, len(metadata[property_files.name]))
1293
1294 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP
1295 # entries, as well as padding the entry headers. We do a preliminary signing
1296 # (with an incomplete metadata entry) to allow that to happen. Then compute
1297 # the ZIP entry offsets, write back the final metadata and do the final
1298 # signing.
1299 prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files)
1300 try:
1301 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
1302 except PropertyFiles.InsufficientSpaceException:
1303 # Even with the preliminary signing, the entry orders may change
1304 # dramatically, which leads to insufficiently reserved space during the
1305 # first call to ComputeAllPropertyFiles(). In that case, we redo all the
1306 # preliminary signing works, based on the already ordered ZIP entries, to
1307 # address the issue.
1308 prelim_signing = ComputeAllPropertyFiles(
1309 prelim_signing, needed_property_files)
1310 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
Tao Baofe5b69a2018-03-02 09:47:43 -08001311
1312 # Replace the METADATA entry.
1313 common.ZipDelete(prelim_signing, METADATA_NAME)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001314 output_zip = zipfile.ZipFile(prelim_signing, 'a')
Tao Baofe5b69a2018-03-02 09:47:43 -08001315 WriteMetadata(metadata, output_zip)
1316 common.ZipClose(output_zip)
1317
1318 # Re-sign the package after updating the metadata entry.
Tao Bao491d7e22018-02-21 13:17:22 -08001319 if OPTIONS.no_signing:
1320 output_file = prelim_signing
1321 else:
1322 SignOutput(prelim_signing, output_file)
Tao Baofe5b69a2018-03-02 09:47:43 -08001323
1324 # Reopen the final signed zip to double check the streaming metadata.
Tao Baod2ce2ed2018-03-16 12:59:42 -07001325 with zipfile.ZipFile(output_file) as output_zip:
Tao Baod3fc38a2018-03-08 16:09:01 -08001326 for property_files in needed_property_files:
1327 property_files.Verify(output_zip, metadata[property_files.name].strip())
Tao Baofe5b69a2018-03-02 09:47:43 -08001328
xunchang1cfe2512019-02-19 14:14:48 -08001329 # If requested, dump the metadata to a separate file.
1330 output_metadata_path = OPTIONS.output_metadata_path
1331 if output_metadata_path:
1332 WriteMetadata(metadata, output_metadata_path)
1333
Tao Baofe5b69a2018-03-02 09:47:43 -08001334
Tao Bao491d7e22018-02-21 13:17:22 -08001335def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
Tao Bao1c320f82019-10-04 23:25:12 -07001336 target_info = common.BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1337 source_info = common.BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -08001338
Tao Bao481bab82017-12-21 11:23:09 -08001339 target_api_version = target_info["recovery_api_version"]
1340 source_api_version = source_info["recovery_api_version"]
1341 if source_api_version == 0:
Tao Bao32fcdab2018-10-12 10:30:39 -07001342 logger.warning(
1343 "Generating edify script for a source that can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001344
Tao Bao481bab82017-12-21 11:23:09 -08001345 script = edify_generator.EdifyGenerator(
1346 source_api_version, target_info, fstab=source_info["fstab"])
1347
1348 if target_info.oem_props or source_info.oem_props:
1349 if not OPTIONS.oem_no_mount:
1350 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -07001351
Tao Baodf3a48b2018-01-10 16:30:43 -08001352 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -08001353
Tao Bao491d7e22018-02-21 13:17:22 -08001354 if not OPTIONS.no_signing:
1355 staging_file = common.MakeTempFile(suffix='.zip')
1356 else:
1357 staging_file = output_file
1358
1359 output_zip = zipfile.ZipFile(
1360 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
1361
Geremy Condra36bd3652014-02-06 19:45:10 -08001362 device_specific = common.DeviceSpecificParams(
1363 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001364 source_version=source_api_version,
Yifan Hong8a66a712019-04-04 15:37:57 -07001365 source_tmp=OPTIONS.source_tmp,
Geremy Condra36bd3652014-02-06 19:45:10 -08001366 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001367 target_version=target_api_version,
Yifan Hong8a66a712019-04-04 15:37:57 -07001368 target_tmp=OPTIONS.target_tmp,
Geremy Condra36bd3652014-02-06 19:45:10 -08001369 output_zip=output_zip,
1370 script=script,
1371 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -08001372 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001373
Geremy Condra36bd3652014-02-06 19:45:10 -08001374 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001375 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001376 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001377 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001378 updating_boot = (not OPTIONS.two_step and
1379 (source_boot.data != target_boot.data))
1380
Geremy Condra36bd3652014-02-06 19:45:10 -08001381 target_recovery = common.GetBootableImage(
1382 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -08001383
Tianjie Xuf67dd802019-05-20 17:50:36 -07001384 block_diff_dict = GetBlockDifferences(target_zip=target_zip,
1385 source_zip=source_zip,
1386 target_info=target_info,
1387 source_info=source_info,
1388 device_specific=device_specific)
Geremy Condra36bd3652014-02-06 19:45:10 -08001389
Yifan Hong9276cf02019-08-21 16:37:04 -07001390 CheckVintfIfTrebleEnabled(OPTIONS.target_tmp, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001391
Tao Bao481bab82017-12-21 11:23:09 -08001392 # Assertions (e.g. device properties check).
1393 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -08001394 device_specific.IncrementalOTA_Assertions()
1395
1396 # Two-step incremental package strategy (in chronological order,
1397 # which is *not* the order in which the generated script has
1398 # things):
1399 #
1400 # if stage is not "2/3" or "3/3":
1401 # do verification on current system
1402 # write recovery image to boot partition
1403 # set stage to "2/3"
1404 # reboot to boot partition and restart recovery
1405 # else if stage is "2/3":
1406 # write recovery image to recovery partition
1407 # set stage to "3/3"
1408 # reboot to recovery partition and restart recovery
1409 # else:
1410 # (stage must be "3/3")
1411 # perform update:
1412 # patch system files, etc.
1413 # force full install of new boot image
1414 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001415 # complete script normally
1416 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001417
1418 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001419 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001420 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001421 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001422 assert fs.fs_type.upper() == "EMMC", \
1423 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001424 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001425 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1426 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001427if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001428""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001429
1430 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1431 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001432 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001433 script.WriteRawImage("/recovery", "recovery.img")
1434 script.AppendExtra("""
1435set_stage("%(bcb_dev)s", "3/3");
1436reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001437else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001438""" % bcb_dev)
1439
Tao Baod42e97e2016-11-30 12:11:57 -08001440 # Stage 1/3: (a) Verify the current system.
1441 script.Comment("Stage 1/3")
1442
Tao Bao6c55a8a2015-04-08 15:30:27 -07001443 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001444 script.Print("Source: {}".format(source_info.fingerprint))
1445 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001446
Geremy Condra36bd3652014-02-06 19:45:10 -08001447 script.Print("Verifying current system...")
1448
1449 device_specific.IncrementalOTA_VerifyBegin()
1450
Tao Bao481bab82017-12-21 11:23:09 -08001451 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001452
Tao Baod8d14be2016-02-04 14:26:02 -08001453 # Check the required cache size (i.e. stashed blocks).
Tianjie Xuf67dd802019-05-20 17:50:36 -07001454 required_cache_sizes = [diff.required_cache for diff in
1455 block_diff_dict.values()]
Geremy Condra36bd3652014-02-06 19:45:10 -08001456 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -08001457 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001458 d = common.Difference(target_boot, source_boot)
1459 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001460 if d is None:
1461 include_full_boot = True
1462 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1463 else:
1464 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001465
Tao Bao32fcdab2018-10-12 10:30:39 -07001466 logger.info(
1467 "boot target: %d source: %d diff: %d", target_boot.size,
1468 source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -08001469
Tao Bao51216552018-08-26 11:53:15 -07001470 common.ZipWriteStr(output_zip, "boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001471
Tao Bao51216552018-08-26 11:53:15 -07001472 script.PatchPartitionCheck(
1473 "{}:{}:{}:{}".format(
1474 boot_type, boot_device, target_boot.size, target_boot.sha1),
1475 "{}:{}:{}:{}".format(
1476 boot_type, boot_device, source_boot.size, source_boot.sha1))
1477
Tianjie Xuf67dd802019-05-20 17:50:36 -07001478 required_cache_sizes.append(target_boot.size)
Tao Baod8d14be2016-02-04 14:26:02 -08001479
Tianjie Xuf67dd802019-05-20 17:50:36 -07001480 if required_cache_sizes:
1481 script.CacheFreeSpaceCheck(max(required_cache_sizes))
1482
1483 # Verify the existing partitions.
1484 for diff in block_diff_dict.values():
1485 diff.WriteVerifyScript(script, touched_blocks_only=True)
Geremy Condra36bd3652014-02-06 19:45:10 -08001486
1487 device_specific.IncrementalOTA_VerifyEnd()
1488
1489 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001490 # Stage 1/3: (b) Write recovery image to /boot.
1491 _WriteRecoveryImageToBoot(script, output_zip)
1492
Geremy Condra36bd3652014-02-06 19:45:10 -08001493 script.AppendExtra("""
1494set_stage("%(bcb_dev)s", "2/3");
1495reboot_now("%(bcb_dev)s", "");
1496else
1497""" % bcb_dev)
1498
Tao Baod42e97e2016-11-30 12:11:57 -08001499 # Stage 3/3: Make changes.
1500 script.Comment("Stage 3/3")
1501
Geremy Condra36bd3652014-02-06 19:45:10 -08001502 script.Comment("---- start making changes here ----")
1503
1504 device_specific.IncrementalOTA_InstallBegin()
1505
Tianjie Xuf67dd802019-05-20 17:50:36 -07001506 progress_dict = {partition: 0.1 for partition in block_diff_dict}
1507 progress_dict["system"] = 1 - len(block_diff_dict) * 0.1
Yifan Hong10c530d2018-12-27 17:34:18 -08001508
1509 if OPTIONS.source_info_dict.get("use_dynamic_partitions") == "true":
1510 if OPTIONS.target_info_dict.get("use_dynamic_partitions") != "true":
1511 raise RuntimeError(
1512 "can't generate incremental that disables dynamic partitions")
1513 dynamic_partitions_diff = common.DynamicPartitionsDifference(
1514 info_dict=OPTIONS.target_info_dict,
1515 source_info_dict=OPTIONS.source_info_dict,
Tianjie Xuf67dd802019-05-20 17:50:36 -07001516 block_diffs=block_diff_dict.values(),
Yifan Hong10c530d2018-12-27 17:34:18 -08001517 progress_dict=progress_dict)
1518 dynamic_partitions_diff.WriteScript(
1519 script, output_zip, write_verify_script=OPTIONS.verify)
1520 else:
Tianjie Xuf67dd802019-05-20 17:50:36 -07001521 for block_diff in block_diff_dict.values():
Yifan Hong10c530d2018-12-27 17:34:18 -08001522 block_diff.WriteScript(script, output_zip,
1523 progress=progress_dict.get(block_diff.partition),
1524 write_verify_script=OPTIONS.verify)
Geremy Condra36bd3652014-02-06 19:45:10 -08001525
1526 if OPTIONS.two_step:
1527 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1528 script.WriteRawImage("/boot", "boot.img")
Tao Bao32fcdab2018-10-12 10:30:39 -07001529 logger.info("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001530
1531 if not OPTIONS.two_step:
1532 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001533 if include_full_boot:
Tao Bao32fcdab2018-10-12 10:30:39 -07001534 logger.info("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001535 script.Print("Installing boot image...")
1536 script.WriteRawImage("/boot", "boot.img")
1537 else:
1538 # Produce the boot image by applying a patch to the current
1539 # contents of the boot partition, and write it back to the
1540 # partition.
Tao Bao32fcdab2018-10-12 10:30:39 -07001541 logger.info("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001542 script.Print("Patching boot image...")
1543 script.ShowProgress(0.1, 10)
Tao Bao51216552018-08-26 11:53:15 -07001544 script.PatchPartition(
1545 '{}:{}:{}:{}'.format(
1546 boot_type, boot_device, target_boot.size, target_boot.sha1),
1547 '{}:{}:{}:{}'.format(
1548 boot_type, boot_device, source_boot.size, source_boot.sha1),
1549 'boot.img.p')
Geremy Condra36bd3652014-02-06 19:45:10 -08001550 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001551 logger.info("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001552
1553 # Do device-specific installation (eg, write radio image).
1554 device_specific.IncrementalOTA_InstallEnd()
1555
1556 if OPTIONS.extra_script is not None:
1557 script.AppendExtra(OPTIONS.extra_script)
1558
Doug Zongker922206e2014-03-04 13:16:24 -08001559 if OPTIONS.wipe_user_data:
1560 script.Print("Erasing user data...")
1561 script.FormatPartition("/data")
1562
Geremy Condra36bd3652014-02-06 19:45:10 -08001563 if OPTIONS.two_step:
1564 script.AppendExtra("""
1565set_stage("%(bcb_dev)s", "");
1566endif;
1567endif;
1568""" % bcb_dev)
1569
1570 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001571 # For downgrade OTAs, we prefer to use the update-binary in the source
1572 # build that is actually newer than the one in the target build.
1573 if OPTIONS.downgrade:
1574 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1575 else:
1576 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001577 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001578
1579 # We haven't written the metadata entry yet, which will be handled in
1580 # FinalizeMetadata().
1581 common.ZipClose(output_zip)
1582
1583 # Sign the generated zip package unless no_signing is specified.
1584 needed_property_files = (
1585 NonAbOtaPropertyFiles(),
1586 )
1587 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Geremy Condra36bd3652014-02-06 19:45:10 -08001588
Doug Zongker32b527d2014-03-04 10:03:02 -08001589
Tao Bao15a146a2018-02-21 16:06:59 -08001590def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
Tao Baof7140c02018-01-30 17:09:24 -08001591 """Returns a target-files.zip file for generating secondary payload.
1592
1593 Although the original target-files.zip already contains secondary slot
1594 images (i.e. IMAGES/system_other.img), we need to rename the files to the
1595 ones without _other suffix. Note that we cannot instead modify the names in
1596 META/ab_partitions.txt, because there are no matching partitions on device.
1597
1598 For the partitions that don't have secondary images, the ones for primary
1599 slot will be used. This is to ensure that we always have valid boot, vbmeta,
1600 bootloader images in the inactive slot.
1601
1602 Args:
1603 input_file: The input target-files.zip file.
Tao Bao15a146a2018-02-21 16:06:59 -08001604 skip_postinstall: Whether to skip copying the postinstall config file.
Tao Baof7140c02018-01-30 17:09:24 -08001605
1606 Returns:
1607 The filename of the target-files.zip for generating secondary payload.
1608 """
Tianjie Xu1c808002019-09-11 00:29:26 -07001609
1610 def GetInfoForSecondaryImages(info_file):
1611 """Updates info file for secondary payload generation.
1612
1613 Scan each line in the info file, and remove the unwanted partitions from
1614 the dynamic partition list in the related properties. e.g.
1615 "super_google_dynamic_partitions_partition_list=system vendor product"
1616 will become "super_google_dynamic_partitions_partition_list=system".
1617
1618 Args:
1619 info_file: The input info file. e.g. misc_info.txt.
1620
1621 Returns:
1622 A string of the updated info content.
1623 """
1624
1625 output_list = []
1626 with open(info_file) as f:
1627 lines = f.read().splitlines()
1628
1629 # The suffix in partition_list variables that follows the name of the
1630 # partition group.
1631 LIST_SUFFIX = 'partition_list'
1632 for line in lines:
1633 if line.startswith('#') or '=' not in line:
1634 output_list.append(line)
1635 continue
1636 key, value = line.strip().split('=', 1)
1637 if key == 'dynamic_partition_list' or key.endswith(LIST_SUFFIX):
1638 partitions = value.split()
1639 partitions = [partition for partition in partitions if partition
Tao Bao3e759462019-09-17 22:43:11 -07001640 not in SECONDARY_PAYLOAD_SKIPPED_IMAGES]
Tianjie Xu1c808002019-09-11 00:29:26 -07001641 output_list.append('{}={}'.format(key, ' '.join(partitions)))
Yifan Hongfe073432019-11-01 12:28:31 -07001642 elif key == 'virtual_ab' or key == "virtual_ab_retrofit":
1643 # Remove virtual_ab flag from secondary payload so that OTA client
1644 # don't use snapshots for secondary update
1645 pass
Tianjie Xu1c808002019-09-11 00:29:26 -07001646 else:
1647 output_list.append(line)
1648 return '\n'.join(output_list)
1649
Tao Baof7140c02018-01-30 17:09:24 -08001650 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1651 target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
1652
Tao Baodba59ee2018-01-09 13:21:02 -08001653 with zipfile.ZipFile(input_file, 'r') as input_zip:
1654 infolist = input_zip.infolist()
Tao Bao12489802018-07-12 14:47:38 -07001655
Tao Bao0ff15de2019-03-20 11:26:06 -07001656 input_tmp = common.UnzipTemp(input_file, UNZIP_PATTERN)
Tao Baodba59ee2018-01-09 13:21:02 -08001657 for info in infolist:
Tao Baof7140c02018-01-30 17:09:24 -08001658 unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
1659 if info.filename == 'IMAGES/system_other.img':
1660 common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
1661
1662 # Primary images and friends need to be skipped explicitly.
1663 elif info.filename in ('IMAGES/system.img',
1664 'IMAGES/system.map'):
1665 pass
Tao Bao3e759462019-09-17 22:43:11 -07001666
1667 # Copy images that are not in SECONDARY_PAYLOAD_SKIPPED_IMAGES.
1668 elif info.filename.startswith(('IMAGES/', 'RADIO/')):
1669 image_name = os.path.basename(info.filename)
1670 if image_name not in ['{}.img'.format(partition) for partition in
1671 SECONDARY_PAYLOAD_SKIPPED_IMAGES]:
1672 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
Tao Baof7140c02018-01-30 17:09:24 -08001673
Tao Bao15a146a2018-02-21 16:06:59 -08001674 # Skip copying the postinstall config if requested.
1675 elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
1676 pass
1677
Tianjie Xu1c808002019-09-11 00:29:26 -07001678 elif info.filename.startswith('META/'):
1679 # Remove the unnecessary partitions for secondary images from the
1680 # ab_partitions file.
1681 if info.filename == AB_PARTITIONS:
1682 with open(unzipped_file) as f:
1683 partition_list = f.read().splitlines()
1684 partition_list = [partition for partition in partition_list if partition
Tao Bao3e759462019-09-17 22:43:11 -07001685 and partition not in SECONDARY_PAYLOAD_SKIPPED_IMAGES]
Tianjie Xu1c808002019-09-11 00:29:26 -07001686 common.ZipWriteStr(target_zip, info.filename, '\n'.join(partition_list))
1687 # Remove the unnecessary partitions from the dynamic partitions list.
1688 elif (info.filename == 'META/misc_info.txt' or
1689 info.filename == DYNAMIC_PARTITION_INFO):
1690 modified_info = GetInfoForSecondaryImages(unzipped_file)
1691 common.ZipWriteStr(target_zip, info.filename, modified_info)
1692 else:
1693 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
Tao Baof7140c02018-01-30 17:09:24 -08001694
Tao Baof7140c02018-01-30 17:09:24 -08001695 common.ZipClose(target_zip)
1696
1697 return target_file
1698
1699
Tao Bao15a146a2018-02-21 16:06:59 -08001700def GetTargetFilesZipWithoutPostinstallConfig(input_file):
1701 """Returns a target-files.zip that's not containing postinstall_config.txt.
1702
1703 This allows brillo_update_payload script to skip writing all the postinstall
1704 hooks in the generated payload. The input target-files.zip file will be
1705 duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
1706 contain the postinstall_config.txt entry, the input file will be returned.
1707
1708 Args:
1709 input_file: The input target-files.zip filename.
1710
1711 Returns:
1712 The filename of target-files.zip that doesn't contain postinstall config.
1713 """
1714 # We should only make a copy if postinstall_config entry exists.
1715 with zipfile.ZipFile(input_file, 'r') as input_zip:
1716 if POSTINSTALL_CONFIG not in input_zip.namelist():
1717 return input_file
1718
1719 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1720 shutil.copyfile(input_file, target_file)
1721 common.ZipDelete(target_file, POSTINSTALL_CONFIG)
1722 return target_file
1723
1724
Yifan Hong50e79542018-11-08 17:44:12 -08001725def GetTargetFilesZipForRetrofitDynamicPartitions(input_file,
Yifan Hongb433eba2019-03-06 12:42:53 -08001726 super_block_devices,
1727 dynamic_partition_list):
Yifan Hong50e79542018-11-08 17:44:12 -08001728 """Returns a target-files.zip for retrofitting dynamic partitions.
1729
1730 This allows brillo_update_payload to generate an OTA based on the exact
1731 bits on the block devices. Postinstall is disabled.
1732
1733 Args:
1734 input_file: The input target-files.zip filename.
1735 super_block_devices: The list of super block devices
Yifan Hongb433eba2019-03-06 12:42:53 -08001736 dynamic_partition_list: The list of dynamic partitions
Yifan Hong50e79542018-11-08 17:44:12 -08001737
1738 Returns:
1739 The filename of target-files.zip with *.img replaced with super_*.img for
1740 each block device in super_block_devices.
1741 """
1742 assert super_block_devices, "No super_block_devices are specified."
1743
1744 replace = {'OTA/super_{}.img'.format(dev): 'IMAGES/{}.img'.format(dev)
Tao Bao03fecb62018-11-28 10:59:23 -08001745 for dev in super_block_devices}
Yifan Hong50e79542018-11-08 17:44:12 -08001746
1747 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1748 shutil.copyfile(input_file, target_file)
1749
Tao Baoa3705452019-06-24 15:33:41 -07001750 with zipfile.ZipFile(input_file) as input_zip:
Yifan Hong50e79542018-11-08 17:44:12 -08001751 namelist = input_zip.namelist()
1752
Yifan Hongb433eba2019-03-06 12:42:53 -08001753 input_tmp = common.UnzipTemp(input_file, RETROFIT_DAP_UNZIP_PATTERN)
1754
1755 # Remove partitions from META/ab_partitions.txt that is in
1756 # dynamic_partition_list but not in super_block_devices so that
1757 # brillo_update_payload won't generate update for those logical partitions.
1758 ab_partitions_file = os.path.join(input_tmp, *AB_PARTITIONS.split('/'))
1759 with open(ab_partitions_file) as f:
1760 ab_partitions_lines = f.readlines()
1761 ab_partitions = [line.strip() for line in ab_partitions_lines]
1762 # Assert that all super_block_devices are in ab_partitions
1763 super_device_not_updated = [partition for partition in super_block_devices
1764 if partition not in ab_partitions]
1765 assert not super_device_not_updated, \
1766 "{} is in super_block_devices but not in {}".format(
1767 super_device_not_updated, AB_PARTITIONS)
1768 # ab_partitions -= (dynamic_partition_list - super_block_devices)
1769 new_ab_partitions = common.MakeTempFile(prefix="ab_partitions", suffix=".txt")
1770 with open(new_ab_partitions, 'w') as f:
1771 for partition in ab_partitions:
1772 if (partition in dynamic_partition_list and
1773 partition not in super_block_devices):
Tao Bao59cf0c52019-06-25 10:04:24 -07001774 logger.info("Dropping %s from ab_partitions.txt", partition)
1775 continue
Yifan Hongb433eba2019-03-06 12:42:53 -08001776 f.write(partition + "\n")
1777 to_delete = [AB_PARTITIONS]
1778
Yifan Hong50e79542018-11-08 17:44:12 -08001779 # Always skip postinstall for a retrofit update.
Yifan Hongb433eba2019-03-06 12:42:53 -08001780 to_delete += [POSTINSTALL_CONFIG]
Yifan Hong50e79542018-11-08 17:44:12 -08001781
1782 # Delete dynamic_partitions_info.txt so that brillo_update_payload thinks this
1783 # is a regular update on devices without dynamic partitions support.
1784 to_delete += [DYNAMIC_PARTITION_INFO]
1785
Tao Bao03fecb62018-11-28 10:59:23 -08001786 # Remove the existing partition images as well as the map files.
Tao Bao59cf0c52019-06-25 10:04:24 -07001787 to_delete += list(replace.values())
Tao Bao03fecb62018-11-28 10:59:23 -08001788 to_delete += ['IMAGES/{}.map'.format(dev) for dev in super_block_devices]
Yifan Hong50e79542018-11-08 17:44:12 -08001789
1790 common.ZipDelete(target_file, to_delete)
1791
Yifan Hong50e79542018-11-08 17:44:12 -08001792 target_zip = zipfile.ZipFile(target_file, 'a', allowZip64=True)
1793
1794 # Write super_{foo}.img as {foo}.img.
1795 for src, dst in replace.items():
1796 assert src in namelist, \
Tao Bao59cf0c52019-06-25 10:04:24 -07001797 'Missing {} in {}; {} cannot be written'.format(src, input_file, dst)
Yifan Hong50e79542018-11-08 17:44:12 -08001798 unzipped_file = os.path.join(input_tmp, *src.split('/'))
1799 common.ZipWrite(target_zip, unzipped_file, arcname=dst)
1800
Yifan Hongb433eba2019-03-06 12:42:53 -08001801 # Write new ab_partitions.txt file
1802 common.ZipWrite(target_zip, new_ab_partitions, arcname=AB_PARTITIONS)
1803
Yifan Hong50e79542018-11-08 17:44:12 -08001804 common.ZipClose(target_zip)
1805
1806 return target_file
1807
1808
Tao Baof0c4aa22018-04-30 20:29:30 -07001809def GenerateAbOtaPackage(target_file, output_file, source_file=None):
Tao Baofe5b69a2018-03-02 09:47:43 -08001810 """Generates an Android OTA package that has A/B update payload."""
Tao Baodea0f8b2016-06-20 17:55:06 -07001811 # Stage the output zip package for package signing.
Tao Bao491d7e22018-02-21 13:17:22 -08001812 if not OPTIONS.no_signing:
1813 staging_file = common.MakeTempFile(suffix='.zip')
1814 else:
1815 staging_file = output_file
Tao Baoa652c002018-03-01 19:31:38 -08001816 output_zip = zipfile.ZipFile(staging_file, "w",
Tao Baoc098e9e2016-01-07 13:03:56 -08001817 compression=zipfile.ZIP_DEFLATED)
1818
Tao Bao481bab82017-12-21 11:23:09 -08001819 if source_file is not None:
Tao Bao1c320f82019-10-04 23:25:12 -07001820 target_info = common.BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1821 source_info = common.BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Tao Bao481bab82017-12-21 11:23:09 -08001822 else:
Tao Bao1c320f82019-10-04 23:25:12 -07001823 target_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Tao Bao481bab82017-12-21 11:23:09 -08001824 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001825
Tao Bao481bab82017-12-21 11:23:09 -08001826 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001827 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001828
Yifan Hong50e79542018-11-08 17:44:12 -08001829 if OPTIONS.retrofit_dynamic_partitions:
1830 target_file = GetTargetFilesZipForRetrofitDynamicPartitions(
Yifan Hongb433eba2019-03-06 12:42:53 -08001831 target_file, target_info.get("super_block_devices").strip().split(),
1832 target_info.get("dynamic_partition_list").strip().split())
Yifan Hong50e79542018-11-08 17:44:12 -08001833 elif OPTIONS.skip_postinstall:
Tao Bao15a146a2018-02-21 16:06:59 -08001834 target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
1835
Tao Bao40b18822018-01-30 18:19:04 -08001836 # Generate payload.
1837 payload = Payload()
1838
1839 # Enforce a max timestamp this payload can be applied on top of.
Tao Baoff1b86e2017-10-03 14:17:57 -07001840 if OPTIONS.downgrade:
Tao Bao2a12ed72018-01-22 11:35:00 -08001841 max_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baoff1b86e2017-10-03 14:17:57 -07001842 else:
1843 max_timestamp = metadata["post-timestamp"]
Tao Bao40b18822018-01-30 18:19:04 -08001844 additional_args = ["--max_timestamp", max_timestamp]
Tao Baoc098e9e2016-01-07 13:03:56 -08001845
Tao Bao40b18822018-01-30 18:19:04 -08001846 payload.Generate(target_file, source_file, additional_args)
Tao Baoc098e9e2016-01-07 13:03:56 -08001847
Tao Bao40b18822018-01-30 18:19:04 -08001848 # Sign the payload.
Tao Baof7140c02018-01-30 17:09:24 -08001849 payload_signer = PayloadSigner()
1850 payload.Sign(payload_signer)
Tao Baoc098e9e2016-01-07 13:03:56 -08001851
Tao Bao40b18822018-01-30 18:19:04 -08001852 # Write the payload into output zip.
1853 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001854
Tao Baof7140c02018-01-30 17:09:24 -08001855 # Generate and include the secondary payload that installs secondary images
1856 # (e.g. system_other.img).
1857 if OPTIONS.include_secondary:
1858 # We always include a full payload for the secondary slot, even when
1859 # building an incremental OTA. See the comments for "--include_secondary".
Tao Bao15a146a2018-02-21 16:06:59 -08001860 secondary_target_file = GetTargetFilesZipForSecondaryImages(
1861 target_file, OPTIONS.skip_postinstall)
Tao Bao667ff572018-02-10 00:02:40 -08001862 secondary_payload = Payload(secondary=True)
Tao Baodb1fe412018-02-09 23:15:05 -08001863 secondary_payload.Generate(secondary_target_file,
1864 additional_args=additional_args)
Tao Baof7140c02018-01-30 17:09:24 -08001865 secondary_payload.Sign(payload_signer)
Tao Bao667ff572018-02-10 00:02:40 -08001866 secondary_payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08001867
Tianjie Xucfa86222016-03-07 16:31:19 -08001868 # If dm-verity is supported for the device, copy contents of care_map
1869 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001870 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08001871 if (target_info.get("verity") == "true" or
1872 target_info.get("avb_enable") == "true"):
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001873 care_map_list = [x for x in ["care_map.pb", "care_map.txt"] if
1874 "META/" + x in target_zip.namelist()]
1875
1876 # Adds care_map if either the protobuf format or the plain text one exists.
1877 if care_map_list:
1878 care_map_name = care_map_list[0]
1879 care_map_data = target_zip.read("META/" + care_map_name)
1880 # In order to support streaming, care_map needs to be packed as
Tao Bao40b18822018-01-30 18:19:04 -08001881 # ZIP_STORED.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001882 common.ZipWriteStr(output_zip, care_map_name, care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08001883 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001884 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001885 logger.warning("Cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07001886
Tao Bao21803d32017-04-19 10:16:09 -07001887 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001888
Yifan Hong9276cf02019-08-21 16:37:04 -07001889 CheckVintfIfTrebleEnabled(target_file, target_info)
1890
Tao Baofe5b69a2018-03-02 09:47:43 -08001891 # We haven't written the metadata entry yet, which will be handled in
1892 # FinalizeMetadata().
Tao Baoc96316c2017-01-24 22:10:49 -08001893 common.ZipClose(output_zip)
1894
Tao Bao85f16982018-03-08 16:28:33 -08001895 # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers
1896 # all the info of the latter. However, system updaters and OTA servers need to
1897 # take time to switch to the new flag. We keep both of the flags for
1898 # P-timeframe, and will remove StreamingPropertyFiles in later release.
Tao Baod3fc38a2018-03-08 16:09:01 -08001899 needed_property_files = (
Tao Bao85f16982018-03-08 16:28:33 -08001900 AbOtaPropertyFiles(),
Tao Baod3fc38a2018-03-08 16:09:01 -08001901 StreamingPropertyFiles(),
1902 )
1903 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Tao Baoc96316c2017-01-24 22:10:49 -08001904
Tao Baoc098e9e2016-01-07 13:03:56 -08001905
Tao Baof0c4aa22018-04-30 20:29:30 -07001906def GenerateNonAbOtaPackage(target_file, output_file, source_file=None):
1907 """Generates a non-A/B OTA package."""
1908 # Sanity check the loaded info dicts first.
1909 if OPTIONS.info_dict.get("no_recovery") == "true":
1910 raise common.ExternalError(
1911 "--- target build has specified no recovery ---")
1912
1913 # Non-A/B OTAs rely on /cache partition to store temporary files.
1914 cache_size = OPTIONS.info_dict.get("cache_size")
1915 if cache_size is None:
1916 logger.warning("--- can't determine the cache partition size ---")
1917 OPTIONS.cache_size = cache_size
1918
1919 if OPTIONS.extra_script is not None:
1920 with open(OPTIONS.extra_script) as fp:
1921 OPTIONS.extra_script = fp.read()
1922
1923 if OPTIONS.extracted_input is not None:
1924 OPTIONS.input_tmp = OPTIONS.extracted_input
1925 else:
1926 logger.info("unzipping target target-files...")
1927 OPTIONS.input_tmp = common.UnzipTemp(target_file, UNZIP_PATTERN)
1928 OPTIONS.target_tmp = OPTIONS.input_tmp
1929
1930 # If the caller explicitly specified the device-specific extensions path via
1931 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
1932 # is present in the target target_files. Otherwise, take the path of the file
1933 # from 'tool_extensions' in the info dict and look for that in the local
1934 # filesystem, relative to the current directory.
1935 if OPTIONS.device_specific is None:
1936 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1937 if os.path.exists(from_input):
1938 logger.info("(using device-specific extensions from target_files)")
1939 OPTIONS.device_specific = from_input
1940 else:
1941 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
1942
1943 if OPTIONS.device_specific is not None:
1944 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
1945
1946 # Generate a full OTA.
1947 if source_file is None:
1948 with zipfile.ZipFile(target_file) as input_zip:
1949 WriteFullOTAPackage(
1950 input_zip,
1951 output_file)
1952
1953 # Generate an incremental OTA.
1954 else:
1955 logger.info("unzipping source target-files...")
1956 OPTIONS.source_tmp = common.UnzipTemp(
1957 OPTIONS.incremental_source, UNZIP_PATTERN)
1958 with zipfile.ZipFile(target_file) as input_zip, \
1959 zipfile.ZipFile(source_file) as source_zip:
1960 WriteBlockIncrementalOTAPackage(
1961 input_zip,
1962 source_zip,
1963 output_file)
1964
1965
Tianjie Xu9afb2212020-05-10 21:48:15 +00001966def CalculateRuntimeFingerprints():
1967 """Returns a set of runtime fingerprints based on the boot variables."""
1968
1969 build_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
1970 fingerprints = {build_info.fingerprint}
1971
1972 if not OPTIONS.boot_variable_values:
1973 return fingerprints
1974
1975 # Calculate all possible combinations of the values for the boot variables.
1976 keys = OPTIONS.boot_variable_values.keys()
1977 value_list = OPTIONS.boot_variable_values.values()
1978 combinations = [dict(zip(keys, values))
1979 for values in itertools.product(*value_list)]
1980 for placeholder_values in combinations:
1981 # Reload the info_dict as some build properties may change their values
1982 # based on the value of ro.boot* properties.
1983 info_dict = copy.deepcopy(OPTIONS.info_dict)
1984 for partition in common.PARTITIONS_WITH_CARE_MAP:
1985 partition_prop_key = "{}.build.prop".format(partition)
1986 old_props = info_dict[partition_prop_key]
1987 info_dict[partition_prop_key] = common.PartitionBuildProps.FromInputFile(
1988 old_props.input_file, partition, placeholder_values)
1989 info_dict["build.prop"] = info_dict["system.build.prop"]
1990
1991 build_info = common.BuildInfo(info_dict, OPTIONS.oem_dicts)
1992 fingerprints.add(build_info.fingerprint)
1993 return fingerprints
1994
1995
Doug Zongkereef39442009-04-02 12:14:19 -07001996def main(argv):
1997
1998 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07001999 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07002000 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07002001 elif o in ("-i", "--incremental_from"):
2002 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07002003 elif o == "--full_radio":
2004 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07002005 elif o == "--full_bootloader":
2006 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08002007 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002008 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08002009 elif o == "--downgrade":
2010 OPTIONS.downgrade = True
2011 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08002012 elif o == "--override_timestamp":
Tao Baofaa8e0b2018-04-12 14:31:43 -07002013 OPTIONS.downgrade = True
Michael Runge6e836112014-04-15 17:40:21 -07002014 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08002015 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08002016 elif o == "--oem_no_mount":
2017 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07002018 elif o in ("-e", "--extra_script"):
2019 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02002020 elif o in ("-t", "--worker_threads"):
2021 if a.isdigit():
2022 OPTIONS.worker_threads = int(a)
2023 else:
2024 raise ValueError("Cannot parse value %r for option %r - only "
2025 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002026 elif o in ("-2", "--two_step"):
2027 OPTIONS.two_step = True
Tao Baof7140c02018-01-30 17:09:24 -08002028 elif o == "--include_secondary":
2029 OPTIONS.include_secondary = True
Doug Zongker26e66192014-02-20 13:22:07 -08002030 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002031 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07002032 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07002033 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08002034 elif o == "--block":
2035 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002036 elif o in ("-b", "--binary"):
2037 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07002038 elif o == "--stash_threshold":
2039 try:
2040 OPTIONS.stash_threshold = float(a)
2041 except ValueError:
2042 raise ValueError("Cannot parse value %r for option %r - expecting "
2043 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08002044 elif o == "--log_diff":
2045 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002046 elif o == "--payload_signer":
2047 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002048 elif o == "--payload_signer_args":
2049 OPTIONS.payload_signer_args = shlex.split(a)
Tianjie Xu21e6deb2019-10-07 18:01:00 -07002050 elif o == "--payload_signer_maximum_signature_size":
2051 OPTIONS.payload_signer_maximum_signature_size = a
xunchang376cc7c2019-04-08 23:04:58 -07002052 elif o == "--payload_signer_key_size":
Tianjie Xu21e6deb2019-10-07 18:01:00 -07002053 # TODO(Xunchang) remove this option after cleaning up the callers.
2054 logger.warning("The option '--payload_signer_key_size' is deprecated."
2055 " Use '--payload_signer_maximum_signature_size' instead.")
2056 OPTIONS.payload_signer_maximum_signature_size = a
Dan Willemsencea5cd22017-03-21 14:44:27 -07002057 elif o == "--extracted_input_target_files":
2058 OPTIONS.extracted_input = a
Tao Bao15a146a2018-02-21 16:06:59 -08002059 elif o == "--skip_postinstall":
2060 OPTIONS.skip_postinstall = True
Yifan Hong50e79542018-11-08 17:44:12 -08002061 elif o == "--retrofit_dynamic_partitions":
2062 OPTIONS.retrofit_dynamic_partitions = True
xunchangabfa2652019-02-19 16:27:10 -08002063 elif o == "--skip_compatibility_check":
2064 OPTIONS.skip_compatibility_check = True
xunchang1cfe2512019-02-19 14:14:48 -08002065 elif o == "--output_metadata_path":
2066 OPTIONS.output_metadata_path = a
Tianjie Xu1b079832019-08-28 12:19:23 -07002067 elif o == "--disable_fec_computation":
2068 OPTIONS.disable_fec_computation = True
Doug Zongkereef39442009-04-02 12:14:19 -07002069 else:
2070 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002071 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002072
2073 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08002074 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002075 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07002076 "package_key=",
2077 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002078 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002079 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002080 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08002081 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08002082 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07002083 "extra_script=",
2084 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002085 "two_step",
Tao Baof7140c02018-01-30 17:09:24 -08002086 "include_secondary",
Dan Albert8b72aef2015-03-23 19:13:21 -07002087 "no_signing",
2088 "block",
2089 "binary=",
2090 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08002091 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002092 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07002093 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002094 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002095 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002096 "payload_signer_args=",
Tianjie Xu21e6deb2019-10-07 18:01:00 -07002097 "payload_signer_maximum_signature_size=",
xunchang376cc7c2019-04-08 23:04:58 -07002098 "payload_signer_key_size=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07002099 "extracted_input_target_files=",
Tao Bao15a146a2018-02-21 16:06:59 -08002100 "skip_postinstall",
Yifan Hong50e79542018-11-08 17:44:12 -08002101 "retrofit_dynamic_partitions",
xunchangabfa2652019-02-19 16:27:10 -08002102 "skip_compatibility_check",
xunchang1cfe2512019-02-19 14:14:48 -08002103 "output_metadata_path=",
Tianjie Xu1b079832019-08-28 12:19:23 -07002104 "disable_fec_computation",
Dan Albert8b72aef2015-03-23 19:13:21 -07002105 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002106
2107 if len(args) != 2:
2108 common.Usage(__doc__)
2109 sys.exit(1)
2110
Tao Bao32fcdab2018-10-12 10:30:39 -07002111 common.InitLogging()
2112
Tao Bao5d182562016-02-23 11:38:39 -08002113 if OPTIONS.downgrade:
Tao Bao5d182562016-02-23 11:38:39 -08002114 # We should only allow downgrading incrementals (as opposed to full).
2115 # Otherwise the device may go back from arbitrary build with this full
2116 # OTA package.
2117 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002118 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08002119
Tao Bao2db13852018-01-08 22:28:57 -08002120 # Load the build info dicts from the zip directly or the extracted input
2121 # directory. We don't need to unzip the entire target-files zips, because they
2122 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
2123 # When loading the info dicts, we don't need to provide the second parameter
2124 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
2125 # some properties with their actual paths, such as 'selinux_fc',
2126 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07002127 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08002128 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07002129 else:
Tao Bao2db13852018-01-08 22:28:57 -08002130 with zipfile.ZipFile(args[0], 'r') as input_zip:
2131 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08002132
Tao Bao32fcdab2018-10-12 10:30:39 -07002133 logger.info("--- target info ---")
2134 common.DumpInfoDict(OPTIONS.info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002135
2136 # Load the source build dict if applicable.
2137 if OPTIONS.incremental_source is not None:
2138 OPTIONS.target_info_dict = OPTIONS.info_dict
2139 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
2140 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2141
Tao Bao32fcdab2018-10-12 10:30:39 -07002142 logger.info("--- source info ---")
2143 common.DumpInfoDict(OPTIONS.source_info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002144
2145 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08002146 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
2147
Yifan Hong50e79542018-11-08 17:44:12 -08002148 # Assume retrofitting dynamic partitions when base build does not set
Yifan Hong50611032018-11-20 14:27:38 -08002149 # use_dynamic_partitions but target build does.
Yifan Hong50e79542018-11-08 17:44:12 -08002150 if (OPTIONS.source_info_dict and
Yifan Hong50611032018-11-20 14:27:38 -08002151 OPTIONS.source_info_dict.get("use_dynamic_partitions") != "true" and
2152 OPTIONS.target_info_dict.get("use_dynamic_partitions") == "true"):
Yifan Hong50e79542018-11-08 17:44:12 -08002153 if OPTIONS.target_info_dict.get("dynamic_partition_retrofit") != "true":
2154 raise common.ExternalError(
2155 "Expect to generate incremental OTA for retrofitting dynamic "
2156 "partitions, but dynamic_partition_retrofit is not set in target "
2157 "build.")
2158 logger.info("Implicitly generating retrofit incremental OTA.")
2159 OPTIONS.retrofit_dynamic_partitions = True
2160
2161 # Skip postinstall for retrofitting dynamic partitions.
2162 if OPTIONS.retrofit_dynamic_partitions:
2163 OPTIONS.skip_postinstall = True
2164
Tao Baoc098e9e2016-01-07 13:03:56 -08002165 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2166
Christian Oderf63e2cd2017-05-01 22:30:15 +02002167 # Use the default key to sign the package if not specified with package_key.
2168 # package_keys are needed on ab_updates, so always define them if an
2169 # ab_update is getting created.
2170 if not OPTIONS.no_signing or ab_update:
2171 if OPTIONS.package_key is None:
2172 OPTIONS.package_key = OPTIONS.info_dict.get(
2173 "default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -07002174 "build/make/target/product/security/testkey")
Christian Oderf63e2cd2017-05-01 22:30:15 +02002175 # Get signing keys
2176 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
2177
Tao Baoc098e9e2016-01-07 13:03:56 -08002178 if ab_update:
Tao Baof0c4aa22018-04-30 20:29:30 -07002179 GenerateAbOtaPackage(
Tao Baoc098e9e2016-01-07 13:03:56 -08002180 target_file=args[0],
2181 output_file=args[1],
2182 source_file=OPTIONS.incremental_source)
2183
Dan Willemsencea5cd22017-03-21 14:44:27 -07002184 else:
Tao Baof0c4aa22018-04-30 20:29:30 -07002185 GenerateNonAbOtaPackage(
2186 target_file=args[0],
2187 output_file=args[1],
2188 source_file=OPTIONS.incremental_source)
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002189
Tao Baof0c4aa22018-04-30 20:29:30 -07002190 # Post OTA generation works.
2191 if OPTIONS.incremental_source is not None and OPTIONS.log_diff:
2192 logger.info("Generating diff logs...")
2193 logger.info("Unzipping target-files for diffing...")
2194 target_dir = common.UnzipTemp(args[0], TARGET_DIFFING_UNZIP_PATTERN)
2195 source_dir = common.UnzipTemp(
2196 OPTIONS.incremental_source, TARGET_DIFFING_UNZIP_PATTERN)
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002197
Tao Baof0c4aa22018-04-30 20:29:30 -07002198 with open(OPTIONS.log_diff, 'w') as out_file:
2199 import target_files_diff
2200 target_files_diff.recursiveDiff(
2201 '', source_dir, target_dir, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07002202
Tao Bao32fcdab2018-10-12 10:30:39 -07002203 logger.info("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002204
2205
2206if __name__ == '__main__':
2207 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002208 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002209 main(sys.argv[1:])
Tao Bao32fcdab2018-10-12 10:30:39 -07002210 except common.ExternalError:
2211 logger.exception("\n ERROR:\n")
Doug Zongkereef39442009-04-02 12:14:19 -07002212 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002213 finally:
2214 common.Cleanup()