blob: 16d7485e045c6fb7199f14afe0028d550f05ca4b [file] [log] [blame]
Doug Zongkereef39442009-04-02 12:14:19 -07001#!/usr/bin/env python
2#
3# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""
Tao Bao30df8b42018-04-23 15:32:53 -070018Given a target-files zipfile, produces an OTA package that installs that build.
19An incremental OTA is produced if -i is given, otherwise a full OTA is produced.
Doug Zongkereef39442009-04-02 12:14:19 -070020
Tao Bao30df8b42018-04-23 15:32:53 -070021Usage: ota_from_target_files [options] input_target_files output_ota_package
Doug Zongkereef39442009-04-02 12:14:19 -070022
Tao Bao30df8b42018-04-23 15:32:53 -070023Common options that apply to both of non-A/B and A/B OTAs
24
25 --downgrade
26 Intentionally generate an incremental OTA that updates from a newer build
Tao Baofaa8e0b2018-04-12 14:31:43 -070027 to an older one (e.g. downgrading from P preview back to O MR1).
28 "ota-downgrade=yes" will be set in the package metadata file. A data wipe
29 will always be enforced when using this flag, so "ota-wipe=yes" will also
30 be included in the metadata file. The update-binary in the source build
31 will be used in the OTA package, unless --binary flag is specified. Please
32 also check the comment for --override_timestamp below.
Tao Bao30df8b42018-04-23 15:32:53 -070033
34 -i (--incremental_from) <file>
35 Generate an incremental OTA using the given target-files zip as the
36 starting build.
37
38 -k (--package_key) <key>
39 Key to use to sign the package (default is the value of
40 default_system_dev_certificate from the input target-files's
Tao Bao59cf0c52019-06-25 10:04:24 -070041 META/misc_info.txt, or "build/make/target/product/security/testkey" if
42 that value is not specified).
Doug Zongkerafb32ea2011-09-22 10:28:04 -070043
44 For incremental OTAs, the default value is based on the source
45 target-file, not the target build.
Doug Zongkereef39442009-04-02 12:14:19 -070046
Tao Bao30df8b42018-04-23 15:32:53 -070047 --override_timestamp
48 Intentionally generate an incremental OTA that updates from a newer build
Tao Baofaa8e0b2018-04-12 14:31:43 -070049 to an older one (based on timestamp comparison), by setting the downgrade
50 flag in the package metadata. This differs from --downgrade flag, as we
51 don't enforce a data wipe with this flag. Because we know for sure this is
52 NOT an actual downgrade case, but two builds happen to be cut in a reverse
53 order (e.g. from two branches). A legit use case is that we cut a new
54 build C (after having A and B), but want to enfore an update path of A ->
55 C -> B. Specifying --downgrade may not help since that would enforce a
56 data wipe for C -> B update.
57
58 We used to set a fake timestamp in the package metadata for this flow. But
59 now we consolidate the two cases (i.e. an actual downgrade, or a downgrade
60 based on timestamp) with the same "ota-downgrade=yes" flag, with the
61 difference being whether "ota-wipe=yes" is set.
Doug Zongkereef39442009-04-02 12:14:19 -070062
Tao Bao30df8b42018-04-23 15:32:53 -070063 --wipe_user_data
64 Generate an OTA package that will wipe the user data partition when
65 installed.
66
Yifan Hong50e79542018-11-08 17:44:12 -080067 --retrofit_dynamic_partitions
68 Generates an OTA package that updates a device to support dynamic
69 partitions (default False). This flag is implied when generating
70 an incremental OTA where the base build does not support dynamic
71 partitions but the target build does. For A/B, when this flag is set,
72 --skip_postinstall is implied.
73
xunchangabfa2652019-02-19 16:27:10 -080074 --skip_compatibility_check
Yifan Hong9276cf02019-08-21 16:37:04 -070075 Skip checking compatibility of the input target files package.
xunchangabfa2652019-02-19 16:27:10 -080076
xunchang1cfe2512019-02-19 14:14:48 -080077 --output_metadata_path
78 Write a copy of the metadata to a separate file. Therefore, users can
79 read the post build fingerprint without extracting the OTA package.
80
Yifan Hong65afc072020-04-17 10:08:10 -070081 --force_non_ab
82 This flag can only be set on an A/B device that also supports non-A/B
83 updates. Implies --two_step.
84 If set, generate that non-A/B update package.
85 If not set, generates A/B package for A/B device and non-A/B package for
86 non-A/B device.
87
Tao Bao30df8b42018-04-23 15:32:53 -070088Non-A/B OTA specific options
89
90 -b (--binary) <file>
91 Use the given binary as the update-binary in the output package, instead
92 of the binary in the build's target_files. Use for development only.
93
94 --block
95 Generate a block-based OTA for non-A/B device. We have deprecated the
96 support for file-based OTA since O. Block-based OTA will be used by
97 default for all non-A/B devices. Keeping this flag here to not break
98 existing callers.
99
100 -e (--extra_script) <file>
101 Insert the contents of file at the end of the update script.
Tao Bao43078aa2015-04-21 14:32:35 -0700102
leozwangaa6c1a12015-08-14 10:57:58 -0700103 --full_bootloader
104 Similar to --full_radio. When generating an incremental OTA, always
105 include a full copy of bootloader image.
106
Tao Bao30df8b42018-04-23 15:32:53 -0700107 --full_radio
108 When generating an incremental OTA, always include a full copy of radio
109 image. This option is only meaningful when -i is specified, because a full
110 radio is always included in a full OTA if applicable.
Michael Runge63f01de2014-10-28 19:24:19 -0700111
Tao Bao30df8b42018-04-23 15:32:53 -0700112 --log_diff <file>
113 Generate a log file that shows the differences in the source and target
114 builds for an incremental package. This option is only meaningful when -i
115 is specified.
116
117 -o (--oem_settings) <main_file[,additional_files...]>
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800118 Comma seperated list of files used to specify the expected OEM-specific
Tao Bao481bab82017-12-21 11:23:09 -0800119 properties on the OEM partition of the intended device. Multiple expected
120 values can be used by providing multiple files. Only the first dict will
121 be used to compute fingerprint, while the rest will be used to assert
122 OEM-specific properties.
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800123
Tao Bao8608cde2016-02-25 19:49:55 -0800124 --oem_no_mount
Tao Bao30df8b42018-04-23 15:32:53 -0700125 For devices with OEM-specific properties but without an OEM partition, do
126 not mount the OEM partition in the updater-script. This should be very
127 rarely used, since it's expected to have a dedicated OEM partition for
128 OEM-specific properties. Only meaningful when -o is specified.
Tao Bao8608cde2016-02-25 19:49:55 -0800129
Tao Bao30df8b42018-04-23 15:32:53 -0700130 --stash_threshold <float>
131 Specify the threshold that will be used to compute the maximum allowed
132 stash size (defaults to 0.8).
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700133
Tao Bao30df8b42018-04-23 15:32:53 -0700134 -t (--worker_threads) <int>
135 Specify the number of worker-threads that will be used when generating
136 patches for incremental updates (defaults to 3).
Tao Bao3e6161a2017-02-28 11:48:48 -0800137
Tao Bao30df8b42018-04-23 15:32:53 -0700138 --verify
139 Verify the checksums of the updated system and vendor (if any) partitions.
140 Non-A/B incremental OTAs only.
Doug Zongker1c390a22009-05-14 19:06:36 -0700141
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800142 -2 (--two_step)
Tao Bao30df8b42018-04-23 15:32:53 -0700143 Generate a 'two-step' OTA package, where recovery is updated first, so
144 that any changes made to the system partition are done using the new
145 recovery (new kernel, etc.).
146
147A/B OTA specific options
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800148
Tianjie Xu1b079832019-08-28 12:19:23 -0700149 --disable_fec_computation
150 Disable the on device FEC data computation for incremental updates.
151
Tao Baof7140c02018-01-30 17:09:24 -0800152 --include_secondary
153 Additionally include the payload for secondary slot images (default:
154 False). Only meaningful when generating A/B OTAs.
155
156 By default, an A/B OTA package doesn't contain the images for the
157 secondary slot (e.g. system_other.img). Specifying this flag allows
158 generating a separate payload that will install secondary slot images.
159
160 Such a package needs to be applied in a two-stage manner, with a reboot
161 in-between. During the first stage, the updater applies the primary
162 payload only. Upon finishing, it reboots the device into the newly updated
163 slot. It then continues to install the secondary payload to the inactive
164 slot, but without switching the active slot at the end (needs the matching
165 support in update_engine, i.e. SWITCH_SLOT_ON_REBOOT flag).
166
167 Due to the special install procedure, the secondary payload will be always
168 generated as a full payload.
169
Tao Baodea0f8b2016-06-20 17:55:06 -0700170 --payload_signer <signer>
171 Specify the signer when signing the payload and metadata for A/B OTAs.
172 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
173 with the package private key. If the private key cannot be accessed
174 directly, a payload signer that knows how to do that should be specified.
175 The signer will be supplied with "-inkey <path_to_key>",
176 "-in <input_file>" and "-out <output_file>" parameters.
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700177
178 --payload_signer_args <args>
179 Specify the arguments needed for payload signer.
Tao Bao15a146a2018-02-21 16:06:59 -0800180
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700181 --payload_signer_maximum_signature_size <signature_size>
182 The maximum signature size (in bytes) that would be generated by the given
183 payload signer. Only meaningful when custom payload signer is specified
184 via '--payload_signer'.
185 If the signer uses a RSA key, this should be the number of bytes to
186 represent the modulus. If it uses an EC key, this is the size of a
187 DER-encoded ECDSA signature.
188
xunchang376cc7c2019-04-08 23:04:58 -0700189 --payload_signer_key_size <key_size>
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700190 Deprecated. Use the '--payload_signer_maximum_signature_size' instead.
xunchang376cc7c2019-04-08 23:04:58 -0700191
Tao Bao15a146a2018-02-21 16:06:59 -0800192 --skip_postinstall
193 Skip the postinstall hooks when generating an A/B OTA package (default:
194 False). Note that this discards ALL the hooks, including non-optional
195 ones. Should only be used if caller knows it's safe to do so (e.g. all the
196 postinstall work is to dexopt apps and a data wipe will happen immediately
197 after). Only meaningful when generating A/B OTAs.
Doug Zongkereef39442009-04-02 12:14:19 -0700198"""
199
Tao Bao89fbb0f2017-01-10 10:47:58 -0800200from __future__ import print_function
201
Tianjie Xuf67dd802019-05-20 17:50:36 -0700202import collections
Tianjie Xu9afb2212020-05-10 21:48:15 +0000203import copy
204import itertools
Tao Bao32fcdab2018-10-12 10:30:39 -0700205import logging
Doug Zongkerfc44a512014-08-26 13:10:25 -0700206import multiprocessing
Tao Bao2dd1c482017-02-03 16:49:39 -0800207import os.path
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700208import shlex
Tao Bao15a146a2018-02-21 16:06:59 -0800209import shutil
Tao Bao85f16982018-03-08 16:28:33 -0800210import struct
Tao Bao481bab82017-12-21 11:23:09 -0800211import sys
Doug Zongkereef39442009-04-02 12:14:19 -0700212import zipfile
213
Yifan Hong9276cf02019-08-21 16:37:04 -0700214import check_target_files_vintf
Doug Zongkereef39442009-04-02 12:14:19 -0700215import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700216import edify_generator
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700217import verity_utils
Doug Zongkereef39442009-04-02 12:14:19 -0700218
Tao Bao481bab82017-12-21 11:23:09 -0800219if sys.hexversion < 0x02070000:
220 print("Python 2.7 or newer is required.", file=sys.stderr)
221 sys.exit(1)
222
Tao Bao32fcdab2018-10-12 10:30:39 -0700223logger = logging.getLogger(__name__)
Tao Bao481bab82017-12-21 11:23:09 -0800224
Doug Zongkereef39442009-04-02 12:14:19 -0700225OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700226OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700227OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700228OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700229OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700230OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800231OPTIONS.downgrade = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700232OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700233OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
234if OPTIONS.worker_threads == 0:
235 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800236OPTIONS.two_step = False
Tao Baof7140c02018-01-30 17:09:24 -0800237OPTIONS.include_secondary = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900238OPTIONS.no_signing = False
Tao Bao457cbf62017-03-06 09:56:01 -0800239OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -0800240OPTIONS.updater_binary = None
Tianjie Xu9afb2212020-05-10 21:48:15 +0000241OPTIONS.oem_dicts = None
Michael Runge6e836112014-04-15 17:40:21 -0700242OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800243OPTIONS.oem_no_mount = False
Tao Bao43078aa2015-04-21 14:32:35 -0700244OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700245OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700246# Stash size cannot exceed cache_size * threshold.
247OPTIONS.cache_size = None
248OPTIONS.stash_threshold = 0.8
Tao Baod62c6032015-11-30 09:40:20 -0800249OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700250OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700251OPTIONS.payload_signer_args = []
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700252OPTIONS.payload_signer_maximum_signature_size = None
Tao Bao5f8ff932017-03-21 22:35:00 -0700253OPTIONS.extracted_input = None
Christian Oderf63e2cd2017-05-01 22:30:15 +0200254OPTIONS.key_passwords = []
Tao Bao15a146a2018-02-21 16:06:59 -0800255OPTIONS.skip_postinstall = False
Yifan Hong50e79542018-11-08 17:44:12 -0800256OPTIONS.retrofit_dynamic_partitions = False
xunchangabfa2652019-02-19 16:27:10 -0800257OPTIONS.skip_compatibility_check = False
xunchang1cfe2512019-02-19 14:14:48 -0800258OPTIONS.output_metadata_path = None
Tianjie Xu1b079832019-08-28 12:19:23 -0700259OPTIONS.disable_fec_computation = False
Tianjie Xu9afb2212020-05-10 21:48:15 +0000260OPTIONS.boot_variable_values = None
Yifan Hong65afc072020-04-17 10:08:10 -0700261OPTIONS.force_non_ab = False
Tao Bao15a146a2018-02-21 16:06:59 -0800262
Tao Bao8dcf7382015-05-21 14:09:49 -0700263
Tao Bao2dd1c482017-02-03 16:49:39 -0800264METADATA_NAME = 'META-INF/com/android/metadata'
Tao Bao15a146a2018-02-21 16:06:59 -0800265POSTINSTALL_CONFIG = 'META/postinstall_config.txt'
Yifan Hong50e79542018-11-08 17:44:12 -0800266DYNAMIC_PARTITION_INFO = 'META/dynamic_partitions_info.txt'
Yifan Hongb433eba2019-03-06 12:42:53 -0800267AB_PARTITIONS = 'META/ab_partitions.txt'
Tao Bao04808502019-07-25 23:11:41 -0700268UNZIP_PATTERN = ['IMAGES/*', 'META/*', 'OTA/*', 'RADIO/*']
Tao Baof0c4aa22018-04-30 20:29:30 -0700269# Files to be unzipped for target diffing purpose.
270TARGET_DIFFING_UNZIP_PATTERN = ['BOOT', 'RECOVERY', 'SYSTEM/*', 'VENDOR/*',
271 'PRODUCT/*', 'SYSTEM_EXT/*', 'ODM/*']
Yifan Hongb433eba2019-03-06 12:42:53 -0800272RETROFIT_DAP_UNZIP_PATTERN = ['OTA/super_*.img', AB_PARTITIONS]
Tao Bao3e759462019-09-17 22:43:11 -0700273
274# Images to be excluded from secondary payload. We essentially only keep
275# 'system_other' and bootloader partitions.
276SECONDARY_PAYLOAD_SKIPPED_IMAGES = [
277 'boot', 'dtbo', 'modem', 'odm', 'product', 'radio', 'recovery',
Tianjiec3850642020-05-13 14:47:31 -0700278 'system_ext', 'vbmeta', 'vbmeta_system', 'vbmeta_vendor', 'vendor',
279 'vendor_boot']
Tao Bao6b0b2f92017-03-05 11:38:11 -0800280
Tao Bao2dd1c482017-02-03 16:49:39 -0800281
Tao Baofabe0832018-01-17 15:52:28 -0800282class PayloadSigner(object):
283 """A class that wraps the payload signing works.
284
285 When generating a Payload, hashes of the payload and metadata files will be
286 signed with the device key, either by calling an external payload signer or
287 by calling openssl with the package key. This class provides a unified
288 interface, so that callers can just call PayloadSigner.Sign().
289
290 If an external payload signer has been specified (OPTIONS.payload_signer), it
291 calls the signer with the provided args (OPTIONS.payload_signer_args). Note
292 that the signing key should be provided as part of the payload_signer_args.
293 Otherwise without an external signer, it uses the package key
294 (OPTIONS.package_key) and calls openssl for the signing works.
295 """
296
297 def __init__(self):
298 if OPTIONS.payload_signer is None:
299 # Prepare the payload signing key.
300 private_key = OPTIONS.package_key + OPTIONS.private_key_suffix
301 pw = OPTIONS.key_passwords[OPTIONS.package_key]
302
303 cmd = ["openssl", "pkcs8", "-in", private_key, "-inform", "DER"]
304 cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"])
305 signing_key = common.MakeTempFile(prefix="key-", suffix=".key")
306 cmd.extend(["-out", signing_key])
Tao Baobec89c12018-10-15 11:53:28 -0700307 common.RunAndCheckOutput(cmd, verbose=False)
Tao Baofabe0832018-01-17 15:52:28 -0800308
309 self.signer = "openssl"
310 self.signer_args = ["pkeyutl", "-sign", "-inkey", signing_key,
311 "-pkeyopt", "digest:sha256"]
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700312 self.maximum_signature_size = self._GetMaximumSignatureSizeInBytes(
313 signing_key)
Tao Baofabe0832018-01-17 15:52:28 -0800314 else:
315 self.signer = OPTIONS.payload_signer
316 self.signer_args = OPTIONS.payload_signer_args
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700317 if OPTIONS.payload_signer_maximum_signature_size:
318 self.maximum_signature_size = int(
319 OPTIONS.payload_signer_maximum_signature_size)
xunchang376cc7c2019-04-08 23:04:58 -0700320 else:
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700321 # The legacy config uses RSA2048 keys.
322 logger.warning("The maximum signature size for payload signer is not"
323 " set, default to 256 bytes.")
324 self.maximum_signature_size = 256
xunchang376cc7c2019-04-08 23:04:58 -0700325
326 @staticmethod
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700327 def _GetMaximumSignatureSizeInBytes(signing_key):
328 out_signature_size_file = common.MakeTempFile("signature_size")
329 cmd = ["delta_generator", "--out_maximum_signature_size_file={}".format(
330 out_signature_size_file), "--private_key={}".format(signing_key)]
331 common.RunAndCheckOutput(cmd)
332 with open(out_signature_size_file) as f:
333 signature_size = f.read().rstrip()
Luca Stefani88e1a142020-03-27 14:05:12 +0100334 logger.info("%s outputs the maximum signature size: %s", cmd[0],
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700335 signature_size)
336 return int(signature_size)
Tao Baofabe0832018-01-17 15:52:28 -0800337
338 def Sign(self, in_file):
339 """Signs the given input file. Returns the output filename."""
340 out_file = common.MakeTempFile(prefix="signed-", suffix=".bin")
341 cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file]
Tao Bao718faed2019-08-02 13:24:19 -0700342 common.RunAndCheckOutput(cmd)
Tao Baofabe0832018-01-17 15:52:28 -0800343 return out_file
344
345
Tao Bao40b18822018-01-30 18:19:04 -0800346class Payload(object):
347 """Manages the creation and the signing of an A/B OTA Payload."""
348
349 PAYLOAD_BIN = 'payload.bin'
350 PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
Tao Baof7140c02018-01-30 17:09:24 -0800351 SECONDARY_PAYLOAD_BIN = 'secondary/payload.bin'
352 SECONDARY_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt'
Tao Bao40b18822018-01-30 18:19:04 -0800353
Tao Bao667ff572018-02-10 00:02:40 -0800354 def __init__(self, secondary=False):
355 """Initializes a Payload instance.
356
357 Args:
358 secondary: Whether it's generating a secondary payload (default: False).
359 """
Tao Bao40b18822018-01-30 18:19:04 -0800360 self.payload_file = None
361 self.payload_properties = None
Tao Bao667ff572018-02-10 00:02:40 -0800362 self.secondary = secondary
Tao Bao40b18822018-01-30 18:19:04 -0800363
Tao Baof0c4aa22018-04-30 20:29:30 -0700364 def _Run(self, cmd): # pylint: disable=no-self-use
Tao Bao718faed2019-08-02 13:24:19 -0700365 # Don't pipe (buffer) the output if verbose is set. Let
366 # brillo_update_payload write to stdout/stderr directly, so its progress can
367 # be monitored.
368 if OPTIONS.verbose:
369 common.RunAndCheckOutput(cmd, stdout=None, stderr=None)
370 else:
371 common.RunAndCheckOutput(cmd)
372
Tao Bao40b18822018-01-30 18:19:04 -0800373 def Generate(self, target_file, source_file=None, additional_args=None):
374 """Generates a payload from the given target-files zip(s).
375
376 Args:
377 target_file: The filename of the target build target-files zip.
378 source_file: The filename of the source build target-files zip; or None if
379 generating a full OTA.
380 additional_args: A list of additional args that should be passed to
381 brillo_update_payload script; or None.
382 """
383 if additional_args is None:
384 additional_args = []
385
386 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
387 cmd = ["brillo_update_payload", "generate",
388 "--payload", payload_file,
389 "--target_image", target_file]
390 if source_file is not None:
391 cmd.extend(["--source_image", source_file])
Tianjie Xu1b079832019-08-28 12:19:23 -0700392 if OPTIONS.disable_fec_computation:
393 cmd.extend(["--disable_fec_computation", "true"])
Tao Bao40b18822018-01-30 18:19:04 -0800394 cmd.extend(additional_args)
Tao Bao718faed2019-08-02 13:24:19 -0700395 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800396
397 self.payload_file = payload_file
398 self.payload_properties = None
399
400 def Sign(self, payload_signer):
401 """Generates and signs the hashes of the payload and metadata.
402
403 Args:
404 payload_signer: A PayloadSigner() instance that serves the signing work.
405
406 Raises:
407 AssertionError: On any failure when calling brillo_update_payload script.
408 """
409 assert isinstance(payload_signer, PayloadSigner)
410
411 # 1. Generate hashes of the payload and metadata files.
412 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
413 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
414 cmd = ["brillo_update_payload", "hash",
415 "--unsigned_payload", self.payload_file,
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700416 "--signature_size", str(payload_signer.maximum_signature_size),
Tao Bao40b18822018-01-30 18:19:04 -0800417 "--metadata_hash_file", metadata_sig_file,
418 "--payload_hash_file", payload_sig_file]
Tao Bao718faed2019-08-02 13:24:19 -0700419 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800420
421 # 2. Sign the hashes.
422 signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
423 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
424
425 # 3. Insert the signatures back into the payload file.
426 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
427 suffix=".bin")
428 cmd = ["brillo_update_payload", "sign",
429 "--unsigned_payload", self.payload_file,
430 "--payload", signed_payload_file,
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700431 "--signature_size", str(payload_signer.maximum_signature_size),
Tao Bao40b18822018-01-30 18:19:04 -0800432 "--metadata_signature_file", signed_metadata_sig_file,
433 "--payload_signature_file", signed_payload_sig_file]
Tao Bao718faed2019-08-02 13:24:19 -0700434 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800435
436 # 4. Dump the signed payload properties.
437 properties_file = common.MakeTempFile(prefix="payload-properties-",
438 suffix=".txt")
439 cmd = ["brillo_update_payload", "properties",
440 "--payload", signed_payload_file,
441 "--properties_file", properties_file]
Tao Bao718faed2019-08-02 13:24:19 -0700442 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800443
Tao Bao667ff572018-02-10 00:02:40 -0800444 if self.secondary:
445 with open(properties_file, "a") as f:
446 f.write("SWITCH_SLOT_ON_REBOOT=0\n")
447
Tao Bao40b18822018-01-30 18:19:04 -0800448 if OPTIONS.wipe_user_data:
449 with open(properties_file, "a") as f:
450 f.write("POWERWASH=1\n")
451
452 self.payload_file = signed_payload_file
453 self.payload_properties = properties_file
454
Tao Bao667ff572018-02-10 00:02:40 -0800455 def WriteToZip(self, output_zip):
Tao Bao40b18822018-01-30 18:19:04 -0800456 """Writes the payload to the given zip.
457
458 Args:
459 output_zip: The output ZipFile instance.
460 """
461 assert self.payload_file is not None
462 assert self.payload_properties is not None
463
Tao Bao667ff572018-02-10 00:02:40 -0800464 if self.secondary:
Tao Baof7140c02018-01-30 17:09:24 -0800465 payload_arcname = Payload.SECONDARY_PAYLOAD_BIN
466 payload_properties_arcname = Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT
467 else:
468 payload_arcname = Payload.PAYLOAD_BIN
469 payload_properties_arcname = Payload.PAYLOAD_PROPERTIES_TXT
470
Tao Bao40b18822018-01-30 18:19:04 -0800471 # Add the signed payload file and properties into the zip. In order to
472 # support streaming, we pack them as ZIP_STORED. So these entries can be
473 # read directly with the offset and length pairs.
Tao Baof7140c02018-01-30 17:09:24 -0800474 common.ZipWrite(output_zip, self.payload_file, arcname=payload_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800475 compress_type=zipfile.ZIP_STORED)
476 common.ZipWrite(output_zip, self.payload_properties,
Tao Baof7140c02018-01-30 17:09:24 -0800477 arcname=payload_properties_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800478 compress_type=zipfile.ZIP_STORED)
479
480
Doug Zongkereef39442009-04-02 12:14:19 -0700481def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200482 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700483
Doug Zongker951495f2009-08-14 12:44:19 -0700484 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
485 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700486
487
Tao Bao481bab82017-12-21 11:23:09 -0800488def _LoadOemDicts(oem_source):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800489 """Returns the list of loaded OEM properties dict."""
Tao Bao481bab82017-12-21 11:23:09 -0800490 if not oem_source:
491 return None
492
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800493 oem_dicts = []
Tao Bao481bab82017-12-21 11:23:09 -0800494 for oem_file in oem_source:
495 with open(oem_file) as fp:
496 oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines()))
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800497 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700498
Doug Zongkereef39442009-04-02 12:14:19 -0700499
Tao Baod42e97e2016-11-30 12:11:57 -0800500def _WriteRecoveryImageToBoot(script, output_zip):
501 """Find and write recovery image to /boot in two-step OTA.
502
503 In two-step OTAs, we write recovery image to /boot as the first step so that
504 we can reboot to there and install a new recovery image to /recovery.
505 A special "recovery-two-step.img" will be preferred, which encodes the correct
506 path of "/boot". Otherwise the device may show "device is corrupt" message
507 when booting into /boot.
508
509 Fall back to using the regular recovery.img if the two-step recovery image
510 doesn't exist. Note that rebuilding the special image at this point may be
511 infeasible, because we don't have the desired boot signer and keys when
512 calling ota_from_target_files.py.
513 """
514
515 recovery_two_step_img_name = "recovery-two-step.img"
516 recovery_two_step_img_path = os.path.join(
Tao Bao04808502019-07-25 23:11:41 -0700517 OPTIONS.input_tmp, "OTA", recovery_two_step_img_name)
Tao Baod42e97e2016-11-30 12:11:57 -0800518 if os.path.exists(recovery_two_step_img_path):
Tao Bao04808502019-07-25 23:11:41 -0700519 common.ZipWrite(
520 output_zip,
521 recovery_two_step_img_path,
522 arcname=recovery_two_step_img_name)
Tao Bao32fcdab2018-10-12 10:30:39 -0700523 logger.info(
524 "two-step package: using %s in stage 1/3", recovery_two_step_img_name)
Tao Baod42e97e2016-11-30 12:11:57 -0800525 script.WriteRawImage("/boot", recovery_two_step_img_name)
526 else:
Tao Bao32fcdab2018-10-12 10:30:39 -0700527 logger.info("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800528 # The "recovery.img" entry has been written into package earlier.
529 script.WriteRawImage("/boot", "recovery.img")
530
531
Bill Peckhame868aec2019-09-17 17:06:47 -0700532def HasRecoveryPatch(target_files_zip, info_dict):
533 board_uses_vendorimage = info_dict.get("board_uses_vendorimage") == "true"
534
535 if board_uses_vendorimage:
536 target_files_dir = "VENDOR"
537 else:
538 target_files_dir = "SYSTEM/vendor"
539
540 patch = "%s/recovery-from-boot.p" % target_files_dir
541 img = "%s/etc/recovery.img" %target_files_dir
542
Tao Baof2cffbd2015-07-22 12:33:18 -0700543 namelist = [name for name in target_files_zip.namelist()]
Bill Peckhame868aec2019-09-17 17:06:47 -0700544 return (patch in namelist or img in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700545
Tao Bao457cbf62017-03-06 09:56:01 -0800546
Yifan Hong51d37562019-04-23 17:06:46 -0700547def HasPartition(target_files_zip, partition):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700548 try:
Yifan Hong51d37562019-04-23 17:06:46 -0700549 target_files_zip.getinfo(partition.upper() + "/")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700550 return True
551 except KeyError:
552 return False
553
Tao Bao457cbf62017-03-06 09:56:01 -0800554
Yifan Hong9276cf02019-08-21 16:37:04 -0700555def HasTrebleEnabled(target_files, target_info):
556 def HasVendorPartition(target_files):
557 if os.path.isdir(target_files):
558 return os.path.isdir(os.path.join(target_files, "VENDOR"))
559 if zipfile.is_zipfile(target_files):
560 return HasPartition(zipfile.ZipFile(target_files), "vendor")
561 raise ValueError("Unknown target_files argument")
Yifan Hong51d37562019-04-23 17:06:46 -0700562
Yifan Hong9276cf02019-08-21 16:37:04 -0700563 return (HasVendorPartition(target_files) and
Tao Bao481bab82017-12-21 11:23:09 -0800564 target_info.GetBuildProp("ro.treble.enabled") == "true")
Tao Baobcd1d162017-08-26 13:10:26 -0700565
566
Tao Bao481bab82017-12-21 11:23:09 -0800567def WriteFingerprintAssertion(script, target_info, source_info):
568 source_oem_props = source_info.oem_props
569 target_oem_props = target_info.oem_props
Michael Runge6e836112014-04-15 17:40:21 -0700570
Tao Bao481bab82017-12-21 11:23:09 -0800571 if source_oem_props is None and target_oem_props is None:
572 script.AssertSomeFingerprint(
573 source_info.fingerprint, target_info.fingerprint)
574 elif source_oem_props is not None and target_oem_props is not None:
575 script.AssertSomeThumbprint(
576 target_info.GetBuildProp("ro.build.thumbprint"),
577 source_info.GetBuildProp("ro.build.thumbprint"))
578 elif source_oem_props is None and target_oem_props is not None:
579 script.AssertFingerprintOrThumbprint(
580 source_info.fingerprint,
581 target_info.GetBuildProp("ro.build.thumbprint"))
582 else:
583 script.AssertFingerprintOrThumbprint(
584 target_info.fingerprint,
585 source_info.GetBuildProp("ro.build.thumbprint"))
Doug Zongker73ef8252009-07-23 15:12:53 -0700586
Doug Zongkerfc44a512014-08-26 13:10:25 -0700587
Yifan Hong9276cf02019-08-21 16:37:04 -0700588def CheckVintfIfTrebleEnabled(target_files, target_info):
589 """Checks compatibility info of the input target files.
Tao Bao21803d32017-04-19 10:16:09 -0700590
Yifan Hong9276cf02019-08-21 16:37:04 -0700591 Metadata used for compatibility verification is retrieved from target_zip.
Tao Bao21803d32017-04-19 10:16:09 -0700592
Yifan Hong9276cf02019-08-21 16:37:04 -0700593 Compatibility should only be checked for devices that have enabled
Tao Baobcd1d162017-08-26 13:10:26 -0700594 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700595
596 Args:
Yifan Hong9276cf02019-08-21 16:37:04 -0700597 target_files: Path to zip file containing the source files to be included
598 for OTA. Can also be the path to extracted directory.
Tao Bao481bab82017-12-21 11:23:09 -0800599 target_info: The BuildInfo instance that holds the target build info.
Tao Bao21803d32017-04-19 10:16:09 -0700600 """
601
Tao Baobcd1d162017-08-26 13:10:26 -0700602 # Will only proceed if the target has enabled the Treble support (as well as
603 # having a /vendor partition).
Yifan Hong9276cf02019-08-21 16:37:04 -0700604 if not HasTrebleEnabled(target_files, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700605 return
606
xunchangabfa2652019-02-19 16:27:10 -0800607 # Skip adding the compatibility package as a workaround for b/114240221. The
608 # compatibility will always fail on devices without qualified kernels.
609 if OPTIONS.skip_compatibility_check:
610 return
611
Yifan Hong9276cf02019-08-21 16:37:04 -0700612 if not check_target_files_vintf.CheckVintf(target_files, target_info):
613 raise RuntimeError("VINTF compatibility check failed")
Tao Bao21803d32017-04-19 10:16:09 -0700614
615
Tianjie Xuf67dd802019-05-20 17:50:36 -0700616def GetBlockDifferences(target_zip, source_zip, target_info, source_info,
617 device_specific):
618 """Returns a ordered dict of block differences with partition name as key."""
619
620 def GetIncrementalBlockDifferenceForPartition(name):
621 if not HasPartition(source_zip, name):
622 raise RuntimeError("can't generate incremental that adds {}".format(name))
623
624 partition_src = common.GetUserImage(name, OPTIONS.source_tmp, source_zip,
625 info_dict=source_info,
626 allow_shared_blocks=allow_shared_blocks)
627
628 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
629 name, 4096, target_info)
630 partition_tgt = common.GetUserImage(name, OPTIONS.target_tmp, target_zip,
631 info_dict=target_info,
632 allow_shared_blocks=allow_shared_blocks,
633 hashtree_info_generator=
634 hashtree_info_generator)
635
636 # Check the first block of the source system partition for remount R/W only
637 # if the filesystem is ext4.
638 partition_source_info = source_info["fstab"]["/" + name]
639 check_first_block = partition_source_info.fs_type == "ext4"
640 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
641 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
642 # b) the blocks listed in block map may not contain all the bytes for a
643 # given file (because they're rounded to be 4K-aligned).
644 partition_target_info = target_info["fstab"]["/" + name]
645 disable_imgdiff = (partition_source_info.fs_type == "squashfs" or
646 partition_target_info.fs_type == "squashfs")
Xindong Xu2a7aaa62020-03-13 15:59:22 +0800647 return common.BlockDifference(name, partition_tgt, partition_src,
Tianjie Xuf67dd802019-05-20 17:50:36 -0700648 check_first_block,
649 version=blockimgdiff_version,
650 disable_imgdiff=disable_imgdiff)
651
652 if source_zip:
653 # See notes in common.GetUserImage()
654 allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or
655 target_info.get('ext4_share_dup_blocks') == "true")
656 blockimgdiff_version = max(
657 int(i) for i in target_info.get(
658 "blockimgdiff_versions", "1").split(","))
659 assert blockimgdiff_version >= 3
660
661 block_diff_dict = collections.OrderedDict()
662 partition_names = ["system", "vendor", "product", "odm", "system_ext"]
663 for partition in partition_names:
664 if not HasPartition(target_zip, partition):
665 continue
666 # Full OTA update.
667 if not source_zip:
668 tgt = common.GetUserImage(partition, OPTIONS.input_tmp, target_zip,
669 info_dict=target_info,
670 reset_file_map=True)
671 block_diff_dict[partition] = common.BlockDifference(partition, tgt,
672 src=None)
673 # Incremental OTA update.
674 else:
675 block_diff_dict[partition] = GetIncrementalBlockDifferenceForPartition(
676 partition)
677 assert "system" in block_diff_dict
678
679 # Get the block diffs from the device specific script. If there is a
680 # duplicate block diff for a partition, ignore the diff in the generic script
681 # and use the one in the device specific script instead.
682 if source_zip:
683 device_specific_diffs = device_specific.IncrementalOTA_GetBlockDifferences()
684 function_name = "IncrementalOTA_GetBlockDifferences"
685 else:
686 device_specific_diffs = device_specific.FullOTA_GetBlockDifferences()
687 function_name = "FullOTA_GetBlockDifferences"
688
689 if device_specific_diffs:
690 assert all(isinstance(diff, common.BlockDifference)
691 for diff in device_specific_diffs), \
692 "{} is not returning a list of BlockDifference objects".format(
693 function_name)
694 for diff in device_specific_diffs:
695 if diff.partition in block_diff_dict:
696 logger.warning("Duplicate block difference found. Device specific block"
697 " diff for partition '%s' overrides the one in generic"
698 " script.", diff.partition)
699 block_diff_dict[diff.partition] = diff
700
701 return block_diff_dict
702
703
Tao Bao491d7e22018-02-21 13:17:22 -0800704def WriteFullOTAPackage(input_zip, output_file):
Tao Bao1c320f82019-10-04 23:25:12 -0700705 target_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700706
Tao Bao481bab82017-12-21 11:23:09 -0800707 # We don't know what version it will be installed on top of. We expect the API
708 # just won't change very often. Similarly for fstab, it might have changed in
709 # the target build.
710 target_api_version = target_info["recovery_api_version"]
711 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700712
Tao Bao481bab82017-12-21 11:23:09 -0800713 if target_info.oem_props and not OPTIONS.oem_no_mount:
714 target_info.WriteMountOemScript(script)
715
Tao Baodf3a48b2018-01-10 16:30:43 -0800716 metadata = GetPackageMetadata(target_info)
Doug Zongker2ea21062010-04-28 16:05:21 -0700717
Tao Bao491d7e22018-02-21 13:17:22 -0800718 if not OPTIONS.no_signing:
719 staging_file = common.MakeTempFile(suffix='.zip')
720 else:
721 staging_file = output_file
722
723 output_zip = zipfile.ZipFile(
724 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
725
Doug Zongker05d3dea2009-06-22 11:32:31 -0700726 device_specific = common.DeviceSpecificParams(
727 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800728 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700729 output_zip=output_zip,
730 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700731 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700732 metadata=metadata,
733 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700734
Bill Peckhame868aec2019-09-17 17:06:47 -0700735 assert HasRecoveryPatch(input_zip, info_dict=OPTIONS.info_dict)
Doug Zongkerc9253822014-02-04 12:17:58 -0800736
Tao Bao481bab82017-12-21 11:23:09 -0800737 # Assertions (e.g. downgrade check, device properties check).
738 ts = target_info.GetBuildProp("ro.build.date.utc")
739 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700740 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700741
Tao Bao481bab82017-12-21 11:23:09 -0800742 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700743 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800744
Tianjie Xuf67dd802019-05-20 17:50:36 -0700745 block_diff_dict = GetBlockDifferences(target_zip=input_zip, source_zip=None,
746 target_info=target_info,
747 source_info=None,
748 device_specific=device_specific)
749
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800750 # Two-step package strategy (in chronological order, which is *not*
751 # the order in which the generated script has things):
752 #
753 # if stage is not "2/3" or "3/3":
754 # write recovery image to boot partition
755 # set stage to "2/3"
756 # reboot to boot partition and restart recovery
757 # else if stage is "2/3":
758 # write recovery image to recovery partition
759 # set stage to "3/3"
760 # reboot to recovery partition and restart recovery
761 # else:
762 # (stage must be "3/3")
763 # set stage to ""
764 # do normal full package installation:
765 # wipe and install system, boot image, etc.
766 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700767 # complete script normally
768 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800769
770 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
771 OPTIONS.input_tmp, "RECOVERY")
772 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800773 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800774 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800775 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800776 assert fs.fs_type.upper() == "EMMC", \
777 "two-step packages only supported on devices with EMMC /misc partitions"
778 bcb_dev = {"bcb_dev": fs.device}
779 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
780 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700781if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800782""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800783
784 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
785 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800786 script.WriteRawImage("/recovery", "recovery.img")
787 script.AppendExtra("""
788set_stage("%(bcb_dev)s", "3/3");
789reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700790else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800791""" % bcb_dev)
792
Tao Baod42e97e2016-11-30 12:11:57 -0800793 # Stage 3/3: Make changes.
794 script.Comment("Stage 3/3")
795
Tao Bao6c55a8a2015-04-08 15:30:27 -0700796 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800797 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700798
Doug Zongkere5ff5902012-01-17 10:55:37 -0800799 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700800
Tianjie Xuf67dd802019-05-20 17:50:36 -0700801 # All other partitions as well as the data wipe use 10% of the progress, and
802 # the update of the system partition takes the remaining progress.
803 system_progress = 0.9 - (len(block_diff_dict) - 1) * 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700804 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800805 system_progress -= 0.1
Tianjie Xuf67dd802019-05-20 17:50:36 -0700806 progress_dict = {partition: 0.1 for partition in block_diff_dict}
807 progress_dict["system"] = system_progress
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700808
Yifan Hong10c530d2018-12-27 17:34:18 -0800809 if target_info.get('use_dynamic_partitions') == "true":
810 # Use empty source_info_dict to indicate that all partitions / groups must
811 # be re-added.
812 dynamic_partitions_diff = common.DynamicPartitionsDifference(
813 info_dict=OPTIONS.info_dict,
Tianjie Xuf67dd802019-05-20 17:50:36 -0700814 block_diffs=block_diff_dict.values(),
Yifan Hong10c530d2018-12-27 17:34:18 -0800815 progress_dict=progress_dict)
816 dynamic_partitions_diff.WriteScript(script, output_zip,
817 write_verify_script=OPTIONS.verify)
818 else:
Tianjie Xuf67dd802019-05-20 17:50:36 -0700819 for block_diff in block_diff_dict.values():
Yifan Hong10c530d2018-12-27 17:34:18 -0800820 block_diff.WriteScript(script, output_zip,
821 progress=progress_dict.get(block_diff.partition),
822 write_verify_script=OPTIONS.verify)
Doug Zongker73ef8252009-07-23 15:12:53 -0700823
Yifan Hong9276cf02019-08-21 16:37:04 -0700824 CheckVintfIfTrebleEnabled(OPTIONS.input_tmp, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700825
Yifan Hong10c530d2018-12-27 17:34:18 -0800826 boot_img = common.GetBootableImage(
827 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Tao Bao481bab82017-12-21 11:23:09 -0800828 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700829 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700830
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700831 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700832
Tianjie Xuf67dd802019-05-20 17:50:36 -0700833 script.ShowProgress(0.1, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700834 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700835
Doug Zongker1c390a22009-05-14 19:06:36 -0700836 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700837 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700838
Doug Zongker14833602010-02-02 13:12:04 -0800839 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800840
Doug Zongker922206e2014-03-04 13:16:24 -0800841 if OPTIONS.wipe_user_data:
842 script.ShowProgress(0.1, 10)
843 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700844
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800845 if OPTIONS.two_step:
846 script.AppendExtra("""
847set_stage("%(bcb_dev)s", "");
848""" % bcb_dev)
849 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800850
851 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
852 script.Comment("Stage 1/3")
853 _WriteRecoveryImageToBoot(script, output_zip)
854
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800855 script.AppendExtra("""
856set_stage("%(bcb_dev)s", "2/3");
857reboot_now("%(bcb_dev)s", "");
858endif;
859endif;
860""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800861
Tao Bao5d182562016-02-23 11:38:39 -0800862 script.SetProgress(1)
863 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800864 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -0800865
866 # We haven't written the metadata entry, which will be done in
867 # FinalizeMetadata.
868 common.ZipClose(output_zip)
869
870 needed_property_files = (
871 NonAbOtaPropertyFiles(),
872 )
873 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Doug Zongker2ea21062010-04-28 16:05:21 -0700874
Doug Zongkerfc44a512014-08-26 13:10:25 -0700875
xunchang1cfe2512019-02-19 14:14:48 -0800876def WriteMetadata(metadata, output):
877 """Writes the metadata to the zip archive or a file.
878
879 Args:
880 metadata: The metadata dict for the package.
881 output: A ZipFile object or a string of the output file path.
882 """
883
Tao Bao59cf0c52019-06-25 10:04:24 -0700884 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.items())])
xunchang1cfe2512019-02-19 14:14:48 -0800885 if isinstance(output, zipfile.ZipFile):
886 common.ZipWriteStr(output, METADATA_NAME, value,
887 compress_type=zipfile.ZIP_STORED)
888 return
889
890 with open(output, 'w') as f:
891 f.write(value)
Doug Zongkereef39442009-04-02 12:14:19 -0700892
Doug Zongkerfc44a512014-08-26 13:10:25 -0700893
Tao Bao481bab82017-12-21 11:23:09 -0800894def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -0800895 # Only incremental OTAs are allowed to reach here.
896 assert OPTIONS.incremental_source is not None
897
Tao Bao481bab82017-12-21 11:23:09 -0800898 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
899 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Bao59cf0c52019-06-25 10:04:24 -0700900 is_downgrade = int(post_timestamp) < int(pre_timestamp)
Tao Baob31892e2017-02-07 11:21:17 -0800901
902 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800903 if not is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700904 raise RuntimeError(
905 "--downgrade or --override_timestamp specified but no downgrade "
906 "detected: pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800907 metadata["ota-downgrade"] = "yes"
Tao Baob31892e2017-02-07 11:21:17 -0800908 else:
909 if is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700910 raise RuntimeError(
911 "Downgrade detected based on timestamp check: pre: %s, post: %s. "
912 "Need to specify --override_timestamp OR --downgrade to allow "
913 "building the incremental." % (pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800914
915
Tao Baodf3a48b2018-01-10 16:30:43 -0800916def GetPackageMetadata(target_info, source_info=None):
917 """Generates and returns the metadata dict.
918
919 It generates a dict() that contains the info to be written into an OTA
920 package (META-INF/com/android/metadata). It also handles the detection of
Tao Baofaa8e0b2018-04-12 14:31:43 -0700921 downgrade / data wipe based on the global options.
Tao Baodf3a48b2018-01-10 16:30:43 -0800922
923 Args:
924 target_info: The BuildInfo instance that holds the target build info.
925 source_info: The BuildInfo instance that holds the source build info, or
926 None if generating full OTA.
927
928 Returns:
929 A dict to be written into package metadata entry.
930 """
Tao Bao1c320f82019-10-04 23:25:12 -0700931 assert isinstance(target_info, common.BuildInfo)
932 assert source_info is None or isinstance(source_info, common.BuildInfo)
Tao Baodf3a48b2018-01-10 16:30:43 -0800933
934 metadata = {
935 'post-build' : target_info.fingerprint,
936 'post-build-incremental' : target_info.GetBuildProp(
937 'ro.build.version.incremental'),
Tao Bao35dc2552018-02-01 13:18:00 -0800938 'post-sdk-level' : target_info.GetBuildProp(
939 'ro.build.version.sdk'),
940 'post-security-patch-level' : target_info.GetBuildProp(
941 'ro.build.version.security_patch'),
Tao Baodf3a48b2018-01-10 16:30:43 -0800942 }
943
Yifan Hong65afc072020-04-17 10:08:10 -0700944 if target_info.is_ab and not OPTIONS.force_non_ab:
Tao Baodf3a48b2018-01-10 16:30:43 -0800945 metadata['ota-type'] = 'AB'
946 metadata['ota-required-cache'] = '0'
947 else:
948 metadata['ota-type'] = 'BLOCK'
949
950 if OPTIONS.wipe_user_data:
951 metadata['ota-wipe'] = 'yes'
952
Tao Bao393eeb42019-03-06 16:00:38 -0800953 if OPTIONS.retrofit_dynamic_partitions:
954 metadata['ota-retrofit-dynamic-partitions'] = 'yes'
955
Tao Baodf3a48b2018-01-10 16:30:43 -0800956 is_incremental = source_info is not None
957 if is_incremental:
958 metadata['pre-build'] = source_info.fingerprint
959 metadata['pre-build-incremental'] = source_info.GetBuildProp(
960 'ro.build.version.incremental')
961 metadata['pre-device'] = source_info.device
962 else:
963 metadata['pre-device'] = target_info.device
964
Tao Baofaa8e0b2018-04-12 14:31:43 -0700965 # Use the actual post-timestamp, even for a downgrade case.
966 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
967
968 # Detect downgrades and set up downgrade flags accordingly.
Tao Baodf3a48b2018-01-10 16:30:43 -0800969 if is_incremental:
970 HandleDowngradeMetadata(metadata, target_info, source_info)
Tao Baodf3a48b2018-01-10 16:30:43 -0800971
972 return metadata
973
974
Tao Baod3fc38a2018-03-08 16:09:01 -0800975class PropertyFiles(object):
976 """A class that computes the property-files string for an OTA package.
977
978 A property-files string is a comma-separated string that contains the
979 offset/size info for an OTA package. The entries, which must be ZIP_STORED,
980 can be fetched directly with the package URL along with the offset/size info.
981 These strings can be used for streaming A/B OTAs, or allowing an updater to
982 download package metadata entry directly, without paying the cost of
983 downloading entire package.
Tao Baofe5b69a2018-03-02 09:47:43 -0800984
Tao Baocc8e2662018-03-01 19:30:00 -0800985 Computing the final property-files string requires two passes. Because doing
986 the whole package signing (with signapk.jar) will possibly reorder the ZIP
987 entries, which may in turn invalidate earlier computed ZIP entry offset/size
988 values.
989
990 This class provides functions to be called for each pass. The general flow is
991 as follows.
992
Tao Baod3fc38a2018-03-08 16:09:01 -0800993 property_files = PropertyFiles()
Tao Baocc8e2662018-03-01 19:30:00 -0800994 # The first pass, which writes placeholders before doing initial signing.
995 property_files.Compute()
996 SignOutput()
997
998 # The second pass, by replacing the placeholders with actual data.
999 property_files.Finalize()
1000 SignOutput()
1001
1002 And the caller can additionally verify the final result.
1003
1004 property_files.Verify()
Tao Baofe5b69a2018-03-02 09:47:43 -08001005 """
1006
Tao Baocc8e2662018-03-01 19:30:00 -08001007 def __init__(self):
Tao Baod3fc38a2018-03-08 16:09:01 -08001008 self.name = None
1009 self.required = ()
1010 self.optional = ()
Tao Baofe5b69a2018-03-02 09:47:43 -08001011
Tao Baocc8e2662018-03-01 19:30:00 -08001012 def Compute(self, input_zip):
1013 """Computes and returns a property-files string with placeholders.
Tao Baofe5b69a2018-03-02 09:47:43 -08001014
Tao Baocc8e2662018-03-01 19:30:00 -08001015 We reserve extra space for the offset and size of the metadata entry itself,
1016 although we don't know the final values until the package gets signed.
Tao Baofe5b69a2018-03-02 09:47:43 -08001017
Tao Baocc8e2662018-03-01 19:30:00 -08001018 Args:
1019 input_zip: The input ZIP file.
Tao Baofe5b69a2018-03-02 09:47:43 -08001020
Tao Baocc8e2662018-03-01 19:30:00 -08001021 Returns:
1022 A string with placeholders for the metadata offset/size info, e.g.
1023 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1024 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001025 return self.GetPropertyFilesString(input_zip, reserve_space=True)
Tao Baofe5b69a2018-03-02 09:47:43 -08001026
Tao Baod2ce2ed2018-03-16 12:59:42 -07001027 class InsufficientSpaceException(Exception):
1028 pass
1029
Tao Baocc8e2662018-03-01 19:30:00 -08001030 def Finalize(self, input_zip, reserved_length):
1031 """Finalizes a property-files string with actual METADATA offset/size info.
1032
1033 The input ZIP file has been signed, with the ZIP entries in the desired
1034 place (signapk.jar will possibly reorder the ZIP entries). Now we compute
1035 the ZIP entry offsets and construct the property-files string with actual
1036 data. Note that during this process, we must pad the property-files string
1037 to the reserved length, so that the METADATA entry size remains the same.
1038 Otherwise the entries' offsets and sizes may change again.
1039
1040 Args:
1041 input_zip: The input ZIP file.
1042 reserved_length: The reserved length of the property-files string during
1043 the call to Compute(). The final string must be no more than this
1044 size.
1045
1046 Returns:
1047 A property-files string including the metadata offset/size info, e.g.
1048 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379 ".
1049
1050 Raises:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001051 InsufficientSpaceException: If the reserved length is insufficient to hold
1052 the final string.
Tao Baocc8e2662018-03-01 19:30:00 -08001053 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001054 result = self.GetPropertyFilesString(input_zip, reserve_space=False)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001055 if len(result) > reserved_length:
1056 raise self.InsufficientSpaceException(
1057 'Insufficient reserved space: reserved={}, actual={}'.format(
1058 reserved_length, len(result)))
1059
Tao Baocc8e2662018-03-01 19:30:00 -08001060 result += ' ' * (reserved_length - len(result))
1061 return result
1062
1063 def Verify(self, input_zip, expected):
1064 """Verifies the input ZIP file contains the expected property-files string.
1065
1066 Args:
1067 input_zip: The input ZIP file.
1068 expected: The property-files string that's computed from Finalize().
1069
1070 Raises:
1071 AssertionError: On finding a mismatch.
1072 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001073 actual = self.GetPropertyFilesString(input_zip)
Tao Baocc8e2662018-03-01 19:30:00 -08001074 assert actual == expected, \
1075 "Mismatching streaming metadata: {} vs {}.".format(actual, expected)
1076
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001077 def GetPropertyFilesString(self, zip_file, reserve_space=False):
1078 """
1079 Constructs the property-files string per request.
1080
1081 Args:
1082 zip_file: The input ZIP file.
1083 reserved_length: The reserved length of the property-files string.
1084
1085 Returns:
1086 A property-files string including the metadata offset/size info, e.g.
1087 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1088 """
Tao Baocc8e2662018-03-01 19:30:00 -08001089
1090 def ComputeEntryOffsetSize(name):
1091 """Computes the zip entry offset and size."""
1092 info = zip_file.getinfo(name)
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001093 offset = info.header_offset
1094 offset += zipfile.sizeFileHeader
1095 offset += len(info.extra) + len(info.filename)
Tao Baocc8e2662018-03-01 19:30:00 -08001096 size = info.file_size
1097 return '%s:%d:%d' % (os.path.basename(name), offset, size)
1098
1099 tokens = []
Tao Bao85f16982018-03-08 16:28:33 -08001100 tokens.extend(self._GetPrecomputed(zip_file))
Tao Baocc8e2662018-03-01 19:30:00 -08001101 for entry in self.required:
1102 tokens.append(ComputeEntryOffsetSize(entry))
1103 for entry in self.optional:
1104 if entry in zip_file.namelist():
1105 tokens.append(ComputeEntryOffsetSize(entry))
1106
1107 # 'META-INF/com/android/metadata' is required. We don't know its actual
1108 # offset and length (as well as the values for other entries). So we reserve
Tao Baod2ce2ed2018-03-16 12:59:42 -07001109 # 15-byte as a placeholder ('offset:length'), which is sufficient to cover
1110 # the space for metadata entry. Because 'offset' allows a max of 10-digit
1111 # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the
1112 # reserved space serves the metadata entry only.
Tao Baocc8e2662018-03-01 19:30:00 -08001113 if reserve_space:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001114 tokens.append('metadata:' + ' ' * 15)
Tao Baocc8e2662018-03-01 19:30:00 -08001115 else:
1116 tokens.append(ComputeEntryOffsetSize(METADATA_NAME))
1117
1118 return ','.join(tokens)
Tao Baofe5b69a2018-03-02 09:47:43 -08001119
Tao Bao85f16982018-03-08 16:28:33 -08001120 def _GetPrecomputed(self, input_zip):
1121 """Computes the additional tokens to be included into the property-files.
1122
1123 This applies to tokens without actual ZIP entries, such as
1124 payload_metadadata.bin. We want to expose the offset/size to updaters, so
1125 that they can download the payload metadata directly with the info.
1126
1127 Args:
1128 input_zip: The input zip file.
1129
1130 Returns:
1131 A list of strings (tokens) to be added to the property-files string.
1132 """
1133 # pylint: disable=no-self-use
1134 # pylint: disable=unused-argument
1135 return []
1136
Tao Baofe5b69a2018-03-02 09:47:43 -08001137
Tao Baod3fc38a2018-03-08 16:09:01 -08001138class StreamingPropertyFiles(PropertyFiles):
1139 """A subclass for computing the property-files for streaming A/B OTAs."""
1140
1141 def __init__(self):
1142 super(StreamingPropertyFiles, self).__init__()
1143 self.name = 'ota-streaming-property-files'
1144 self.required = (
1145 # payload.bin and payload_properties.txt must exist.
1146 'payload.bin',
1147 'payload_properties.txt',
1148 )
1149 self.optional = (
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001150 # care_map is available only if dm-verity is enabled.
1151 'care_map.pb',
Tao Baod3fc38a2018-03-08 16:09:01 -08001152 'care_map.txt',
1153 # compatibility.zip is available only if target supports Treble.
1154 'compatibility.zip',
1155 )
1156
1157
Tao Bao85f16982018-03-08 16:28:33 -08001158class AbOtaPropertyFiles(StreamingPropertyFiles):
1159 """The property-files for A/B OTA that includes payload_metadata.bin info.
1160
1161 Since P, we expose one more token (aka property-file), in addition to the ones
1162 for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'.
1163 'payload_metadata.bin' is the header part of a payload ('payload.bin'), which
1164 doesn't exist as a separate ZIP entry, but can be used to verify if the
1165 payload can be applied on the given device.
1166
1167 For backward compatibility, we keep both of the 'ota-streaming-property-files'
1168 and the newly added 'ota-property-files' in P. The new token will only be
1169 available in 'ota-property-files'.
1170 """
1171
1172 def __init__(self):
1173 super(AbOtaPropertyFiles, self).__init__()
1174 self.name = 'ota-property-files'
1175
1176 def _GetPrecomputed(self, input_zip):
1177 offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip)
1178 return ['payload_metadata.bin:{}:{}'.format(offset, size)]
1179
1180 @staticmethod
1181 def _GetPayloadMetadataOffsetAndSize(input_zip):
1182 """Computes the offset and size of the payload metadata for a given package.
1183
1184 (From system/update_engine/update_metadata.proto)
1185 A delta update file contains all the deltas needed to update a system from
1186 one specific version to another specific version. The update format is
1187 represented by this struct pseudocode:
1188
1189 struct delta_update_file {
1190 char magic[4] = "CrAU";
1191 uint64 file_format_version;
1192 uint64 manifest_size; // Size of protobuf DeltaArchiveManifest
1193
1194 // Only present if format_version > 1:
1195 uint32 metadata_signature_size;
1196
1197 // The Bzip2 compressed DeltaArchiveManifest
1198 char manifest[metadata_signature_size];
1199
1200 // The signature of the metadata (from the beginning of the payload up to
1201 // this location, not including the signature itself). This is a
1202 // serialized Signatures message.
1203 char medatada_signature_message[metadata_signature_size];
1204
1205 // Data blobs for files, no specific format. The specific offset
1206 // and length of each data blob is recorded in the DeltaArchiveManifest.
1207 struct {
1208 char data[];
1209 } blobs[];
1210
1211 // These two are not signed:
1212 uint64 payload_signatures_message_size;
1213 char payload_signatures_message[];
1214 };
1215
1216 'payload-metadata.bin' contains all the bytes from the beginning of the
1217 payload, till the end of 'medatada_signature_message'.
1218 """
1219 payload_info = input_zip.getinfo('payload.bin')
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001220 payload_offset = payload_info.header_offset
1221 payload_offset += zipfile.sizeFileHeader
1222 payload_offset += len(payload_info.extra) + len(payload_info.filename)
Tao Bao85f16982018-03-08 16:28:33 -08001223 payload_size = payload_info.file_size
1224
Tao Bao59cf0c52019-06-25 10:04:24 -07001225 with input_zip.open('payload.bin') as payload_fp:
Tao Bao85f16982018-03-08 16:28:33 -08001226 header_bin = payload_fp.read(24)
1227
1228 # network byte order (big-endian)
1229 header = struct.unpack("!IQQL", header_bin)
1230
1231 # 'CrAU'
1232 magic = header[0]
1233 assert magic == 0x43724155, "Invalid magic: {:x}".format(magic)
1234
1235 manifest_size = header[2]
1236 metadata_signature_size = header[3]
1237 metadata_total = 24 + manifest_size + metadata_signature_size
1238 assert metadata_total < payload_size
1239
1240 return (payload_offset, metadata_total)
1241
1242
Tao Bao491d7e22018-02-21 13:17:22 -08001243class NonAbOtaPropertyFiles(PropertyFiles):
1244 """The property-files for non-A/B OTA.
1245
1246 For non-A/B OTA, the property-files string contains the info for METADATA
1247 entry, with which a system updater can be fetched the package metadata prior
1248 to downloading the entire package.
1249 """
1250
1251 def __init__(self):
1252 super(NonAbOtaPropertyFiles, self).__init__()
1253 self.name = 'ota-property-files'
1254
1255
Tao Baod3fc38a2018-03-08 16:09:01 -08001256def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
Tao Baofe5b69a2018-03-02 09:47:43 -08001257 """Finalizes the metadata and signs an A/B OTA package.
1258
1259 In order to stream an A/B OTA package, we need 'ota-streaming-property-files'
1260 that contains the offsets and sizes for the ZIP entries. An example
1261 property-files string is as follows.
1262
1263 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379"
1264
1265 OTA server can pass down this string, in addition to the package URL, to the
1266 system update client. System update client can then fetch individual ZIP
1267 entries (ZIP_STORED) directly at the given offset of the URL.
1268
1269 Args:
1270 metadata: The metadata dict for the package.
1271 input_file: The input ZIP filename that doesn't contain the package METADATA
1272 entry yet.
1273 output_file: The final output ZIP filename.
Tao Baod3fc38a2018-03-08 16:09:01 -08001274 needed_property_files: The list of PropertyFiles' to be generated.
Tao Baofe5b69a2018-03-02 09:47:43 -08001275 """
Tao Baofe5b69a2018-03-02 09:47:43 -08001276
Tao Baod2ce2ed2018-03-16 12:59:42 -07001277 def ComputeAllPropertyFiles(input_file, needed_property_files):
1278 # Write the current metadata entry with placeholders.
1279 with zipfile.ZipFile(input_file) as input_zip:
1280 for property_files in needed_property_files:
1281 metadata[property_files.name] = property_files.Compute(input_zip)
1282 namelist = input_zip.namelist()
Tao Baofe5b69a2018-03-02 09:47:43 -08001283
Tao Baod2ce2ed2018-03-16 12:59:42 -07001284 if METADATA_NAME in namelist:
1285 common.ZipDelete(input_file, METADATA_NAME)
1286 output_zip = zipfile.ZipFile(input_file, 'a')
1287 WriteMetadata(metadata, output_zip)
1288 common.ZipClose(output_zip)
1289
1290 if OPTIONS.no_signing:
1291 return input_file
1292
Tao Bao491d7e22018-02-21 13:17:22 -08001293 prelim_signing = common.MakeTempFile(suffix='.zip')
1294 SignOutput(input_file, prelim_signing)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001295 return prelim_signing
Tao Baofe5b69a2018-03-02 09:47:43 -08001296
Tao Baod2ce2ed2018-03-16 12:59:42 -07001297 def FinalizeAllPropertyFiles(prelim_signing, needed_property_files):
1298 with zipfile.ZipFile(prelim_signing) as prelim_signing_zip:
1299 for property_files in needed_property_files:
1300 metadata[property_files.name] = property_files.Finalize(
1301 prelim_signing_zip, len(metadata[property_files.name]))
1302
1303 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP
1304 # entries, as well as padding the entry headers. We do a preliminary signing
1305 # (with an incomplete metadata entry) to allow that to happen. Then compute
1306 # the ZIP entry offsets, write back the final metadata and do the final
1307 # signing.
1308 prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files)
1309 try:
1310 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
1311 except PropertyFiles.InsufficientSpaceException:
1312 # Even with the preliminary signing, the entry orders may change
1313 # dramatically, which leads to insufficiently reserved space during the
1314 # first call to ComputeAllPropertyFiles(). In that case, we redo all the
1315 # preliminary signing works, based on the already ordered ZIP entries, to
1316 # address the issue.
1317 prelim_signing = ComputeAllPropertyFiles(
1318 prelim_signing, needed_property_files)
1319 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
Tao Baofe5b69a2018-03-02 09:47:43 -08001320
1321 # Replace the METADATA entry.
1322 common.ZipDelete(prelim_signing, METADATA_NAME)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001323 output_zip = zipfile.ZipFile(prelim_signing, 'a')
Tao Baofe5b69a2018-03-02 09:47:43 -08001324 WriteMetadata(metadata, output_zip)
1325 common.ZipClose(output_zip)
1326
1327 # Re-sign the package after updating the metadata entry.
Tao Bao491d7e22018-02-21 13:17:22 -08001328 if OPTIONS.no_signing:
1329 output_file = prelim_signing
1330 else:
1331 SignOutput(prelim_signing, output_file)
Tao Baofe5b69a2018-03-02 09:47:43 -08001332
1333 # Reopen the final signed zip to double check the streaming metadata.
Tao Baod2ce2ed2018-03-16 12:59:42 -07001334 with zipfile.ZipFile(output_file) as output_zip:
Tao Baod3fc38a2018-03-08 16:09:01 -08001335 for property_files in needed_property_files:
1336 property_files.Verify(output_zip, metadata[property_files.name].strip())
Tao Baofe5b69a2018-03-02 09:47:43 -08001337
xunchang1cfe2512019-02-19 14:14:48 -08001338 # If requested, dump the metadata to a separate file.
1339 output_metadata_path = OPTIONS.output_metadata_path
1340 if output_metadata_path:
1341 WriteMetadata(metadata, output_metadata_path)
1342
Tao Baofe5b69a2018-03-02 09:47:43 -08001343
Tao Bao491d7e22018-02-21 13:17:22 -08001344def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
Tao Bao1c320f82019-10-04 23:25:12 -07001345 target_info = common.BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1346 source_info = common.BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -08001347
Tao Bao481bab82017-12-21 11:23:09 -08001348 target_api_version = target_info["recovery_api_version"]
1349 source_api_version = source_info["recovery_api_version"]
1350 if source_api_version == 0:
Tao Bao32fcdab2018-10-12 10:30:39 -07001351 logger.warning(
1352 "Generating edify script for a source that can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001353
Tao Bao481bab82017-12-21 11:23:09 -08001354 script = edify_generator.EdifyGenerator(
1355 source_api_version, target_info, fstab=source_info["fstab"])
1356
1357 if target_info.oem_props or source_info.oem_props:
1358 if not OPTIONS.oem_no_mount:
1359 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -07001360
Tao Baodf3a48b2018-01-10 16:30:43 -08001361 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -08001362
Tao Bao491d7e22018-02-21 13:17:22 -08001363 if not OPTIONS.no_signing:
1364 staging_file = common.MakeTempFile(suffix='.zip')
1365 else:
1366 staging_file = output_file
1367
1368 output_zip = zipfile.ZipFile(
1369 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
1370
Geremy Condra36bd3652014-02-06 19:45:10 -08001371 device_specific = common.DeviceSpecificParams(
1372 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001373 source_version=source_api_version,
Yifan Hong8a66a712019-04-04 15:37:57 -07001374 source_tmp=OPTIONS.source_tmp,
Geremy Condra36bd3652014-02-06 19:45:10 -08001375 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001376 target_version=target_api_version,
Yifan Hong8a66a712019-04-04 15:37:57 -07001377 target_tmp=OPTIONS.target_tmp,
Geremy Condra36bd3652014-02-06 19:45:10 -08001378 output_zip=output_zip,
1379 script=script,
1380 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -08001381 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001382
Geremy Condra36bd3652014-02-06 19:45:10 -08001383 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001384 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001385 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001386 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001387 updating_boot = (not OPTIONS.two_step and
1388 (source_boot.data != target_boot.data))
1389
Geremy Condra36bd3652014-02-06 19:45:10 -08001390 target_recovery = common.GetBootableImage(
1391 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -08001392
Tianjie Xuf67dd802019-05-20 17:50:36 -07001393 block_diff_dict = GetBlockDifferences(target_zip=target_zip,
1394 source_zip=source_zip,
1395 target_info=target_info,
1396 source_info=source_info,
1397 device_specific=device_specific)
Geremy Condra36bd3652014-02-06 19:45:10 -08001398
Yifan Hong9276cf02019-08-21 16:37:04 -07001399 CheckVintfIfTrebleEnabled(OPTIONS.target_tmp, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001400
Tao Bao481bab82017-12-21 11:23:09 -08001401 # Assertions (e.g. device properties check).
1402 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -08001403 device_specific.IncrementalOTA_Assertions()
1404
1405 # Two-step incremental package strategy (in chronological order,
1406 # which is *not* the order in which the generated script has
1407 # things):
1408 #
1409 # if stage is not "2/3" or "3/3":
1410 # do verification on current system
1411 # write recovery image to boot partition
1412 # set stage to "2/3"
1413 # reboot to boot partition and restart recovery
1414 # else if stage is "2/3":
1415 # write recovery image to recovery partition
1416 # set stage to "3/3"
1417 # reboot to recovery partition and restart recovery
1418 # else:
1419 # (stage must be "3/3")
1420 # perform update:
1421 # patch system files, etc.
1422 # force full install of new boot image
1423 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001424 # complete script normally
1425 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001426
1427 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001428 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001429 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001430 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001431 assert fs.fs_type.upper() == "EMMC", \
1432 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001433 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001434 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1435 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001436if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001437""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001438
1439 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1440 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001441 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001442 script.WriteRawImage("/recovery", "recovery.img")
1443 script.AppendExtra("""
1444set_stage("%(bcb_dev)s", "3/3");
1445reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001446else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001447""" % bcb_dev)
1448
Tao Baod42e97e2016-11-30 12:11:57 -08001449 # Stage 1/3: (a) Verify the current system.
1450 script.Comment("Stage 1/3")
1451
Tao Bao6c55a8a2015-04-08 15:30:27 -07001452 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001453 script.Print("Source: {}".format(source_info.fingerprint))
1454 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001455
Geremy Condra36bd3652014-02-06 19:45:10 -08001456 script.Print("Verifying current system...")
1457
1458 device_specific.IncrementalOTA_VerifyBegin()
1459
Tao Bao481bab82017-12-21 11:23:09 -08001460 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001461
Tao Baod8d14be2016-02-04 14:26:02 -08001462 # Check the required cache size (i.e. stashed blocks).
Tianjie Xuf67dd802019-05-20 17:50:36 -07001463 required_cache_sizes = [diff.required_cache for diff in
1464 block_diff_dict.values()]
Geremy Condra36bd3652014-02-06 19:45:10 -08001465 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -08001466 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001467 d = common.Difference(target_boot, source_boot)
1468 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001469 if d is None:
1470 include_full_boot = True
1471 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1472 else:
1473 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001474
Tao Bao32fcdab2018-10-12 10:30:39 -07001475 logger.info(
1476 "boot target: %d source: %d diff: %d", target_boot.size,
1477 source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -08001478
Tao Bao51216552018-08-26 11:53:15 -07001479 common.ZipWriteStr(output_zip, "boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001480
Tao Bao51216552018-08-26 11:53:15 -07001481 script.PatchPartitionCheck(
1482 "{}:{}:{}:{}".format(
1483 boot_type, boot_device, target_boot.size, target_boot.sha1),
1484 "{}:{}:{}:{}".format(
1485 boot_type, boot_device, source_boot.size, source_boot.sha1))
1486
Tianjie Xuf67dd802019-05-20 17:50:36 -07001487 required_cache_sizes.append(target_boot.size)
Tao Baod8d14be2016-02-04 14:26:02 -08001488
Tianjie Xuf67dd802019-05-20 17:50:36 -07001489 if required_cache_sizes:
1490 script.CacheFreeSpaceCheck(max(required_cache_sizes))
1491
1492 # Verify the existing partitions.
1493 for diff in block_diff_dict.values():
1494 diff.WriteVerifyScript(script, touched_blocks_only=True)
Geremy Condra36bd3652014-02-06 19:45:10 -08001495
1496 device_specific.IncrementalOTA_VerifyEnd()
1497
1498 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001499 # Stage 1/3: (b) Write recovery image to /boot.
1500 _WriteRecoveryImageToBoot(script, output_zip)
1501
Geremy Condra36bd3652014-02-06 19:45:10 -08001502 script.AppendExtra("""
1503set_stage("%(bcb_dev)s", "2/3");
1504reboot_now("%(bcb_dev)s", "");
1505else
1506""" % bcb_dev)
1507
Tao Baod42e97e2016-11-30 12:11:57 -08001508 # Stage 3/3: Make changes.
1509 script.Comment("Stage 3/3")
1510
Geremy Condra36bd3652014-02-06 19:45:10 -08001511 script.Comment("---- start making changes here ----")
1512
1513 device_specific.IncrementalOTA_InstallBegin()
1514
Tianjie Xuf67dd802019-05-20 17:50:36 -07001515 progress_dict = {partition: 0.1 for partition in block_diff_dict}
1516 progress_dict["system"] = 1 - len(block_diff_dict) * 0.1
Yifan Hong10c530d2018-12-27 17:34:18 -08001517
1518 if OPTIONS.source_info_dict.get("use_dynamic_partitions") == "true":
1519 if OPTIONS.target_info_dict.get("use_dynamic_partitions") != "true":
1520 raise RuntimeError(
1521 "can't generate incremental that disables dynamic partitions")
1522 dynamic_partitions_diff = common.DynamicPartitionsDifference(
1523 info_dict=OPTIONS.target_info_dict,
1524 source_info_dict=OPTIONS.source_info_dict,
Tianjie Xuf67dd802019-05-20 17:50:36 -07001525 block_diffs=block_diff_dict.values(),
Yifan Hong10c530d2018-12-27 17:34:18 -08001526 progress_dict=progress_dict)
1527 dynamic_partitions_diff.WriteScript(
1528 script, output_zip, write_verify_script=OPTIONS.verify)
1529 else:
Tianjie Xuf67dd802019-05-20 17:50:36 -07001530 for block_diff in block_diff_dict.values():
Yifan Hong10c530d2018-12-27 17:34:18 -08001531 block_diff.WriteScript(script, output_zip,
1532 progress=progress_dict.get(block_diff.partition),
1533 write_verify_script=OPTIONS.verify)
Geremy Condra36bd3652014-02-06 19:45:10 -08001534
1535 if OPTIONS.two_step:
1536 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1537 script.WriteRawImage("/boot", "boot.img")
Tao Bao32fcdab2018-10-12 10:30:39 -07001538 logger.info("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001539
1540 if not OPTIONS.two_step:
1541 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001542 if include_full_boot:
Tao Bao32fcdab2018-10-12 10:30:39 -07001543 logger.info("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001544 script.Print("Installing boot image...")
1545 script.WriteRawImage("/boot", "boot.img")
1546 else:
1547 # Produce the boot image by applying a patch to the current
1548 # contents of the boot partition, and write it back to the
1549 # partition.
Tao Bao32fcdab2018-10-12 10:30:39 -07001550 logger.info("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001551 script.Print("Patching boot image...")
1552 script.ShowProgress(0.1, 10)
Tao Bao51216552018-08-26 11:53:15 -07001553 script.PatchPartition(
1554 '{}:{}:{}:{}'.format(
1555 boot_type, boot_device, target_boot.size, target_boot.sha1),
1556 '{}:{}:{}:{}'.format(
1557 boot_type, boot_device, source_boot.size, source_boot.sha1),
1558 'boot.img.p')
Geremy Condra36bd3652014-02-06 19:45:10 -08001559 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001560 logger.info("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001561
1562 # Do device-specific installation (eg, write radio image).
1563 device_specific.IncrementalOTA_InstallEnd()
1564
1565 if OPTIONS.extra_script is not None:
1566 script.AppendExtra(OPTIONS.extra_script)
1567
Doug Zongker922206e2014-03-04 13:16:24 -08001568 if OPTIONS.wipe_user_data:
1569 script.Print("Erasing user data...")
1570 script.FormatPartition("/data")
1571
Geremy Condra36bd3652014-02-06 19:45:10 -08001572 if OPTIONS.two_step:
1573 script.AppendExtra("""
1574set_stage("%(bcb_dev)s", "");
1575endif;
1576endif;
1577""" % bcb_dev)
1578
1579 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001580 # For downgrade OTAs, we prefer to use the update-binary in the source
1581 # build that is actually newer than the one in the target build.
1582 if OPTIONS.downgrade:
1583 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1584 else:
1585 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001586 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001587
1588 # We haven't written the metadata entry yet, which will be handled in
1589 # FinalizeMetadata().
1590 common.ZipClose(output_zip)
1591
1592 # Sign the generated zip package unless no_signing is specified.
1593 needed_property_files = (
1594 NonAbOtaPropertyFiles(),
1595 )
1596 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Geremy Condra36bd3652014-02-06 19:45:10 -08001597
Doug Zongker32b527d2014-03-04 10:03:02 -08001598
Tao Bao15a146a2018-02-21 16:06:59 -08001599def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
Tao Baof7140c02018-01-30 17:09:24 -08001600 """Returns a target-files.zip file for generating secondary payload.
1601
1602 Although the original target-files.zip already contains secondary slot
1603 images (i.e. IMAGES/system_other.img), we need to rename the files to the
1604 ones without _other suffix. Note that we cannot instead modify the names in
1605 META/ab_partitions.txt, because there are no matching partitions on device.
1606
1607 For the partitions that don't have secondary images, the ones for primary
1608 slot will be used. This is to ensure that we always have valid boot, vbmeta,
1609 bootloader images in the inactive slot.
1610
1611 Args:
1612 input_file: The input target-files.zip file.
Tao Bao15a146a2018-02-21 16:06:59 -08001613 skip_postinstall: Whether to skip copying the postinstall config file.
Tao Baof7140c02018-01-30 17:09:24 -08001614
1615 Returns:
1616 The filename of the target-files.zip for generating secondary payload.
1617 """
Tianjie Xu1c808002019-09-11 00:29:26 -07001618
1619 def GetInfoForSecondaryImages(info_file):
1620 """Updates info file for secondary payload generation.
1621
1622 Scan each line in the info file, and remove the unwanted partitions from
1623 the dynamic partition list in the related properties. e.g.
1624 "super_google_dynamic_partitions_partition_list=system vendor product"
1625 will become "super_google_dynamic_partitions_partition_list=system".
1626
1627 Args:
1628 info_file: The input info file. e.g. misc_info.txt.
1629
1630 Returns:
1631 A string of the updated info content.
1632 """
1633
1634 output_list = []
1635 with open(info_file) as f:
1636 lines = f.read().splitlines()
1637
1638 # The suffix in partition_list variables that follows the name of the
1639 # partition group.
1640 LIST_SUFFIX = 'partition_list'
1641 for line in lines:
1642 if line.startswith('#') or '=' not in line:
1643 output_list.append(line)
1644 continue
1645 key, value = line.strip().split('=', 1)
1646 if key == 'dynamic_partition_list' or key.endswith(LIST_SUFFIX):
1647 partitions = value.split()
1648 partitions = [partition for partition in partitions if partition
Tao Bao3e759462019-09-17 22:43:11 -07001649 not in SECONDARY_PAYLOAD_SKIPPED_IMAGES]
Tianjie Xu1c808002019-09-11 00:29:26 -07001650 output_list.append('{}={}'.format(key, ' '.join(partitions)))
Yifan Hongfe073432019-11-01 12:28:31 -07001651 elif key == 'virtual_ab' or key == "virtual_ab_retrofit":
1652 # Remove virtual_ab flag from secondary payload so that OTA client
1653 # don't use snapshots for secondary update
1654 pass
Tianjie Xu1c808002019-09-11 00:29:26 -07001655 else:
1656 output_list.append(line)
1657 return '\n'.join(output_list)
1658
Tao Baof7140c02018-01-30 17:09:24 -08001659 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1660 target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
1661
Tao Baodba59ee2018-01-09 13:21:02 -08001662 with zipfile.ZipFile(input_file, 'r') as input_zip:
1663 infolist = input_zip.infolist()
Tao Bao12489802018-07-12 14:47:38 -07001664
Tao Bao0ff15de2019-03-20 11:26:06 -07001665 input_tmp = common.UnzipTemp(input_file, UNZIP_PATTERN)
Tao Baodba59ee2018-01-09 13:21:02 -08001666 for info in infolist:
Tao Baof7140c02018-01-30 17:09:24 -08001667 unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
1668 if info.filename == 'IMAGES/system_other.img':
1669 common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
1670
1671 # Primary images and friends need to be skipped explicitly.
1672 elif info.filename in ('IMAGES/system.img',
1673 'IMAGES/system.map'):
1674 pass
Tao Bao3e759462019-09-17 22:43:11 -07001675
1676 # Copy images that are not in SECONDARY_PAYLOAD_SKIPPED_IMAGES.
1677 elif info.filename.startswith(('IMAGES/', 'RADIO/')):
1678 image_name = os.path.basename(info.filename)
1679 if image_name not in ['{}.img'.format(partition) for partition in
1680 SECONDARY_PAYLOAD_SKIPPED_IMAGES]:
1681 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
Tao Baof7140c02018-01-30 17:09:24 -08001682
Tao Bao15a146a2018-02-21 16:06:59 -08001683 # Skip copying the postinstall config if requested.
1684 elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
1685 pass
1686
Tianjie Xu1c808002019-09-11 00:29:26 -07001687 elif info.filename.startswith('META/'):
1688 # Remove the unnecessary partitions for secondary images from the
1689 # ab_partitions file.
1690 if info.filename == AB_PARTITIONS:
1691 with open(unzipped_file) as f:
1692 partition_list = f.read().splitlines()
1693 partition_list = [partition for partition in partition_list if partition
Tao Bao3e759462019-09-17 22:43:11 -07001694 and partition not in SECONDARY_PAYLOAD_SKIPPED_IMAGES]
Tianjie Xu1c808002019-09-11 00:29:26 -07001695 common.ZipWriteStr(target_zip, info.filename, '\n'.join(partition_list))
1696 # Remove the unnecessary partitions from the dynamic partitions list.
1697 elif (info.filename == 'META/misc_info.txt' or
1698 info.filename == DYNAMIC_PARTITION_INFO):
1699 modified_info = GetInfoForSecondaryImages(unzipped_file)
1700 common.ZipWriteStr(target_zip, info.filename, modified_info)
1701 else:
1702 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
Tao Baof7140c02018-01-30 17:09:24 -08001703
Tao Baof7140c02018-01-30 17:09:24 -08001704 common.ZipClose(target_zip)
1705
1706 return target_file
1707
1708
Tao Bao15a146a2018-02-21 16:06:59 -08001709def GetTargetFilesZipWithoutPostinstallConfig(input_file):
1710 """Returns a target-files.zip that's not containing postinstall_config.txt.
1711
1712 This allows brillo_update_payload script to skip writing all the postinstall
1713 hooks in the generated payload. The input target-files.zip file will be
1714 duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
1715 contain the postinstall_config.txt entry, the input file will be returned.
1716
1717 Args:
1718 input_file: The input target-files.zip filename.
1719
1720 Returns:
1721 The filename of target-files.zip that doesn't contain postinstall config.
1722 """
1723 # We should only make a copy if postinstall_config entry exists.
1724 with zipfile.ZipFile(input_file, 'r') as input_zip:
1725 if POSTINSTALL_CONFIG not in input_zip.namelist():
1726 return input_file
1727
1728 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1729 shutil.copyfile(input_file, target_file)
1730 common.ZipDelete(target_file, POSTINSTALL_CONFIG)
1731 return target_file
1732
1733
Yifan Hong50e79542018-11-08 17:44:12 -08001734def GetTargetFilesZipForRetrofitDynamicPartitions(input_file,
Yifan Hongb433eba2019-03-06 12:42:53 -08001735 super_block_devices,
1736 dynamic_partition_list):
Yifan Hong50e79542018-11-08 17:44:12 -08001737 """Returns a target-files.zip for retrofitting dynamic partitions.
1738
1739 This allows brillo_update_payload to generate an OTA based on the exact
1740 bits on the block devices. Postinstall is disabled.
1741
1742 Args:
1743 input_file: The input target-files.zip filename.
1744 super_block_devices: The list of super block devices
Yifan Hongb433eba2019-03-06 12:42:53 -08001745 dynamic_partition_list: The list of dynamic partitions
Yifan Hong50e79542018-11-08 17:44:12 -08001746
1747 Returns:
1748 The filename of target-files.zip with *.img replaced with super_*.img for
1749 each block device in super_block_devices.
1750 """
1751 assert super_block_devices, "No super_block_devices are specified."
1752
1753 replace = {'OTA/super_{}.img'.format(dev): 'IMAGES/{}.img'.format(dev)
Tao Bao03fecb62018-11-28 10:59:23 -08001754 for dev in super_block_devices}
Yifan Hong50e79542018-11-08 17:44:12 -08001755
1756 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1757 shutil.copyfile(input_file, target_file)
1758
Tao Baoa3705452019-06-24 15:33:41 -07001759 with zipfile.ZipFile(input_file) as input_zip:
Yifan Hong50e79542018-11-08 17:44:12 -08001760 namelist = input_zip.namelist()
1761
Yifan Hongb433eba2019-03-06 12:42:53 -08001762 input_tmp = common.UnzipTemp(input_file, RETROFIT_DAP_UNZIP_PATTERN)
1763
1764 # Remove partitions from META/ab_partitions.txt that is in
1765 # dynamic_partition_list but not in super_block_devices so that
1766 # brillo_update_payload won't generate update for those logical partitions.
1767 ab_partitions_file = os.path.join(input_tmp, *AB_PARTITIONS.split('/'))
1768 with open(ab_partitions_file) as f:
1769 ab_partitions_lines = f.readlines()
1770 ab_partitions = [line.strip() for line in ab_partitions_lines]
1771 # Assert that all super_block_devices are in ab_partitions
1772 super_device_not_updated = [partition for partition in super_block_devices
1773 if partition not in ab_partitions]
1774 assert not super_device_not_updated, \
1775 "{} is in super_block_devices but not in {}".format(
1776 super_device_not_updated, AB_PARTITIONS)
1777 # ab_partitions -= (dynamic_partition_list - super_block_devices)
1778 new_ab_partitions = common.MakeTempFile(prefix="ab_partitions", suffix=".txt")
1779 with open(new_ab_partitions, 'w') as f:
1780 for partition in ab_partitions:
1781 if (partition in dynamic_partition_list and
1782 partition not in super_block_devices):
Tao Bao59cf0c52019-06-25 10:04:24 -07001783 logger.info("Dropping %s from ab_partitions.txt", partition)
1784 continue
Yifan Hongb433eba2019-03-06 12:42:53 -08001785 f.write(partition + "\n")
1786 to_delete = [AB_PARTITIONS]
1787
Yifan Hong50e79542018-11-08 17:44:12 -08001788 # Always skip postinstall for a retrofit update.
Yifan Hongb433eba2019-03-06 12:42:53 -08001789 to_delete += [POSTINSTALL_CONFIG]
Yifan Hong50e79542018-11-08 17:44:12 -08001790
1791 # Delete dynamic_partitions_info.txt so that brillo_update_payload thinks this
1792 # is a regular update on devices without dynamic partitions support.
1793 to_delete += [DYNAMIC_PARTITION_INFO]
1794
Tao Bao03fecb62018-11-28 10:59:23 -08001795 # Remove the existing partition images as well as the map files.
Tao Bao59cf0c52019-06-25 10:04:24 -07001796 to_delete += list(replace.values())
Tao Bao03fecb62018-11-28 10:59:23 -08001797 to_delete += ['IMAGES/{}.map'.format(dev) for dev in super_block_devices]
Yifan Hong50e79542018-11-08 17:44:12 -08001798
1799 common.ZipDelete(target_file, to_delete)
1800
Yifan Hong50e79542018-11-08 17:44:12 -08001801 target_zip = zipfile.ZipFile(target_file, 'a', allowZip64=True)
1802
1803 # Write super_{foo}.img as {foo}.img.
1804 for src, dst in replace.items():
1805 assert src in namelist, \
Tao Bao59cf0c52019-06-25 10:04:24 -07001806 'Missing {} in {}; {} cannot be written'.format(src, input_file, dst)
Yifan Hong50e79542018-11-08 17:44:12 -08001807 unzipped_file = os.path.join(input_tmp, *src.split('/'))
1808 common.ZipWrite(target_zip, unzipped_file, arcname=dst)
1809
Yifan Hongb433eba2019-03-06 12:42:53 -08001810 # Write new ab_partitions.txt file
1811 common.ZipWrite(target_zip, new_ab_partitions, arcname=AB_PARTITIONS)
1812
Yifan Hong50e79542018-11-08 17:44:12 -08001813 common.ZipClose(target_zip)
1814
1815 return target_file
1816
1817
Tao Baof0c4aa22018-04-30 20:29:30 -07001818def GenerateAbOtaPackage(target_file, output_file, source_file=None):
Tao Baofe5b69a2018-03-02 09:47:43 -08001819 """Generates an Android OTA package that has A/B update payload."""
Tao Baodea0f8b2016-06-20 17:55:06 -07001820 # Stage the output zip package for package signing.
Tao Bao491d7e22018-02-21 13:17:22 -08001821 if not OPTIONS.no_signing:
1822 staging_file = common.MakeTempFile(suffix='.zip')
1823 else:
1824 staging_file = output_file
Tao Baoa652c002018-03-01 19:31:38 -08001825 output_zip = zipfile.ZipFile(staging_file, "w",
Tao Baoc098e9e2016-01-07 13:03:56 -08001826 compression=zipfile.ZIP_DEFLATED)
1827
Tao Bao481bab82017-12-21 11:23:09 -08001828 if source_file is not None:
Tao Bao1c320f82019-10-04 23:25:12 -07001829 target_info = common.BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1830 source_info = common.BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Tao Bao481bab82017-12-21 11:23:09 -08001831 else:
Tao Bao1c320f82019-10-04 23:25:12 -07001832 target_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Tao Bao481bab82017-12-21 11:23:09 -08001833 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001834
Tao Bao481bab82017-12-21 11:23:09 -08001835 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001836 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001837
Yifan Hong50e79542018-11-08 17:44:12 -08001838 if OPTIONS.retrofit_dynamic_partitions:
1839 target_file = GetTargetFilesZipForRetrofitDynamicPartitions(
Yifan Hongb433eba2019-03-06 12:42:53 -08001840 target_file, target_info.get("super_block_devices").strip().split(),
1841 target_info.get("dynamic_partition_list").strip().split())
Yifan Hong50e79542018-11-08 17:44:12 -08001842 elif OPTIONS.skip_postinstall:
Tao Bao15a146a2018-02-21 16:06:59 -08001843 target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
1844
Tao Bao40b18822018-01-30 18:19:04 -08001845 # Generate payload.
1846 payload = Payload()
1847
1848 # Enforce a max timestamp this payload can be applied on top of.
Tao Baoff1b86e2017-10-03 14:17:57 -07001849 if OPTIONS.downgrade:
Tao Bao2a12ed72018-01-22 11:35:00 -08001850 max_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baoff1b86e2017-10-03 14:17:57 -07001851 else:
1852 max_timestamp = metadata["post-timestamp"]
Tao Bao40b18822018-01-30 18:19:04 -08001853 additional_args = ["--max_timestamp", max_timestamp]
Tao Baoc098e9e2016-01-07 13:03:56 -08001854
Tao Bao40b18822018-01-30 18:19:04 -08001855 payload.Generate(target_file, source_file, additional_args)
Tao Baoc098e9e2016-01-07 13:03:56 -08001856
Tao Bao40b18822018-01-30 18:19:04 -08001857 # Sign the payload.
Tao Baof7140c02018-01-30 17:09:24 -08001858 payload_signer = PayloadSigner()
1859 payload.Sign(payload_signer)
Tao Baoc098e9e2016-01-07 13:03:56 -08001860
Tao Bao40b18822018-01-30 18:19:04 -08001861 # Write the payload into output zip.
1862 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001863
Tao Baof7140c02018-01-30 17:09:24 -08001864 # Generate and include the secondary payload that installs secondary images
1865 # (e.g. system_other.img).
1866 if OPTIONS.include_secondary:
1867 # We always include a full payload for the secondary slot, even when
1868 # building an incremental OTA. See the comments for "--include_secondary".
Tao Bao15a146a2018-02-21 16:06:59 -08001869 secondary_target_file = GetTargetFilesZipForSecondaryImages(
1870 target_file, OPTIONS.skip_postinstall)
Tao Bao667ff572018-02-10 00:02:40 -08001871 secondary_payload = Payload(secondary=True)
Tao Baodb1fe412018-02-09 23:15:05 -08001872 secondary_payload.Generate(secondary_target_file,
1873 additional_args=additional_args)
Tao Baof7140c02018-01-30 17:09:24 -08001874 secondary_payload.Sign(payload_signer)
Tao Bao667ff572018-02-10 00:02:40 -08001875 secondary_payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08001876
Tianjie Xucfa86222016-03-07 16:31:19 -08001877 # If dm-verity is supported for the device, copy contents of care_map
1878 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001879 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08001880 if (target_info.get("verity") == "true" or
1881 target_info.get("avb_enable") == "true"):
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001882 care_map_list = [x for x in ["care_map.pb", "care_map.txt"] if
1883 "META/" + x in target_zip.namelist()]
1884
1885 # Adds care_map if either the protobuf format or the plain text one exists.
1886 if care_map_list:
1887 care_map_name = care_map_list[0]
1888 care_map_data = target_zip.read("META/" + care_map_name)
1889 # In order to support streaming, care_map needs to be packed as
Tao Bao40b18822018-01-30 18:19:04 -08001890 # ZIP_STORED.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001891 common.ZipWriteStr(output_zip, care_map_name, care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08001892 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001893 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001894 logger.warning("Cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07001895
Tao Bao21803d32017-04-19 10:16:09 -07001896 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001897
Yifan Hong9276cf02019-08-21 16:37:04 -07001898 CheckVintfIfTrebleEnabled(target_file, target_info)
1899
Tao Baofe5b69a2018-03-02 09:47:43 -08001900 # We haven't written the metadata entry yet, which will be handled in
1901 # FinalizeMetadata().
Tao Baoc96316c2017-01-24 22:10:49 -08001902 common.ZipClose(output_zip)
1903
Tao Bao85f16982018-03-08 16:28:33 -08001904 # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers
1905 # all the info of the latter. However, system updaters and OTA servers need to
1906 # take time to switch to the new flag. We keep both of the flags for
1907 # P-timeframe, and will remove StreamingPropertyFiles in later release.
Tao Baod3fc38a2018-03-08 16:09:01 -08001908 needed_property_files = (
Tao Bao85f16982018-03-08 16:28:33 -08001909 AbOtaPropertyFiles(),
Tao Baod3fc38a2018-03-08 16:09:01 -08001910 StreamingPropertyFiles(),
1911 )
1912 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Tao Baoc96316c2017-01-24 22:10:49 -08001913
Tao Baoc098e9e2016-01-07 13:03:56 -08001914
Tao Baof0c4aa22018-04-30 20:29:30 -07001915def GenerateNonAbOtaPackage(target_file, output_file, source_file=None):
1916 """Generates a non-A/B OTA package."""
1917 # Sanity check the loaded info dicts first.
1918 if OPTIONS.info_dict.get("no_recovery") == "true":
1919 raise common.ExternalError(
1920 "--- target build has specified no recovery ---")
1921
1922 # Non-A/B OTAs rely on /cache partition to store temporary files.
1923 cache_size = OPTIONS.info_dict.get("cache_size")
1924 if cache_size is None:
1925 logger.warning("--- can't determine the cache partition size ---")
1926 OPTIONS.cache_size = cache_size
1927
1928 if OPTIONS.extra_script is not None:
1929 with open(OPTIONS.extra_script) as fp:
1930 OPTIONS.extra_script = fp.read()
1931
1932 if OPTIONS.extracted_input is not None:
1933 OPTIONS.input_tmp = OPTIONS.extracted_input
1934 else:
1935 logger.info("unzipping target target-files...")
1936 OPTIONS.input_tmp = common.UnzipTemp(target_file, UNZIP_PATTERN)
1937 OPTIONS.target_tmp = OPTIONS.input_tmp
1938
1939 # If the caller explicitly specified the device-specific extensions path via
1940 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
1941 # is present in the target target_files. Otherwise, take the path of the file
1942 # from 'tool_extensions' in the info dict and look for that in the local
1943 # filesystem, relative to the current directory.
1944 if OPTIONS.device_specific is None:
1945 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1946 if os.path.exists(from_input):
1947 logger.info("(using device-specific extensions from target_files)")
1948 OPTIONS.device_specific = from_input
1949 else:
1950 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
1951
1952 if OPTIONS.device_specific is not None:
1953 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
1954
1955 # Generate a full OTA.
1956 if source_file is None:
1957 with zipfile.ZipFile(target_file) as input_zip:
1958 WriteFullOTAPackage(
1959 input_zip,
1960 output_file)
1961
1962 # Generate an incremental OTA.
1963 else:
1964 logger.info("unzipping source target-files...")
1965 OPTIONS.source_tmp = common.UnzipTemp(
1966 OPTIONS.incremental_source, UNZIP_PATTERN)
1967 with zipfile.ZipFile(target_file) as input_zip, \
1968 zipfile.ZipFile(source_file) as source_zip:
1969 WriteBlockIncrementalOTAPackage(
1970 input_zip,
1971 source_zip,
1972 output_file)
1973
1974
Tianjie Xu9afb2212020-05-10 21:48:15 +00001975def CalculateRuntimeFingerprints():
1976 """Returns a set of runtime fingerprints based on the boot variables."""
1977
1978 build_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
1979 fingerprints = {build_info.fingerprint}
1980
1981 if not OPTIONS.boot_variable_values:
1982 return fingerprints
1983
1984 # Calculate all possible combinations of the values for the boot variables.
1985 keys = OPTIONS.boot_variable_values.keys()
1986 value_list = OPTIONS.boot_variable_values.values()
1987 combinations = [dict(zip(keys, values))
1988 for values in itertools.product(*value_list)]
1989 for placeholder_values in combinations:
1990 # Reload the info_dict as some build properties may change their values
1991 # based on the value of ro.boot* properties.
1992 info_dict = copy.deepcopy(OPTIONS.info_dict)
1993 for partition in common.PARTITIONS_WITH_CARE_MAP:
1994 partition_prop_key = "{}.build.prop".format(partition)
1995 old_props = info_dict[partition_prop_key]
1996 info_dict[partition_prop_key] = common.PartitionBuildProps.FromInputFile(
1997 old_props.input_file, partition, placeholder_values)
1998 info_dict["build.prop"] = info_dict["system.build.prop"]
1999
2000 build_info = common.BuildInfo(info_dict, OPTIONS.oem_dicts)
2001 fingerprints.add(build_info.fingerprint)
2002 return fingerprints
2003
2004
Doug Zongkereef39442009-04-02 12:14:19 -07002005def main(argv):
2006
2007 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07002008 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07002009 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07002010 elif o in ("-i", "--incremental_from"):
2011 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07002012 elif o == "--full_radio":
2013 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07002014 elif o == "--full_bootloader":
2015 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08002016 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002017 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08002018 elif o == "--downgrade":
2019 OPTIONS.downgrade = True
2020 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08002021 elif o == "--override_timestamp":
Tao Baofaa8e0b2018-04-12 14:31:43 -07002022 OPTIONS.downgrade = True
Michael Runge6e836112014-04-15 17:40:21 -07002023 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08002024 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08002025 elif o == "--oem_no_mount":
2026 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07002027 elif o in ("-e", "--extra_script"):
2028 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02002029 elif o in ("-t", "--worker_threads"):
2030 if a.isdigit():
2031 OPTIONS.worker_threads = int(a)
2032 else:
2033 raise ValueError("Cannot parse value %r for option %r - only "
2034 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002035 elif o in ("-2", "--two_step"):
2036 OPTIONS.two_step = True
Tao Baof7140c02018-01-30 17:09:24 -08002037 elif o == "--include_secondary":
2038 OPTIONS.include_secondary = True
Doug Zongker26e66192014-02-20 13:22:07 -08002039 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002040 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07002041 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07002042 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08002043 elif o == "--block":
2044 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002045 elif o in ("-b", "--binary"):
2046 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07002047 elif o == "--stash_threshold":
2048 try:
2049 OPTIONS.stash_threshold = float(a)
2050 except ValueError:
2051 raise ValueError("Cannot parse value %r for option %r - expecting "
2052 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08002053 elif o == "--log_diff":
2054 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002055 elif o == "--payload_signer":
2056 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002057 elif o == "--payload_signer_args":
2058 OPTIONS.payload_signer_args = shlex.split(a)
Tianjie Xu21e6deb2019-10-07 18:01:00 -07002059 elif o == "--payload_signer_maximum_signature_size":
2060 OPTIONS.payload_signer_maximum_signature_size = a
xunchang376cc7c2019-04-08 23:04:58 -07002061 elif o == "--payload_signer_key_size":
Tianjie Xu21e6deb2019-10-07 18:01:00 -07002062 # TODO(Xunchang) remove this option after cleaning up the callers.
2063 logger.warning("The option '--payload_signer_key_size' is deprecated."
2064 " Use '--payload_signer_maximum_signature_size' instead.")
2065 OPTIONS.payload_signer_maximum_signature_size = a
Dan Willemsencea5cd22017-03-21 14:44:27 -07002066 elif o == "--extracted_input_target_files":
2067 OPTIONS.extracted_input = a
Tao Bao15a146a2018-02-21 16:06:59 -08002068 elif o == "--skip_postinstall":
2069 OPTIONS.skip_postinstall = True
Yifan Hong50e79542018-11-08 17:44:12 -08002070 elif o == "--retrofit_dynamic_partitions":
2071 OPTIONS.retrofit_dynamic_partitions = True
xunchangabfa2652019-02-19 16:27:10 -08002072 elif o == "--skip_compatibility_check":
2073 OPTIONS.skip_compatibility_check = True
xunchang1cfe2512019-02-19 14:14:48 -08002074 elif o == "--output_metadata_path":
2075 OPTIONS.output_metadata_path = a
Tianjie Xu1b079832019-08-28 12:19:23 -07002076 elif o == "--disable_fec_computation":
2077 OPTIONS.disable_fec_computation = True
Yifan Hong65afc072020-04-17 10:08:10 -07002078 elif o == "--force_non_ab":
2079 OPTIONS.force_non_ab = True
Doug Zongkereef39442009-04-02 12:14:19 -07002080 else:
2081 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002082 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002083
2084 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08002085 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002086 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07002087 "package_key=",
2088 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002089 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002090 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002091 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08002092 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08002093 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07002094 "extra_script=",
2095 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002096 "two_step",
Tao Baof7140c02018-01-30 17:09:24 -08002097 "include_secondary",
Dan Albert8b72aef2015-03-23 19:13:21 -07002098 "no_signing",
2099 "block",
2100 "binary=",
2101 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08002102 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002103 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07002104 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002105 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002106 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002107 "payload_signer_args=",
Tianjie Xu21e6deb2019-10-07 18:01:00 -07002108 "payload_signer_maximum_signature_size=",
xunchang376cc7c2019-04-08 23:04:58 -07002109 "payload_signer_key_size=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07002110 "extracted_input_target_files=",
Tao Bao15a146a2018-02-21 16:06:59 -08002111 "skip_postinstall",
Yifan Hong50e79542018-11-08 17:44:12 -08002112 "retrofit_dynamic_partitions",
xunchangabfa2652019-02-19 16:27:10 -08002113 "skip_compatibility_check",
xunchang1cfe2512019-02-19 14:14:48 -08002114 "output_metadata_path=",
Tianjie Xu1b079832019-08-28 12:19:23 -07002115 "disable_fec_computation",
Yifan Hong65afc072020-04-17 10:08:10 -07002116 "force_non_ab",
Dan Albert8b72aef2015-03-23 19:13:21 -07002117 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002118
2119 if len(args) != 2:
2120 common.Usage(__doc__)
2121 sys.exit(1)
2122
Tao Bao32fcdab2018-10-12 10:30:39 -07002123 common.InitLogging()
2124
Tao Bao5d182562016-02-23 11:38:39 -08002125 if OPTIONS.downgrade:
Tao Bao5d182562016-02-23 11:38:39 -08002126 # We should only allow downgrading incrementals (as opposed to full).
2127 # Otherwise the device may go back from arbitrary build with this full
2128 # OTA package.
2129 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002130 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08002131
Tao Bao2db13852018-01-08 22:28:57 -08002132 # Load the build info dicts from the zip directly or the extracted input
2133 # directory. We don't need to unzip the entire target-files zips, because they
2134 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
2135 # When loading the info dicts, we don't need to provide the second parameter
2136 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
2137 # some properties with their actual paths, such as 'selinux_fc',
2138 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07002139 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08002140 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07002141 else:
Tao Bao2db13852018-01-08 22:28:57 -08002142 with zipfile.ZipFile(args[0], 'r') as input_zip:
2143 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08002144
Tao Bao32fcdab2018-10-12 10:30:39 -07002145 logger.info("--- target info ---")
2146 common.DumpInfoDict(OPTIONS.info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002147
2148 # Load the source build dict if applicable.
2149 if OPTIONS.incremental_source is not None:
2150 OPTIONS.target_info_dict = OPTIONS.info_dict
2151 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
2152 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2153
Tao Bao32fcdab2018-10-12 10:30:39 -07002154 logger.info("--- source info ---")
2155 common.DumpInfoDict(OPTIONS.source_info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002156
2157 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08002158 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
2159
Yifan Hong50e79542018-11-08 17:44:12 -08002160 # Assume retrofitting dynamic partitions when base build does not set
Yifan Hong50611032018-11-20 14:27:38 -08002161 # use_dynamic_partitions but target build does.
Yifan Hong50e79542018-11-08 17:44:12 -08002162 if (OPTIONS.source_info_dict and
Yifan Hong50611032018-11-20 14:27:38 -08002163 OPTIONS.source_info_dict.get("use_dynamic_partitions") != "true" and
2164 OPTIONS.target_info_dict.get("use_dynamic_partitions") == "true"):
Yifan Hong50e79542018-11-08 17:44:12 -08002165 if OPTIONS.target_info_dict.get("dynamic_partition_retrofit") != "true":
2166 raise common.ExternalError(
2167 "Expect to generate incremental OTA for retrofitting dynamic "
2168 "partitions, but dynamic_partition_retrofit is not set in target "
2169 "build.")
2170 logger.info("Implicitly generating retrofit incremental OTA.")
2171 OPTIONS.retrofit_dynamic_partitions = True
2172
2173 # Skip postinstall for retrofitting dynamic partitions.
2174 if OPTIONS.retrofit_dynamic_partitions:
2175 OPTIONS.skip_postinstall = True
2176
Tao Baoc098e9e2016-01-07 13:03:56 -08002177 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
Yifan Hong65afc072020-04-17 10:08:10 -07002178 allow_non_ab = OPTIONS.info_dict.get("allow_non_ab") == "true"
2179 if OPTIONS.force_non_ab:
2180 assert allow_non_ab, "--force_non_ab only allowed on devices that supports non-A/B"
2181 assert ab_update, "--force_non_ab only allowed on A/B devices"
2182
2183 generate_ab = not OPTIONS.force_non_ab and ab_update
Tao Baoc098e9e2016-01-07 13:03:56 -08002184
Christian Oderf63e2cd2017-05-01 22:30:15 +02002185 # Use the default key to sign the package if not specified with package_key.
2186 # package_keys are needed on ab_updates, so always define them if an
Yifan Hong65afc072020-04-17 10:08:10 -07002187 # A/B update is getting created.
2188 if not OPTIONS.no_signing or generate_ab:
Christian Oderf63e2cd2017-05-01 22:30:15 +02002189 if OPTIONS.package_key is None:
2190 OPTIONS.package_key = OPTIONS.info_dict.get(
2191 "default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -07002192 "build/make/target/product/security/testkey")
Christian Oderf63e2cd2017-05-01 22:30:15 +02002193 # Get signing keys
2194 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
2195
Yifan Hong65afc072020-04-17 10:08:10 -07002196 if generate_ab:
Tao Baof0c4aa22018-04-30 20:29:30 -07002197 GenerateAbOtaPackage(
Tao Baoc098e9e2016-01-07 13:03:56 -08002198 target_file=args[0],
2199 output_file=args[1],
2200 source_file=OPTIONS.incremental_source)
2201
Dan Willemsencea5cd22017-03-21 14:44:27 -07002202 else:
Tao Baof0c4aa22018-04-30 20:29:30 -07002203 GenerateNonAbOtaPackage(
2204 target_file=args[0],
2205 output_file=args[1],
2206 source_file=OPTIONS.incremental_source)
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002207
Tao Baof0c4aa22018-04-30 20:29:30 -07002208 # Post OTA generation works.
2209 if OPTIONS.incremental_source is not None and OPTIONS.log_diff:
2210 logger.info("Generating diff logs...")
2211 logger.info("Unzipping target-files for diffing...")
2212 target_dir = common.UnzipTemp(args[0], TARGET_DIFFING_UNZIP_PATTERN)
2213 source_dir = common.UnzipTemp(
2214 OPTIONS.incremental_source, TARGET_DIFFING_UNZIP_PATTERN)
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002215
Tao Baof0c4aa22018-04-30 20:29:30 -07002216 with open(OPTIONS.log_diff, 'w') as out_file:
2217 import target_files_diff
2218 target_files_diff.recursiveDiff(
2219 '', source_dir, target_dir, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07002220
Tao Bao32fcdab2018-10-12 10:30:39 -07002221 logger.info("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002222
2223
2224if __name__ == '__main__':
2225 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002226 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002227 main(sys.argv[1:])
Tao Bao32fcdab2018-10-12 10:30:39 -07002228 except common.ExternalError:
2229 logger.exception("\n ERROR:\n")
Doug Zongkereef39442009-04-02 12:14:19 -07002230 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002231 finally:
2232 common.Cleanup()