blob: 60b73e798178963a93f12093da44904fac1eba2e [file] [log] [blame]
Doug Zongkereef39442009-04-02 12:14:19 -07001#!/usr/bin/env python
2#
3# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""
Tao Bao30df8b42018-04-23 15:32:53 -070018Given a target-files zipfile, produces an OTA package that installs that build.
19An incremental OTA is produced if -i is given, otherwise a full OTA is produced.
Doug Zongkereef39442009-04-02 12:14:19 -070020
Tao Bao30df8b42018-04-23 15:32:53 -070021Usage: ota_from_target_files [options] input_target_files output_ota_package
Doug Zongkereef39442009-04-02 12:14:19 -070022
Tao Bao30df8b42018-04-23 15:32:53 -070023Common options that apply to both of non-A/B and A/B OTAs
24
25 --downgrade
26 Intentionally generate an incremental OTA that updates from a newer build
Tao Baofaa8e0b2018-04-12 14:31:43 -070027 to an older one (e.g. downgrading from P preview back to O MR1).
28 "ota-downgrade=yes" will be set in the package metadata file. A data wipe
29 will always be enforced when using this flag, so "ota-wipe=yes" will also
30 be included in the metadata file. The update-binary in the source build
31 will be used in the OTA package, unless --binary flag is specified. Please
32 also check the comment for --override_timestamp below.
Tao Bao30df8b42018-04-23 15:32:53 -070033
34 -i (--incremental_from) <file>
35 Generate an incremental OTA using the given target-files zip as the
36 starting build.
37
38 -k (--package_key) <key>
39 Key to use to sign the package (default is the value of
40 default_system_dev_certificate from the input target-files's
Tao Bao59cf0c52019-06-25 10:04:24 -070041 META/misc_info.txt, or "build/make/target/product/security/testkey" if
42 that value is not specified).
Doug Zongkerafb32ea2011-09-22 10:28:04 -070043
44 For incremental OTAs, the default value is based on the source
45 target-file, not the target build.
Doug Zongkereef39442009-04-02 12:14:19 -070046
Tao Bao30df8b42018-04-23 15:32:53 -070047 --override_timestamp
48 Intentionally generate an incremental OTA that updates from a newer build
Tao Baofaa8e0b2018-04-12 14:31:43 -070049 to an older one (based on timestamp comparison), by setting the downgrade
50 flag in the package metadata. This differs from --downgrade flag, as we
51 don't enforce a data wipe with this flag. Because we know for sure this is
52 NOT an actual downgrade case, but two builds happen to be cut in a reverse
53 order (e.g. from two branches). A legit use case is that we cut a new
54 build C (after having A and B), but want to enfore an update path of A ->
55 C -> B. Specifying --downgrade may not help since that would enforce a
56 data wipe for C -> B update.
57
58 We used to set a fake timestamp in the package metadata for this flow. But
59 now we consolidate the two cases (i.e. an actual downgrade, or a downgrade
60 based on timestamp) with the same "ota-downgrade=yes" flag, with the
61 difference being whether "ota-wipe=yes" is set.
Doug Zongkereef39442009-04-02 12:14:19 -070062
Tao Bao30df8b42018-04-23 15:32:53 -070063 --wipe_user_data
64 Generate an OTA package that will wipe the user data partition when
65 installed.
66
Yifan Hong50e79542018-11-08 17:44:12 -080067 --retrofit_dynamic_partitions
68 Generates an OTA package that updates a device to support dynamic
69 partitions (default False). This flag is implied when generating
70 an incremental OTA where the base build does not support dynamic
71 partitions but the target build does. For A/B, when this flag is set,
72 --skip_postinstall is implied.
73
xunchangabfa2652019-02-19 16:27:10 -080074 --skip_compatibility_check
Yifan Hong9276cf02019-08-21 16:37:04 -070075 Skip checking compatibility of the input target files package.
xunchangabfa2652019-02-19 16:27:10 -080076
xunchang1cfe2512019-02-19 14:14:48 -080077 --output_metadata_path
78 Write a copy of the metadata to a separate file. Therefore, users can
79 read the post build fingerprint without extracting the OTA package.
80
Tao Bao30df8b42018-04-23 15:32:53 -070081Non-A/B OTA specific options
82
83 -b (--binary) <file>
84 Use the given binary as the update-binary in the output package, instead
85 of the binary in the build's target_files. Use for development only.
86
87 --block
88 Generate a block-based OTA for non-A/B device. We have deprecated the
89 support for file-based OTA since O. Block-based OTA will be used by
90 default for all non-A/B devices. Keeping this flag here to not break
91 existing callers.
92
93 -e (--extra_script) <file>
94 Insert the contents of file at the end of the update script.
Tao Bao43078aa2015-04-21 14:32:35 -070095
leozwangaa6c1a12015-08-14 10:57:58 -070096 --full_bootloader
97 Similar to --full_radio. When generating an incremental OTA, always
98 include a full copy of bootloader image.
99
Tao Bao30df8b42018-04-23 15:32:53 -0700100 --full_radio
101 When generating an incremental OTA, always include a full copy of radio
102 image. This option is only meaningful when -i is specified, because a full
103 radio is always included in a full OTA if applicable.
Michael Runge63f01de2014-10-28 19:24:19 -0700104
Tao Bao30df8b42018-04-23 15:32:53 -0700105 --log_diff <file>
106 Generate a log file that shows the differences in the source and target
107 builds for an incremental package. This option is only meaningful when -i
108 is specified.
109
110 -o (--oem_settings) <main_file[,additional_files...]>
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800111 Comma seperated list of files used to specify the expected OEM-specific
Tao Bao481bab82017-12-21 11:23:09 -0800112 properties on the OEM partition of the intended device. Multiple expected
113 values can be used by providing multiple files. Only the first dict will
114 be used to compute fingerprint, while the rest will be used to assert
115 OEM-specific properties.
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800116
Tao Bao8608cde2016-02-25 19:49:55 -0800117 --oem_no_mount
Tao Bao30df8b42018-04-23 15:32:53 -0700118 For devices with OEM-specific properties but without an OEM partition, do
119 not mount the OEM partition in the updater-script. This should be very
120 rarely used, since it's expected to have a dedicated OEM partition for
121 OEM-specific properties. Only meaningful when -o is specified.
Tao Bao8608cde2016-02-25 19:49:55 -0800122
Tao Bao30df8b42018-04-23 15:32:53 -0700123 --stash_threshold <float>
124 Specify the threshold that will be used to compute the maximum allowed
125 stash size (defaults to 0.8).
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700126
Tao Bao30df8b42018-04-23 15:32:53 -0700127 -t (--worker_threads) <int>
128 Specify the number of worker-threads that will be used when generating
129 patches for incremental updates (defaults to 3).
Tao Bao3e6161a2017-02-28 11:48:48 -0800130
Tao Bao30df8b42018-04-23 15:32:53 -0700131 --verify
132 Verify the checksums of the updated system and vendor (if any) partitions.
133 Non-A/B incremental OTAs only.
Doug Zongker1c390a22009-05-14 19:06:36 -0700134
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800135 -2 (--two_step)
Tao Bao30df8b42018-04-23 15:32:53 -0700136 Generate a 'two-step' OTA package, where recovery is updated first, so
137 that any changes made to the system partition are done using the new
138 recovery (new kernel, etc.).
139
140A/B OTA specific options
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800141
Tao Baof7140c02018-01-30 17:09:24 -0800142 --include_secondary
143 Additionally include the payload for secondary slot images (default:
144 False). Only meaningful when generating A/B OTAs.
145
146 By default, an A/B OTA package doesn't contain the images for the
147 secondary slot (e.g. system_other.img). Specifying this flag allows
148 generating a separate payload that will install secondary slot images.
149
150 Such a package needs to be applied in a two-stage manner, with a reboot
151 in-between. During the first stage, the updater applies the primary
152 payload only. Upon finishing, it reboots the device into the newly updated
153 slot. It then continues to install the secondary payload to the inactive
154 slot, but without switching the active slot at the end (needs the matching
155 support in update_engine, i.e. SWITCH_SLOT_ON_REBOOT flag).
156
157 Due to the special install procedure, the secondary payload will be always
158 generated as a full payload.
159
Tao Baodea0f8b2016-06-20 17:55:06 -0700160 --payload_signer <signer>
161 Specify the signer when signing the payload and metadata for A/B OTAs.
162 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
163 with the package private key. If the private key cannot be accessed
164 directly, a payload signer that knows how to do that should be specified.
165 The signer will be supplied with "-inkey <path_to_key>",
166 "-in <input_file>" and "-out <output_file>" parameters.
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700167
168 --payload_signer_args <args>
169 Specify the arguments needed for payload signer.
Tao Bao15a146a2018-02-21 16:06:59 -0800170
xunchang376cc7c2019-04-08 23:04:58 -0700171 --payload_signer_key_size <key_size>
172 Specify the key size in bytes of the payload signer.
173
Tao Bao15a146a2018-02-21 16:06:59 -0800174 --skip_postinstall
175 Skip the postinstall hooks when generating an A/B OTA package (default:
176 False). Note that this discards ALL the hooks, including non-optional
177 ones. Should only be used if caller knows it's safe to do so (e.g. all the
178 postinstall work is to dexopt apps and a data wipe will happen immediately
179 after). Only meaningful when generating A/B OTAs.
Doug Zongkereef39442009-04-02 12:14:19 -0700180"""
181
Tao Bao89fbb0f2017-01-10 10:47:58 -0800182from __future__ import print_function
183
Tianjie Xuf67dd802019-05-20 17:50:36 -0700184import collections
Tao Bao32fcdab2018-10-12 10:30:39 -0700185import logging
Doug Zongkerfc44a512014-08-26 13:10:25 -0700186import multiprocessing
Tao Bao2dd1c482017-02-03 16:49:39 -0800187import os.path
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700188import shlex
Tao Bao15a146a2018-02-21 16:06:59 -0800189import shutil
Tao Bao85f16982018-03-08 16:28:33 -0800190import struct
Tao Bao481bab82017-12-21 11:23:09 -0800191import sys
Doug Zongkereef39442009-04-02 12:14:19 -0700192import zipfile
193
Yifan Hong9276cf02019-08-21 16:37:04 -0700194import check_target_files_vintf
Doug Zongkereef39442009-04-02 12:14:19 -0700195import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700196import edify_generator
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700197import verity_utils
Doug Zongkereef39442009-04-02 12:14:19 -0700198
Tao Bao481bab82017-12-21 11:23:09 -0800199if sys.hexversion < 0x02070000:
200 print("Python 2.7 or newer is required.", file=sys.stderr)
201 sys.exit(1)
202
Tao Bao32fcdab2018-10-12 10:30:39 -0700203logger = logging.getLogger(__name__)
Tao Bao481bab82017-12-21 11:23:09 -0800204
Doug Zongkereef39442009-04-02 12:14:19 -0700205OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700206OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700207OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700208OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700209OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700210OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800211OPTIONS.downgrade = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700212OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700213OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
214if OPTIONS.worker_threads == 0:
215 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800216OPTIONS.two_step = False
Tao Baof7140c02018-01-30 17:09:24 -0800217OPTIONS.include_secondary = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900218OPTIONS.no_signing = False
Tao Bao457cbf62017-03-06 09:56:01 -0800219OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -0800220OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700221OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800222OPTIONS.oem_no_mount = False
Tao Bao43078aa2015-04-21 14:32:35 -0700223OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700224OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700225# Stash size cannot exceed cache_size * threshold.
226OPTIONS.cache_size = None
227OPTIONS.stash_threshold = 0.8
Tao Baod62c6032015-11-30 09:40:20 -0800228OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700229OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700230OPTIONS.payload_signer_args = []
xunchang376cc7c2019-04-08 23:04:58 -0700231OPTIONS.payload_signer_key_size = None
Tao Bao5f8ff932017-03-21 22:35:00 -0700232OPTIONS.extracted_input = None
Christian Oderf63e2cd2017-05-01 22:30:15 +0200233OPTIONS.key_passwords = []
Tao Bao15a146a2018-02-21 16:06:59 -0800234OPTIONS.skip_postinstall = False
Yifan Hong50e79542018-11-08 17:44:12 -0800235OPTIONS.retrofit_dynamic_partitions = False
xunchangabfa2652019-02-19 16:27:10 -0800236OPTIONS.skip_compatibility_check = False
xunchang1cfe2512019-02-19 14:14:48 -0800237OPTIONS.output_metadata_path = None
Tao Bao15a146a2018-02-21 16:06:59 -0800238
Tao Bao8dcf7382015-05-21 14:09:49 -0700239
Tao Bao2dd1c482017-02-03 16:49:39 -0800240METADATA_NAME = 'META-INF/com/android/metadata'
Tao Bao15a146a2018-02-21 16:06:59 -0800241POSTINSTALL_CONFIG = 'META/postinstall_config.txt'
Yifan Hong50e79542018-11-08 17:44:12 -0800242DYNAMIC_PARTITION_INFO = 'META/dynamic_partitions_info.txt'
Yifan Hongb433eba2019-03-06 12:42:53 -0800243AB_PARTITIONS = 'META/ab_partitions.txt'
Tao Bao04808502019-07-25 23:11:41 -0700244UNZIP_PATTERN = ['IMAGES/*', 'META/*', 'OTA/*', 'RADIO/*']
Tao Baof0c4aa22018-04-30 20:29:30 -0700245# Files to be unzipped for target diffing purpose.
246TARGET_DIFFING_UNZIP_PATTERN = ['BOOT', 'RECOVERY', 'SYSTEM/*', 'VENDOR/*',
247 'PRODUCT/*', 'SYSTEM_EXT/*', 'ODM/*']
Yifan Hongb433eba2019-03-06 12:42:53 -0800248RETROFIT_DAP_UNZIP_PATTERN = ['OTA/super_*.img', AB_PARTITIONS]
Tao Bao6b0b2f92017-03-05 11:38:11 -0800249
Tao Bao2dd1c482017-02-03 16:49:39 -0800250
Tao Bao481bab82017-12-21 11:23:09 -0800251class BuildInfo(object):
252 """A class that holds the information for a given build.
253
254 This class wraps up the property querying for a given source or target build.
255 It abstracts away the logic of handling OEM-specific properties, and caches
256 the commonly used properties such as fingerprint.
257
258 There are two types of info dicts: a) build-time info dict, which is generated
259 at build time (i.e. included in a target_files zip); b) OEM info dict that is
260 specified at package generation time (via command line argument
261 '--oem_settings'). If a build doesn't use OEM-specific properties (i.e. not
262 having "oem_fingerprint_properties" in build-time info dict), all the queries
263 would be answered based on build-time info dict only. Otherwise if using
264 OEM-specific properties, some of them will be calculated from two info dicts.
265
266 Users can query properties similarly as using a dict() (e.g. info['fstab']),
267 or to query build properties via GetBuildProp() or GetVendorBuildProp().
268
269 Attributes:
270 info_dict: The build-time info dict.
271 is_ab: Whether it's a build that uses A/B OTA.
272 oem_dicts: A list of OEM dicts.
273 oem_props: A list of OEM properties that should be read from OEM dicts; None
274 if the build doesn't use any OEM-specific property.
275 fingerprint: The fingerprint of the build, which would be calculated based
276 on OEM properties if applicable.
277 device: The device name, which could come from OEM dicts if applicable.
278 """
279
Steven Laver9e73e822019-01-29 20:20:08 -0800280 _RO_PRODUCT_RESOLVE_PROPS = ["ro.product.brand", "ro.product.device",
281 "ro.product.manufacturer", "ro.product.model",
282 "ro.product.name"]
Justin Yun6151e3f2019-06-25 15:58:13 +0900283 _RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER = ["product", "odm", "vendor",
284 "system_ext", "system"]
Steven Laver9e73e822019-01-29 20:20:08 -0800285
Tao Bao481bab82017-12-21 11:23:09 -0800286 def __init__(self, info_dict, oem_dicts):
287 """Initializes a BuildInfo instance with the given dicts.
288
Tao Bao667c7532018-07-06 10:13:59 -0700289 Note that it only wraps up the given dicts, without making copies.
290
Tao Bao481bab82017-12-21 11:23:09 -0800291 Arguments:
292 info_dict: The build-time info dict.
293 oem_dicts: A list of OEM dicts (which is parsed from --oem_settings). Note
294 that it always uses the first dict to calculate the fingerprint or the
295 device name. The rest would be used for asserting OEM properties only
Tao Bao667c7532018-07-06 10:13:59 -0700296 (e.g. one package can be installed on one of these devices).
Tao Bao481bab82017-12-21 11:23:09 -0800297 """
298 self.info_dict = info_dict
299 self.oem_dicts = oem_dicts
300
301 self._is_ab = info_dict.get("ab_update") == "true"
302 self._oem_props = info_dict.get("oem_fingerprint_properties")
303
304 if self._oem_props:
305 assert oem_dicts, "OEM source required for this build"
306
307 # These two should be computed only after setting self._oem_props.
308 self._device = self.GetOemProperty("ro.product.device")
309 self._fingerprint = self.CalculateFingerprint()
310
311 @property
312 def is_ab(self):
313 return self._is_ab
314
315 @property
316 def device(self):
317 return self._device
318
319 @property
320 def fingerprint(self):
321 return self._fingerprint
322
323 @property
Tao Baoea6cbd02018-09-05 13:06:37 -0700324 def vendor_fingerprint(self):
Yifan Hong51d37562019-04-23 17:06:46 -0700325 return self._fingerprint_of("vendor")
326
327 @property
328 def product_fingerprint(self):
329 return self._fingerprint_of("product")
330
331 @property
332 def odm_fingerprint(self):
333 return self._fingerprint_of("odm")
334
335 def _fingerprint_of(self, partition):
336 if partition + ".build.prop" not in self.info_dict:
Tao Baoea6cbd02018-09-05 13:06:37 -0700337 return None
Yifan Hong51d37562019-04-23 17:06:46 -0700338 build_prop = self.info_dict[partition + ".build.prop"]
339 if "ro." + partition + ".build.fingerprint" in build_prop:
340 return build_prop["ro." + partition + ".build.fingerprint"]
341 if "ro." + partition + ".build.thumbprint" in build_prop:
342 return build_prop["ro." + partition + ".build.thumbprint"]
Tao Baoea6cbd02018-09-05 13:06:37 -0700343 return None
344
345 @property
Tao Bao481bab82017-12-21 11:23:09 -0800346 def oem_props(self):
347 return self._oem_props
348
349 def __getitem__(self, key):
350 return self.info_dict[key]
351
Tao Bao667c7532018-07-06 10:13:59 -0700352 def __setitem__(self, key, value):
353 self.info_dict[key] = value
354
Tao Bao481bab82017-12-21 11:23:09 -0800355 def get(self, key, default=None):
356 return self.info_dict.get(key, default)
357
Tao Bao667c7532018-07-06 10:13:59 -0700358 def items(self):
359 return self.info_dict.items()
360
Tao Bao481bab82017-12-21 11:23:09 -0800361 def GetBuildProp(self, prop):
362 """Returns the inquired build property."""
Steven Laver9e73e822019-01-29 20:20:08 -0800363 if prop in BuildInfo._RO_PRODUCT_RESOLVE_PROPS:
364 return self._ResolveRoProductBuildProp(prop)
365
Tao Bao481bab82017-12-21 11:23:09 -0800366 try:
367 return self.info_dict.get("build.prop", {})[prop]
368 except KeyError:
369 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
370
Steven Laver9e73e822019-01-29 20:20:08 -0800371 def _ResolveRoProductBuildProp(self, prop):
372 """Resolves the inquired ro.product.* build property"""
373 prop_val = self.info_dict.get("build.prop", {}).get(prop)
374 if prop_val:
375 return prop_val
376
377 source_order_val = self.info_dict.get("build.prop", {}).get(
Tao Bao59cf0c52019-06-25 10:04:24 -0700378 "ro.product.property_source_order")
Steven Laver9e73e822019-01-29 20:20:08 -0800379 if source_order_val:
380 source_order = source_order_val.split(",")
381 else:
382 source_order = BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER
383
384 # Check that all sources in ro.product.property_source_order are valid
385 if any([x not in BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER
386 for x in source_order]):
387 raise common.ExternalError(
Tao Bao59cf0c52019-06-25 10:04:24 -0700388 "Invalid ro.product.property_source_order '{}'".format(source_order))
Steven Laver9e73e822019-01-29 20:20:08 -0800389
390 for source in source_order:
Tao Bao59cf0c52019-06-25 10:04:24 -0700391 source_prop = prop.replace(
392 "ro.product", "ro.product.{}".format(source), 1)
393 prop_val = self.info_dict.get(
394 "{}.build.prop".format(source), {}).get(source_prop)
Steven Laver9e73e822019-01-29 20:20:08 -0800395 if prop_val:
396 return prop_val
397
398 raise common.ExternalError("couldn't resolve {}".format(prop))
399
Tao Bao481bab82017-12-21 11:23:09 -0800400 def GetVendorBuildProp(self, prop):
401 """Returns the inquired vendor build property."""
402 try:
403 return self.info_dict.get("vendor.build.prop", {})[prop]
404 except KeyError:
405 raise common.ExternalError(
406 "couldn't find %s in vendor.build.prop" % (prop,))
407
408 def GetOemProperty(self, key):
409 if self.oem_props is not None and key in self.oem_props:
410 return self.oem_dicts[0][key]
411 return self.GetBuildProp(key)
412
413 def CalculateFingerprint(self):
414 if self.oem_props is None:
Steven Laver9e73e822019-01-29 20:20:08 -0800415 try:
416 return self.GetBuildProp("ro.build.fingerprint")
417 except common.ExternalError:
418 return "{}/{}/{}:{}/{}/{}:{}/{}".format(
Tao Bao59cf0c52019-06-25 10:04:24 -0700419 self.GetBuildProp("ro.product.brand"),
420 self.GetBuildProp("ro.product.name"),
421 self.GetBuildProp("ro.product.device"),
422 self.GetBuildProp("ro.build.version.release"),
423 self.GetBuildProp("ro.build.id"),
424 self.GetBuildProp("ro.build.version.incremental"),
425 self.GetBuildProp("ro.build.type"),
426 self.GetBuildProp("ro.build.tags"))
Tao Bao481bab82017-12-21 11:23:09 -0800427 return "%s/%s/%s:%s" % (
428 self.GetOemProperty("ro.product.brand"),
429 self.GetOemProperty("ro.product.name"),
430 self.GetOemProperty("ro.product.device"),
431 self.GetBuildProp("ro.build.thumbprint"))
432
433 def WriteMountOemScript(self, script):
434 assert self.oem_props is not None
435 recovery_mount_options = self.info_dict.get("recovery_mount_options")
436 script.Mount("/oem", recovery_mount_options)
437
438 def WriteDeviceAssertions(self, script, oem_no_mount):
439 # Read the property directly if not using OEM properties.
440 if not self.oem_props:
441 script.AssertDevice(self.device)
442 return
443
444 # Otherwise assert OEM properties.
445 if not self.oem_dicts:
446 raise common.ExternalError(
447 "No OEM file provided to answer expected assertions")
448
449 for prop in self.oem_props.split():
450 values = []
451 for oem_dict in self.oem_dicts:
452 if prop in oem_dict:
453 values.append(oem_dict[prop])
454 if not values:
455 raise common.ExternalError(
456 "The OEM file is missing the property %s" % (prop,))
457 script.AssertOemProperty(prop, values, oem_no_mount)
458
459
Tao Baofabe0832018-01-17 15:52:28 -0800460class PayloadSigner(object):
461 """A class that wraps the payload signing works.
462
463 When generating a Payload, hashes of the payload and metadata files will be
464 signed with the device key, either by calling an external payload signer or
465 by calling openssl with the package key. This class provides a unified
466 interface, so that callers can just call PayloadSigner.Sign().
467
468 If an external payload signer has been specified (OPTIONS.payload_signer), it
469 calls the signer with the provided args (OPTIONS.payload_signer_args). Note
470 that the signing key should be provided as part of the payload_signer_args.
471 Otherwise without an external signer, it uses the package key
472 (OPTIONS.package_key) and calls openssl for the signing works.
473 """
474
475 def __init__(self):
476 if OPTIONS.payload_signer is None:
477 # Prepare the payload signing key.
478 private_key = OPTIONS.package_key + OPTIONS.private_key_suffix
479 pw = OPTIONS.key_passwords[OPTIONS.package_key]
480
481 cmd = ["openssl", "pkcs8", "-in", private_key, "-inform", "DER"]
482 cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"])
483 signing_key = common.MakeTempFile(prefix="key-", suffix=".key")
484 cmd.extend(["-out", signing_key])
Tao Baobec89c12018-10-15 11:53:28 -0700485 common.RunAndCheckOutput(cmd, verbose=False)
Tao Baofabe0832018-01-17 15:52:28 -0800486
487 self.signer = "openssl"
488 self.signer_args = ["pkeyutl", "-sign", "-inkey", signing_key,
489 "-pkeyopt", "digest:sha256"]
xunchang376cc7c2019-04-08 23:04:58 -0700490 self.key_size = self._GetKeySizeInBytes(signing_key)
Tao Baofabe0832018-01-17 15:52:28 -0800491 else:
492 self.signer = OPTIONS.payload_signer
493 self.signer_args = OPTIONS.payload_signer_args
xunchang376cc7c2019-04-08 23:04:58 -0700494 if OPTIONS.payload_signer_key_size:
495 self.key_size = int(OPTIONS.payload_signer_key_size)
496 assert self.key_size == 256 or self.key_size == 512, \
497 "Unsupported key size {}".format(OPTIONS.payload_signer_key_size)
498 else:
499 self.key_size = 256
500
501 @staticmethod
502 def _GetKeySizeInBytes(signing_key):
503 modulus_file = common.MakeTempFile(prefix="modulus-")
504 cmd = ["openssl", "rsa", "-inform", "PEM", "-in", signing_key, "-modulus",
505 "-noout", "-out", modulus_file]
506 common.RunAndCheckOutput(cmd, verbose=False)
507
508 with open(modulus_file) as f:
509 modulus_string = f.read()
510 # The modulus string has the format "Modulus=$data", where $data is the
511 # concatenation of hex dump of the modulus.
512 MODULUS_PREFIX = "Modulus="
513 assert modulus_string.startswith(MODULUS_PREFIX)
514 modulus_string = modulus_string[len(MODULUS_PREFIX):]
Tao Bao59cf0c52019-06-25 10:04:24 -0700515 key_size = len(modulus_string) // 2
xunchang376cc7c2019-04-08 23:04:58 -0700516 assert key_size == 256 or key_size == 512, \
517 "Unsupported key size {}".format(key_size)
518 return key_size
Tao Baofabe0832018-01-17 15:52:28 -0800519
520 def Sign(self, in_file):
521 """Signs the given input file. Returns the output filename."""
522 out_file = common.MakeTempFile(prefix="signed-", suffix=".bin")
523 cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file]
Tao Bao718faed2019-08-02 13:24:19 -0700524 common.RunAndCheckOutput(cmd)
Tao Baofabe0832018-01-17 15:52:28 -0800525 return out_file
526
527
Tao Bao40b18822018-01-30 18:19:04 -0800528class Payload(object):
529 """Manages the creation and the signing of an A/B OTA Payload."""
530
531 PAYLOAD_BIN = 'payload.bin'
532 PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
Tao Baof7140c02018-01-30 17:09:24 -0800533 SECONDARY_PAYLOAD_BIN = 'secondary/payload.bin'
534 SECONDARY_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt'
Tao Bao40b18822018-01-30 18:19:04 -0800535
Tao Bao667ff572018-02-10 00:02:40 -0800536 def __init__(self, secondary=False):
537 """Initializes a Payload instance.
538
539 Args:
540 secondary: Whether it's generating a secondary payload (default: False).
541 """
Tao Bao40b18822018-01-30 18:19:04 -0800542 self.payload_file = None
543 self.payload_properties = None
Tao Bao667ff572018-02-10 00:02:40 -0800544 self.secondary = secondary
Tao Bao40b18822018-01-30 18:19:04 -0800545
Tao Baof0c4aa22018-04-30 20:29:30 -0700546 def _Run(self, cmd): # pylint: disable=no-self-use
Tao Bao718faed2019-08-02 13:24:19 -0700547 # Don't pipe (buffer) the output if verbose is set. Let
548 # brillo_update_payload write to stdout/stderr directly, so its progress can
549 # be monitored.
550 if OPTIONS.verbose:
551 common.RunAndCheckOutput(cmd, stdout=None, stderr=None)
552 else:
553 common.RunAndCheckOutput(cmd)
554
Tao Bao40b18822018-01-30 18:19:04 -0800555 def Generate(self, target_file, source_file=None, additional_args=None):
556 """Generates a payload from the given target-files zip(s).
557
558 Args:
559 target_file: The filename of the target build target-files zip.
560 source_file: The filename of the source build target-files zip; or None if
561 generating a full OTA.
562 additional_args: A list of additional args that should be passed to
563 brillo_update_payload script; or None.
564 """
565 if additional_args is None:
566 additional_args = []
567
568 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
569 cmd = ["brillo_update_payload", "generate",
570 "--payload", payload_file,
571 "--target_image", target_file]
572 if source_file is not None:
573 cmd.extend(["--source_image", source_file])
574 cmd.extend(additional_args)
Tao Bao718faed2019-08-02 13:24:19 -0700575 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800576
577 self.payload_file = payload_file
578 self.payload_properties = None
579
580 def Sign(self, payload_signer):
581 """Generates and signs the hashes of the payload and metadata.
582
583 Args:
584 payload_signer: A PayloadSigner() instance that serves the signing work.
585
586 Raises:
587 AssertionError: On any failure when calling brillo_update_payload script.
588 """
589 assert isinstance(payload_signer, PayloadSigner)
590
591 # 1. Generate hashes of the payload and metadata files.
592 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
593 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
594 cmd = ["brillo_update_payload", "hash",
595 "--unsigned_payload", self.payload_file,
xunchang376cc7c2019-04-08 23:04:58 -0700596 "--signature_size", str(payload_signer.key_size),
Tao Bao40b18822018-01-30 18:19:04 -0800597 "--metadata_hash_file", metadata_sig_file,
598 "--payload_hash_file", payload_sig_file]
Tao Bao718faed2019-08-02 13:24:19 -0700599 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800600
601 # 2. Sign the hashes.
602 signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
603 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
604
605 # 3. Insert the signatures back into the payload file.
606 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
607 suffix=".bin")
608 cmd = ["brillo_update_payload", "sign",
609 "--unsigned_payload", self.payload_file,
610 "--payload", signed_payload_file,
xunchang376cc7c2019-04-08 23:04:58 -0700611 "--signature_size", str(payload_signer.key_size),
Tao Bao40b18822018-01-30 18:19:04 -0800612 "--metadata_signature_file", signed_metadata_sig_file,
613 "--payload_signature_file", signed_payload_sig_file]
Tao Bao718faed2019-08-02 13:24:19 -0700614 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800615
616 # 4. Dump the signed payload properties.
617 properties_file = common.MakeTempFile(prefix="payload-properties-",
618 suffix=".txt")
619 cmd = ["brillo_update_payload", "properties",
620 "--payload", signed_payload_file,
621 "--properties_file", properties_file]
Tao Bao718faed2019-08-02 13:24:19 -0700622 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800623
Tao Bao667ff572018-02-10 00:02:40 -0800624 if self.secondary:
625 with open(properties_file, "a") as f:
626 f.write("SWITCH_SLOT_ON_REBOOT=0\n")
627
Tao Bao40b18822018-01-30 18:19:04 -0800628 if OPTIONS.wipe_user_data:
629 with open(properties_file, "a") as f:
630 f.write("POWERWASH=1\n")
631
632 self.payload_file = signed_payload_file
633 self.payload_properties = properties_file
634
Tao Bao667ff572018-02-10 00:02:40 -0800635 def WriteToZip(self, output_zip):
Tao Bao40b18822018-01-30 18:19:04 -0800636 """Writes the payload to the given zip.
637
638 Args:
639 output_zip: The output ZipFile instance.
640 """
641 assert self.payload_file is not None
642 assert self.payload_properties is not None
643
Tao Bao667ff572018-02-10 00:02:40 -0800644 if self.secondary:
Tao Baof7140c02018-01-30 17:09:24 -0800645 payload_arcname = Payload.SECONDARY_PAYLOAD_BIN
646 payload_properties_arcname = Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT
647 else:
648 payload_arcname = Payload.PAYLOAD_BIN
649 payload_properties_arcname = Payload.PAYLOAD_PROPERTIES_TXT
650
Tao Bao40b18822018-01-30 18:19:04 -0800651 # Add the signed payload file and properties into the zip. In order to
652 # support streaming, we pack them as ZIP_STORED. So these entries can be
653 # read directly with the offset and length pairs.
Tao Baof7140c02018-01-30 17:09:24 -0800654 common.ZipWrite(output_zip, self.payload_file, arcname=payload_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800655 compress_type=zipfile.ZIP_STORED)
656 common.ZipWrite(output_zip, self.payload_properties,
Tao Baof7140c02018-01-30 17:09:24 -0800657 arcname=payload_properties_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800658 compress_type=zipfile.ZIP_STORED)
659
660
Doug Zongkereef39442009-04-02 12:14:19 -0700661def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200662 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700663
Doug Zongker951495f2009-08-14 12:44:19 -0700664 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
665 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700666
667
Tao Bao481bab82017-12-21 11:23:09 -0800668def _LoadOemDicts(oem_source):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800669 """Returns the list of loaded OEM properties dict."""
Tao Bao481bab82017-12-21 11:23:09 -0800670 if not oem_source:
671 return None
672
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800673 oem_dicts = []
Tao Bao481bab82017-12-21 11:23:09 -0800674 for oem_file in oem_source:
675 with open(oem_file) as fp:
676 oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines()))
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800677 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700678
Doug Zongkereef39442009-04-02 12:14:19 -0700679
Tao Baod42e97e2016-11-30 12:11:57 -0800680def _WriteRecoveryImageToBoot(script, output_zip):
681 """Find and write recovery image to /boot in two-step OTA.
682
683 In two-step OTAs, we write recovery image to /boot as the first step so that
684 we can reboot to there and install a new recovery image to /recovery.
685 A special "recovery-two-step.img" will be preferred, which encodes the correct
686 path of "/boot". Otherwise the device may show "device is corrupt" message
687 when booting into /boot.
688
689 Fall back to using the regular recovery.img if the two-step recovery image
690 doesn't exist. Note that rebuilding the special image at this point may be
691 infeasible, because we don't have the desired boot signer and keys when
692 calling ota_from_target_files.py.
693 """
694
695 recovery_two_step_img_name = "recovery-two-step.img"
696 recovery_two_step_img_path = os.path.join(
Tao Bao04808502019-07-25 23:11:41 -0700697 OPTIONS.input_tmp, "OTA", recovery_two_step_img_name)
Tao Baod42e97e2016-11-30 12:11:57 -0800698 if os.path.exists(recovery_two_step_img_path):
Tao Bao04808502019-07-25 23:11:41 -0700699 common.ZipWrite(
700 output_zip,
701 recovery_two_step_img_path,
702 arcname=recovery_two_step_img_name)
Tao Bao32fcdab2018-10-12 10:30:39 -0700703 logger.info(
704 "two-step package: using %s in stage 1/3", recovery_two_step_img_name)
Tao Baod42e97e2016-11-30 12:11:57 -0800705 script.WriteRawImage("/boot", recovery_two_step_img_name)
706 else:
Tao Bao32fcdab2018-10-12 10:30:39 -0700707 logger.info("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800708 # The "recovery.img" entry has been written into package earlier.
709 script.WriteRawImage("/boot", "recovery.img")
710
711
Doug Zongkerc9253822014-02-04 12:17:58 -0800712def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700713 namelist = [name for name in target_files_zip.namelist()]
714 return ("SYSTEM/recovery-from-boot.p" in namelist or
715 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700716
Tao Bao457cbf62017-03-06 09:56:01 -0800717
Yifan Hong51d37562019-04-23 17:06:46 -0700718def HasPartition(target_files_zip, partition):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700719 try:
Yifan Hong51d37562019-04-23 17:06:46 -0700720 target_files_zip.getinfo(partition.upper() + "/")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700721 return True
722 except KeyError:
723 return False
724
Tao Bao457cbf62017-03-06 09:56:01 -0800725
Yifan Hong9276cf02019-08-21 16:37:04 -0700726def HasTrebleEnabled(target_files, target_info):
727 def HasVendorPartition(target_files):
728 if os.path.isdir(target_files):
729 return os.path.isdir(os.path.join(target_files, "VENDOR"))
730 if zipfile.is_zipfile(target_files):
731 return HasPartition(zipfile.ZipFile(target_files), "vendor")
732 raise ValueError("Unknown target_files argument")
Yifan Hong51d37562019-04-23 17:06:46 -0700733
Yifan Hong9276cf02019-08-21 16:37:04 -0700734 return (HasVendorPartition(target_files) and
Tao Bao481bab82017-12-21 11:23:09 -0800735 target_info.GetBuildProp("ro.treble.enabled") == "true")
Tao Baobcd1d162017-08-26 13:10:26 -0700736
737
Tao Bao481bab82017-12-21 11:23:09 -0800738def WriteFingerprintAssertion(script, target_info, source_info):
739 source_oem_props = source_info.oem_props
740 target_oem_props = target_info.oem_props
Michael Runge6e836112014-04-15 17:40:21 -0700741
Tao Bao481bab82017-12-21 11:23:09 -0800742 if source_oem_props is None and target_oem_props is None:
743 script.AssertSomeFingerprint(
744 source_info.fingerprint, target_info.fingerprint)
745 elif source_oem_props is not None and target_oem_props is not None:
746 script.AssertSomeThumbprint(
747 target_info.GetBuildProp("ro.build.thumbprint"),
748 source_info.GetBuildProp("ro.build.thumbprint"))
749 elif source_oem_props is None and target_oem_props is not None:
750 script.AssertFingerprintOrThumbprint(
751 source_info.fingerprint,
752 target_info.GetBuildProp("ro.build.thumbprint"))
753 else:
754 script.AssertFingerprintOrThumbprint(
755 target_info.fingerprint,
756 source_info.GetBuildProp("ro.build.thumbprint"))
Doug Zongker73ef8252009-07-23 15:12:53 -0700757
Doug Zongkerfc44a512014-08-26 13:10:25 -0700758
Yifan Hong9276cf02019-08-21 16:37:04 -0700759def CheckVintfIfTrebleEnabled(target_files, target_info):
760 """Checks compatibility info of the input target files.
Tao Bao21803d32017-04-19 10:16:09 -0700761
Yifan Hong9276cf02019-08-21 16:37:04 -0700762 Metadata used for compatibility verification is retrieved from target_zip.
Tao Bao21803d32017-04-19 10:16:09 -0700763
Yifan Hong9276cf02019-08-21 16:37:04 -0700764 Compatibility should only be checked for devices that have enabled
Tao Baobcd1d162017-08-26 13:10:26 -0700765 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700766
767 Args:
Yifan Hong9276cf02019-08-21 16:37:04 -0700768 target_files: Path to zip file containing the source files to be included
769 for OTA. Can also be the path to extracted directory.
Tao Bao481bab82017-12-21 11:23:09 -0800770 target_info: The BuildInfo instance that holds the target build info.
Tao Bao21803d32017-04-19 10:16:09 -0700771 """
772
Tao Baobcd1d162017-08-26 13:10:26 -0700773 # Will only proceed if the target has enabled the Treble support (as well as
774 # having a /vendor partition).
Yifan Hong9276cf02019-08-21 16:37:04 -0700775 if not HasTrebleEnabled(target_files, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700776 return
777
xunchangabfa2652019-02-19 16:27:10 -0800778 # Skip adding the compatibility package as a workaround for b/114240221. The
779 # compatibility will always fail on devices without qualified kernels.
780 if OPTIONS.skip_compatibility_check:
781 return
782
Yifan Hong9276cf02019-08-21 16:37:04 -0700783 if not check_target_files_vintf.CheckVintf(target_files, target_info):
784 raise RuntimeError("VINTF compatibility check failed")
Tao Bao21803d32017-04-19 10:16:09 -0700785
786
Tianjie Xuf67dd802019-05-20 17:50:36 -0700787def GetBlockDifferences(target_zip, source_zip, target_info, source_info,
788 device_specific):
789 """Returns a ordered dict of block differences with partition name as key."""
790
791 def GetIncrementalBlockDifferenceForPartition(name):
792 if not HasPartition(source_zip, name):
793 raise RuntimeError("can't generate incremental that adds {}".format(name))
794
795 partition_src = common.GetUserImage(name, OPTIONS.source_tmp, source_zip,
796 info_dict=source_info,
797 allow_shared_blocks=allow_shared_blocks)
798
799 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
800 name, 4096, target_info)
801 partition_tgt = common.GetUserImage(name, OPTIONS.target_tmp, target_zip,
802 info_dict=target_info,
803 allow_shared_blocks=allow_shared_blocks,
804 hashtree_info_generator=
805 hashtree_info_generator)
806
807 # Check the first block of the source system partition for remount R/W only
808 # if the filesystem is ext4.
809 partition_source_info = source_info["fstab"]["/" + name]
810 check_first_block = partition_source_info.fs_type == "ext4"
811 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
812 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
813 # b) the blocks listed in block map may not contain all the bytes for a
814 # given file (because they're rounded to be 4K-aligned).
815 partition_target_info = target_info["fstab"]["/" + name]
816 disable_imgdiff = (partition_source_info.fs_type == "squashfs" or
817 partition_target_info.fs_type == "squashfs")
818 return common.BlockDifference(name, partition_src, partition_tgt,
819 check_first_block,
820 version=blockimgdiff_version,
821 disable_imgdiff=disable_imgdiff)
822
823 if source_zip:
824 # See notes in common.GetUserImage()
825 allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or
826 target_info.get('ext4_share_dup_blocks') == "true")
827 blockimgdiff_version = max(
828 int(i) for i in target_info.get(
829 "blockimgdiff_versions", "1").split(","))
830 assert blockimgdiff_version >= 3
831
832 block_diff_dict = collections.OrderedDict()
833 partition_names = ["system", "vendor", "product", "odm", "system_ext"]
834 for partition in partition_names:
835 if not HasPartition(target_zip, partition):
836 continue
837 # Full OTA update.
838 if not source_zip:
839 tgt = common.GetUserImage(partition, OPTIONS.input_tmp, target_zip,
840 info_dict=target_info,
841 reset_file_map=True)
842 block_diff_dict[partition] = common.BlockDifference(partition, tgt,
843 src=None)
844 # Incremental OTA update.
845 else:
846 block_diff_dict[partition] = GetIncrementalBlockDifferenceForPartition(
847 partition)
848 assert "system" in block_diff_dict
849
850 # Get the block diffs from the device specific script. If there is a
851 # duplicate block diff for a partition, ignore the diff in the generic script
852 # and use the one in the device specific script instead.
853 if source_zip:
854 device_specific_diffs = device_specific.IncrementalOTA_GetBlockDifferences()
855 function_name = "IncrementalOTA_GetBlockDifferences"
856 else:
857 device_specific_diffs = device_specific.FullOTA_GetBlockDifferences()
858 function_name = "FullOTA_GetBlockDifferences"
859
860 if device_specific_diffs:
861 assert all(isinstance(diff, common.BlockDifference)
862 for diff in device_specific_diffs), \
863 "{} is not returning a list of BlockDifference objects".format(
864 function_name)
865 for diff in device_specific_diffs:
866 if diff.partition in block_diff_dict:
867 logger.warning("Duplicate block difference found. Device specific block"
868 " diff for partition '%s' overrides the one in generic"
869 " script.", diff.partition)
870 block_diff_dict[diff.partition] = diff
871
872 return block_diff_dict
873
874
Tao Bao491d7e22018-02-21 13:17:22 -0800875def WriteFullOTAPackage(input_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -0800876 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700877
Tao Bao481bab82017-12-21 11:23:09 -0800878 # We don't know what version it will be installed on top of. We expect the API
879 # just won't change very often. Similarly for fstab, it might have changed in
880 # the target build.
881 target_api_version = target_info["recovery_api_version"]
882 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700883
Tao Bao481bab82017-12-21 11:23:09 -0800884 if target_info.oem_props and not OPTIONS.oem_no_mount:
885 target_info.WriteMountOemScript(script)
886
Tao Baodf3a48b2018-01-10 16:30:43 -0800887 metadata = GetPackageMetadata(target_info)
Doug Zongker2ea21062010-04-28 16:05:21 -0700888
Tao Bao491d7e22018-02-21 13:17:22 -0800889 if not OPTIONS.no_signing:
890 staging_file = common.MakeTempFile(suffix='.zip')
891 else:
892 staging_file = output_file
893
894 output_zip = zipfile.ZipFile(
895 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
896
Doug Zongker05d3dea2009-06-22 11:32:31 -0700897 device_specific = common.DeviceSpecificParams(
898 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800899 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700900 output_zip=output_zip,
901 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700902 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700903 metadata=metadata,
904 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700905
Tao Bao457cbf62017-03-06 09:56:01 -0800906 assert HasRecoveryPatch(input_zip)
Doug Zongkerc9253822014-02-04 12:17:58 -0800907
Tao Bao481bab82017-12-21 11:23:09 -0800908 # Assertions (e.g. downgrade check, device properties check).
909 ts = target_info.GetBuildProp("ro.build.date.utc")
910 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700911 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700912
Tao Bao481bab82017-12-21 11:23:09 -0800913 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700914 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800915
Tianjie Xuf67dd802019-05-20 17:50:36 -0700916 block_diff_dict = GetBlockDifferences(target_zip=input_zip, source_zip=None,
917 target_info=target_info,
918 source_info=None,
919 device_specific=device_specific)
920
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800921 # Two-step package strategy (in chronological order, which is *not*
922 # the order in which the generated script has things):
923 #
924 # if stage is not "2/3" or "3/3":
925 # write recovery image to boot partition
926 # set stage to "2/3"
927 # reboot to boot partition and restart recovery
928 # else if stage is "2/3":
929 # write recovery image to recovery partition
930 # set stage to "3/3"
931 # reboot to recovery partition and restart recovery
932 # else:
933 # (stage must be "3/3")
934 # set stage to ""
935 # do normal full package installation:
936 # wipe and install system, boot image, etc.
937 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700938 # complete script normally
939 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800940
941 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
942 OPTIONS.input_tmp, "RECOVERY")
943 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800944 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800945 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800946 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800947 assert fs.fs_type.upper() == "EMMC", \
948 "two-step packages only supported on devices with EMMC /misc partitions"
949 bcb_dev = {"bcb_dev": fs.device}
950 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
951 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700952if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800953""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800954
955 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
956 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800957 script.WriteRawImage("/recovery", "recovery.img")
958 script.AppendExtra("""
959set_stage("%(bcb_dev)s", "3/3");
960reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700961else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800962""" % bcb_dev)
963
Tao Baod42e97e2016-11-30 12:11:57 -0800964 # Stage 3/3: Make changes.
965 script.Comment("Stage 3/3")
966
Tao Bao6c55a8a2015-04-08 15:30:27 -0700967 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800968 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700969
Doug Zongkere5ff5902012-01-17 10:55:37 -0800970 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700971
Tianjie Xuf67dd802019-05-20 17:50:36 -0700972 # All other partitions as well as the data wipe use 10% of the progress, and
973 # the update of the system partition takes the remaining progress.
974 system_progress = 0.9 - (len(block_diff_dict) - 1) * 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700975 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800976 system_progress -= 0.1
Tianjie Xuf67dd802019-05-20 17:50:36 -0700977 progress_dict = {partition: 0.1 for partition in block_diff_dict}
978 progress_dict["system"] = system_progress
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700979
Yifan Hong10c530d2018-12-27 17:34:18 -0800980 if target_info.get('use_dynamic_partitions') == "true":
981 # Use empty source_info_dict to indicate that all partitions / groups must
982 # be re-added.
983 dynamic_partitions_diff = common.DynamicPartitionsDifference(
984 info_dict=OPTIONS.info_dict,
Tianjie Xuf67dd802019-05-20 17:50:36 -0700985 block_diffs=block_diff_dict.values(),
Yifan Hong10c530d2018-12-27 17:34:18 -0800986 progress_dict=progress_dict)
987 dynamic_partitions_diff.WriteScript(script, output_zip,
988 write_verify_script=OPTIONS.verify)
989 else:
Tianjie Xuf67dd802019-05-20 17:50:36 -0700990 for block_diff in block_diff_dict.values():
Yifan Hong10c530d2018-12-27 17:34:18 -0800991 block_diff.WriteScript(script, output_zip,
992 progress=progress_dict.get(block_diff.partition),
993 write_verify_script=OPTIONS.verify)
Doug Zongker73ef8252009-07-23 15:12:53 -0700994
Yifan Hong9276cf02019-08-21 16:37:04 -0700995 CheckVintfIfTrebleEnabled(OPTIONS.input_tmp, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700996
Yifan Hong10c530d2018-12-27 17:34:18 -0800997 boot_img = common.GetBootableImage(
998 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Tao Bao481bab82017-12-21 11:23:09 -0800999 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -07001000 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001001
Doug Zongker9ce0fb62010-09-20 18:04:41 -07001002 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -07001003
Tianjie Xuf67dd802019-05-20 17:50:36 -07001004 script.ShowProgress(0.1, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001005 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -07001006
Doug Zongker1c390a22009-05-14 19:06:36 -07001007 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001008 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001009
Doug Zongker14833602010-02-02 13:12:04 -08001010 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001011
Doug Zongker922206e2014-03-04 13:16:24 -08001012 if OPTIONS.wipe_user_data:
1013 script.ShowProgress(0.1, 10)
1014 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001015
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001016 if OPTIONS.two_step:
1017 script.AppendExtra("""
1018set_stage("%(bcb_dev)s", "");
1019""" % bcb_dev)
1020 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -08001021
1022 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
1023 script.Comment("Stage 1/3")
1024 _WriteRecoveryImageToBoot(script, output_zip)
1025
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001026 script.AppendExtra("""
1027set_stage("%(bcb_dev)s", "2/3");
1028reboot_now("%(bcb_dev)s", "");
1029endif;
1030endif;
1031""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -08001032
Tao Bao5d182562016-02-23 11:38:39 -08001033 script.SetProgress(1)
1034 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001035 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001036
1037 # We haven't written the metadata entry, which will be done in
1038 # FinalizeMetadata.
1039 common.ZipClose(output_zip)
1040
1041 needed_property_files = (
1042 NonAbOtaPropertyFiles(),
1043 )
1044 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Doug Zongker2ea21062010-04-28 16:05:21 -07001045
Doug Zongkerfc44a512014-08-26 13:10:25 -07001046
xunchang1cfe2512019-02-19 14:14:48 -08001047def WriteMetadata(metadata, output):
1048 """Writes the metadata to the zip archive or a file.
1049
1050 Args:
1051 metadata: The metadata dict for the package.
1052 output: A ZipFile object or a string of the output file path.
1053 """
1054
Tao Bao59cf0c52019-06-25 10:04:24 -07001055 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.items())])
xunchang1cfe2512019-02-19 14:14:48 -08001056 if isinstance(output, zipfile.ZipFile):
1057 common.ZipWriteStr(output, METADATA_NAME, value,
1058 compress_type=zipfile.ZIP_STORED)
1059 return
1060
1061 with open(output, 'w') as f:
1062 f.write(value)
Doug Zongkereef39442009-04-02 12:14:19 -07001063
Doug Zongkerfc44a512014-08-26 13:10:25 -07001064
Tao Bao481bab82017-12-21 11:23:09 -08001065def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -08001066 # Only incremental OTAs are allowed to reach here.
1067 assert OPTIONS.incremental_source is not None
1068
Tao Bao481bab82017-12-21 11:23:09 -08001069 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
1070 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Bao59cf0c52019-06-25 10:04:24 -07001071 is_downgrade = int(post_timestamp) < int(pre_timestamp)
Tao Baob31892e2017-02-07 11:21:17 -08001072
1073 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -08001074 if not is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -07001075 raise RuntimeError(
1076 "--downgrade or --override_timestamp specified but no downgrade "
1077 "detected: pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -08001078 metadata["ota-downgrade"] = "yes"
Tao Baob31892e2017-02-07 11:21:17 -08001079 else:
1080 if is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -07001081 raise RuntimeError(
1082 "Downgrade detected based on timestamp check: pre: %s, post: %s. "
1083 "Need to specify --override_timestamp OR --downgrade to allow "
1084 "building the incremental." % (pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -08001085
1086
Tao Baodf3a48b2018-01-10 16:30:43 -08001087def GetPackageMetadata(target_info, source_info=None):
1088 """Generates and returns the metadata dict.
1089
1090 It generates a dict() that contains the info to be written into an OTA
1091 package (META-INF/com/android/metadata). It also handles the detection of
Tao Baofaa8e0b2018-04-12 14:31:43 -07001092 downgrade / data wipe based on the global options.
Tao Baodf3a48b2018-01-10 16:30:43 -08001093
1094 Args:
1095 target_info: The BuildInfo instance that holds the target build info.
1096 source_info: The BuildInfo instance that holds the source build info, or
1097 None if generating full OTA.
1098
1099 Returns:
1100 A dict to be written into package metadata entry.
1101 """
1102 assert isinstance(target_info, BuildInfo)
1103 assert source_info is None or isinstance(source_info, BuildInfo)
1104
1105 metadata = {
1106 'post-build' : target_info.fingerprint,
1107 'post-build-incremental' : target_info.GetBuildProp(
1108 'ro.build.version.incremental'),
Tao Bao35dc2552018-02-01 13:18:00 -08001109 'post-sdk-level' : target_info.GetBuildProp(
1110 'ro.build.version.sdk'),
1111 'post-security-patch-level' : target_info.GetBuildProp(
1112 'ro.build.version.security_patch'),
Tao Baodf3a48b2018-01-10 16:30:43 -08001113 }
1114
1115 if target_info.is_ab:
1116 metadata['ota-type'] = 'AB'
1117 metadata['ota-required-cache'] = '0'
1118 else:
1119 metadata['ota-type'] = 'BLOCK'
1120
1121 if OPTIONS.wipe_user_data:
1122 metadata['ota-wipe'] = 'yes'
1123
Tao Bao393eeb42019-03-06 16:00:38 -08001124 if OPTIONS.retrofit_dynamic_partitions:
1125 metadata['ota-retrofit-dynamic-partitions'] = 'yes'
1126
Tao Baodf3a48b2018-01-10 16:30:43 -08001127 is_incremental = source_info is not None
1128 if is_incremental:
1129 metadata['pre-build'] = source_info.fingerprint
1130 metadata['pre-build-incremental'] = source_info.GetBuildProp(
1131 'ro.build.version.incremental')
1132 metadata['pre-device'] = source_info.device
1133 else:
1134 metadata['pre-device'] = target_info.device
1135
Tao Baofaa8e0b2018-04-12 14:31:43 -07001136 # Use the actual post-timestamp, even for a downgrade case.
1137 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
1138
1139 # Detect downgrades and set up downgrade flags accordingly.
Tao Baodf3a48b2018-01-10 16:30:43 -08001140 if is_incremental:
1141 HandleDowngradeMetadata(metadata, target_info, source_info)
Tao Baodf3a48b2018-01-10 16:30:43 -08001142
1143 return metadata
1144
1145
Tao Baod3fc38a2018-03-08 16:09:01 -08001146class PropertyFiles(object):
1147 """A class that computes the property-files string for an OTA package.
1148
1149 A property-files string is a comma-separated string that contains the
1150 offset/size info for an OTA package. The entries, which must be ZIP_STORED,
1151 can be fetched directly with the package URL along with the offset/size info.
1152 These strings can be used for streaming A/B OTAs, or allowing an updater to
1153 download package metadata entry directly, without paying the cost of
1154 downloading entire package.
Tao Baofe5b69a2018-03-02 09:47:43 -08001155
Tao Baocc8e2662018-03-01 19:30:00 -08001156 Computing the final property-files string requires two passes. Because doing
1157 the whole package signing (with signapk.jar) will possibly reorder the ZIP
1158 entries, which may in turn invalidate earlier computed ZIP entry offset/size
1159 values.
1160
1161 This class provides functions to be called for each pass. The general flow is
1162 as follows.
1163
Tao Baod3fc38a2018-03-08 16:09:01 -08001164 property_files = PropertyFiles()
Tao Baocc8e2662018-03-01 19:30:00 -08001165 # The first pass, which writes placeholders before doing initial signing.
1166 property_files.Compute()
1167 SignOutput()
1168
1169 # The second pass, by replacing the placeholders with actual data.
1170 property_files.Finalize()
1171 SignOutput()
1172
1173 And the caller can additionally verify the final result.
1174
1175 property_files.Verify()
Tao Baofe5b69a2018-03-02 09:47:43 -08001176 """
1177
Tao Baocc8e2662018-03-01 19:30:00 -08001178 def __init__(self):
Tao Baod3fc38a2018-03-08 16:09:01 -08001179 self.name = None
1180 self.required = ()
1181 self.optional = ()
Tao Baofe5b69a2018-03-02 09:47:43 -08001182
Tao Baocc8e2662018-03-01 19:30:00 -08001183 def Compute(self, input_zip):
1184 """Computes and returns a property-files string with placeholders.
Tao Baofe5b69a2018-03-02 09:47:43 -08001185
Tao Baocc8e2662018-03-01 19:30:00 -08001186 We reserve extra space for the offset and size of the metadata entry itself,
1187 although we don't know the final values until the package gets signed.
Tao Baofe5b69a2018-03-02 09:47:43 -08001188
Tao Baocc8e2662018-03-01 19:30:00 -08001189 Args:
1190 input_zip: The input ZIP file.
Tao Baofe5b69a2018-03-02 09:47:43 -08001191
Tao Baocc8e2662018-03-01 19:30:00 -08001192 Returns:
1193 A string with placeholders for the metadata offset/size info, e.g.
1194 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1195 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001196 return self.GetPropertyFilesString(input_zip, reserve_space=True)
Tao Baofe5b69a2018-03-02 09:47:43 -08001197
Tao Baod2ce2ed2018-03-16 12:59:42 -07001198 class InsufficientSpaceException(Exception):
1199 pass
1200
Tao Baocc8e2662018-03-01 19:30:00 -08001201 def Finalize(self, input_zip, reserved_length):
1202 """Finalizes a property-files string with actual METADATA offset/size info.
1203
1204 The input ZIP file has been signed, with the ZIP entries in the desired
1205 place (signapk.jar will possibly reorder the ZIP entries). Now we compute
1206 the ZIP entry offsets and construct the property-files string with actual
1207 data. Note that during this process, we must pad the property-files string
1208 to the reserved length, so that the METADATA entry size remains the same.
1209 Otherwise the entries' offsets and sizes may change again.
1210
1211 Args:
1212 input_zip: The input ZIP file.
1213 reserved_length: The reserved length of the property-files string during
1214 the call to Compute(). The final string must be no more than this
1215 size.
1216
1217 Returns:
1218 A property-files string including the metadata offset/size info, e.g.
1219 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379 ".
1220
1221 Raises:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001222 InsufficientSpaceException: If the reserved length is insufficient to hold
1223 the final string.
Tao Baocc8e2662018-03-01 19:30:00 -08001224 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001225 result = self.GetPropertyFilesString(input_zip, reserve_space=False)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001226 if len(result) > reserved_length:
1227 raise self.InsufficientSpaceException(
1228 'Insufficient reserved space: reserved={}, actual={}'.format(
1229 reserved_length, len(result)))
1230
Tao Baocc8e2662018-03-01 19:30:00 -08001231 result += ' ' * (reserved_length - len(result))
1232 return result
1233
1234 def Verify(self, input_zip, expected):
1235 """Verifies the input ZIP file contains the expected property-files string.
1236
1237 Args:
1238 input_zip: The input ZIP file.
1239 expected: The property-files string that's computed from Finalize().
1240
1241 Raises:
1242 AssertionError: On finding a mismatch.
1243 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001244 actual = self.GetPropertyFilesString(input_zip)
Tao Baocc8e2662018-03-01 19:30:00 -08001245 assert actual == expected, \
1246 "Mismatching streaming metadata: {} vs {}.".format(actual, expected)
1247
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001248 def GetPropertyFilesString(self, zip_file, reserve_space=False):
1249 """
1250 Constructs the property-files string per request.
1251
1252 Args:
1253 zip_file: The input ZIP file.
1254 reserved_length: The reserved length of the property-files string.
1255
1256 Returns:
1257 A property-files string including the metadata offset/size info, e.g.
1258 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1259 """
Tao Baocc8e2662018-03-01 19:30:00 -08001260
1261 def ComputeEntryOffsetSize(name):
1262 """Computes the zip entry offset and size."""
1263 info = zip_file.getinfo(name)
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001264 offset = info.header_offset
1265 offset += zipfile.sizeFileHeader
1266 offset += len(info.extra) + len(info.filename)
Tao Baocc8e2662018-03-01 19:30:00 -08001267 size = info.file_size
1268 return '%s:%d:%d' % (os.path.basename(name), offset, size)
1269
1270 tokens = []
Tao Bao85f16982018-03-08 16:28:33 -08001271 tokens.extend(self._GetPrecomputed(zip_file))
Tao Baocc8e2662018-03-01 19:30:00 -08001272 for entry in self.required:
1273 tokens.append(ComputeEntryOffsetSize(entry))
1274 for entry in self.optional:
1275 if entry in zip_file.namelist():
1276 tokens.append(ComputeEntryOffsetSize(entry))
1277
1278 # 'META-INF/com/android/metadata' is required. We don't know its actual
1279 # offset and length (as well as the values for other entries). So we reserve
Tao Baod2ce2ed2018-03-16 12:59:42 -07001280 # 15-byte as a placeholder ('offset:length'), which is sufficient to cover
1281 # the space for metadata entry. Because 'offset' allows a max of 10-digit
1282 # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the
1283 # reserved space serves the metadata entry only.
Tao Baocc8e2662018-03-01 19:30:00 -08001284 if reserve_space:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001285 tokens.append('metadata:' + ' ' * 15)
Tao Baocc8e2662018-03-01 19:30:00 -08001286 else:
1287 tokens.append(ComputeEntryOffsetSize(METADATA_NAME))
1288
1289 return ','.join(tokens)
Tao Baofe5b69a2018-03-02 09:47:43 -08001290
Tao Bao85f16982018-03-08 16:28:33 -08001291 def _GetPrecomputed(self, input_zip):
1292 """Computes the additional tokens to be included into the property-files.
1293
1294 This applies to tokens without actual ZIP entries, such as
1295 payload_metadadata.bin. We want to expose the offset/size to updaters, so
1296 that they can download the payload metadata directly with the info.
1297
1298 Args:
1299 input_zip: The input zip file.
1300
1301 Returns:
1302 A list of strings (tokens) to be added to the property-files string.
1303 """
1304 # pylint: disable=no-self-use
1305 # pylint: disable=unused-argument
1306 return []
1307
Tao Baofe5b69a2018-03-02 09:47:43 -08001308
Tao Baod3fc38a2018-03-08 16:09:01 -08001309class StreamingPropertyFiles(PropertyFiles):
1310 """A subclass for computing the property-files for streaming A/B OTAs."""
1311
1312 def __init__(self):
1313 super(StreamingPropertyFiles, self).__init__()
1314 self.name = 'ota-streaming-property-files'
1315 self.required = (
1316 # payload.bin and payload_properties.txt must exist.
1317 'payload.bin',
1318 'payload_properties.txt',
1319 )
1320 self.optional = (
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001321 # care_map is available only if dm-verity is enabled.
1322 'care_map.pb',
Tao Baod3fc38a2018-03-08 16:09:01 -08001323 'care_map.txt',
1324 # compatibility.zip is available only if target supports Treble.
1325 'compatibility.zip',
1326 )
1327
1328
Tao Bao85f16982018-03-08 16:28:33 -08001329class AbOtaPropertyFiles(StreamingPropertyFiles):
1330 """The property-files for A/B OTA that includes payload_metadata.bin info.
1331
1332 Since P, we expose one more token (aka property-file), in addition to the ones
1333 for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'.
1334 'payload_metadata.bin' is the header part of a payload ('payload.bin'), which
1335 doesn't exist as a separate ZIP entry, but can be used to verify if the
1336 payload can be applied on the given device.
1337
1338 For backward compatibility, we keep both of the 'ota-streaming-property-files'
1339 and the newly added 'ota-property-files' in P. The new token will only be
1340 available in 'ota-property-files'.
1341 """
1342
1343 def __init__(self):
1344 super(AbOtaPropertyFiles, self).__init__()
1345 self.name = 'ota-property-files'
1346
1347 def _GetPrecomputed(self, input_zip):
1348 offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip)
1349 return ['payload_metadata.bin:{}:{}'.format(offset, size)]
1350
1351 @staticmethod
1352 def _GetPayloadMetadataOffsetAndSize(input_zip):
1353 """Computes the offset and size of the payload metadata for a given package.
1354
1355 (From system/update_engine/update_metadata.proto)
1356 A delta update file contains all the deltas needed to update a system from
1357 one specific version to another specific version. The update format is
1358 represented by this struct pseudocode:
1359
1360 struct delta_update_file {
1361 char magic[4] = "CrAU";
1362 uint64 file_format_version;
1363 uint64 manifest_size; // Size of protobuf DeltaArchiveManifest
1364
1365 // Only present if format_version > 1:
1366 uint32 metadata_signature_size;
1367
1368 // The Bzip2 compressed DeltaArchiveManifest
1369 char manifest[metadata_signature_size];
1370
1371 // The signature of the metadata (from the beginning of the payload up to
1372 // this location, not including the signature itself). This is a
1373 // serialized Signatures message.
1374 char medatada_signature_message[metadata_signature_size];
1375
1376 // Data blobs for files, no specific format. The specific offset
1377 // and length of each data blob is recorded in the DeltaArchiveManifest.
1378 struct {
1379 char data[];
1380 } blobs[];
1381
1382 // These two are not signed:
1383 uint64 payload_signatures_message_size;
1384 char payload_signatures_message[];
1385 };
1386
1387 'payload-metadata.bin' contains all the bytes from the beginning of the
1388 payload, till the end of 'medatada_signature_message'.
1389 """
1390 payload_info = input_zip.getinfo('payload.bin')
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001391 payload_offset = payload_info.header_offset
1392 payload_offset += zipfile.sizeFileHeader
1393 payload_offset += len(payload_info.extra) + len(payload_info.filename)
Tao Bao85f16982018-03-08 16:28:33 -08001394 payload_size = payload_info.file_size
1395
Tao Bao59cf0c52019-06-25 10:04:24 -07001396 with input_zip.open('payload.bin') as payload_fp:
Tao Bao85f16982018-03-08 16:28:33 -08001397 header_bin = payload_fp.read(24)
1398
1399 # network byte order (big-endian)
1400 header = struct.unpack("!IQQL", header_bin)
1401
1402 # 'CrAU'
1403 magic = header[0]
1404 assert magic == 0x43724155, "Invalid magic: {:x}".format(magic)
1405
1406 manifest_size = header[2]
1407 metadata_signature_size = header[3]
1408 metadata_total = 24 + manifest_size + metadata_signature_size
1409 assert metadata_total < payload_size
1410
1411 return (payload_offset, metadata_total)
1412
1413
Tao Bao491d7e22018-02-21 13:17:22 -08001414class NonAbOtaPropertyFiles(PropertyFiles):
1415 """The property-files for non-A/B OTA.
1416
1417 For non-A/B OTA, the property-files string contains the info for METADATA
1418 entry, with which a system updater can be fetched the package metadata prior
1419 to downloading the entire package.
1420 """
1421
1422 def __init__(self):
1423 super(NonAbOtaPropertyFiles, self).__init__()
1424 self.name = 'ota-property-files'
1425
1426
Tao Baod3fc38a2018-03-08 16:09:01 -08001427def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
Tao Baofe5b69a2018-03-02 09:47:43 -08001428 """Finalizes the metadata and signs an A/B OTA package.
1429
1430 In order to stream an A/B OTA package, we need 'ota-streaming-property-files'
1431 that contains the offsets and sizes for the ZIP entries. An example
1432 property-files string is as follows.
1433
1434 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379"
1435
1436 OTA server can pass down this string, in addition to the package URL, to the
1437 system update client. System update client can then fetch individual ZIP
1438 entries (ZIP_STORED) directly at the given offset of the URL.
1439
1440 Args:
1441 metadata: The metadata dict for the package.
1442 input_file: The input ZIP filename that doesn't contain the package METADATA
1443 entry yet.
1444 output_file: The final output ZIP filename.
Tao Baod3fc38a2018-03-08 16:09:01 -08001445 needed_property_files: The list of PropertyFiles' to be generated.
Tao Baofe5b69a2018-03-02 09:47:43 -08001446 """
Tao Baofe5b69a2018-03-02 09:47:43 -08001447
Tao Baod2ce2ed2018-03-16 12:59:42 -07001448 def ComputeAllPropertyFiles(input_file, needed_property_files):
1449 # Write the current metadata entry with placeholders.
1450 with zipfile.ZipFile(input_file) as input_zip:
1451 for property_files in needed_property_files:
1452 metadata[property_files.name] = property_files.Compute(input_zip)
1453 namelist = input_zip.namelist()
Tao Baofe5b69a2018-03-02 09:47:43 -08001454
Tao Baod2ce2ed2018-03-16 12:59:42 -07001455 if METADATA_NAME in namelist:
1456 common.ZipDelete(input_file, METADATA_NAME)
1457 output_zip = zipfile.ZipFile(input_file, 'a')
1458 WriteMetadata(metadata, output_zip)
1459 common.ZipClose(output_zip)
1460
1461 if OPTIONS.no_signing:
1462 return input_file
1463
Tao Bao491d7e22018-02-21 13:17:22 -08001464 prelim_signing = common.MakeTempFile(suffix='.zip')
1465 SignOutput(input_file, prelim_signing)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001466 return prelim_signing
Tao Baofe5b69a2018-03-02 09:47:43 -08001467
Tao Baod2ce2ed2018-03-16 12:59:42 -07001468 def FinalizeAllPropertyFiles(prelim_signing, needed_property_files):
1469 with zipfile.ZipFile(prelim_signing) as prelim_signing_zip:
1470 for property_files in needed_property_files:
1471 metadata[property_files.name] = property_files.Finalize(
1472 prelim_signing_zip, len(metadata[property_files.name]))
1473
1474 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP
1475 # entries, as well as padding the entry headers. We do a preliminary signing
1476 # (with an incomplete metadata entry) to allow that to happen. Then compute
1477 # the ZIP entry offsets, write back the final metadata and do the final
1478 # signing.
1479 prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files)
1480 try:
1481 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
1482 except PropertyFiles.InsufficientSpaceException:
1483 # Even with the preliminary signing, the entry orders may change
1484 # dramatically, which leads to insufficiently reserved space during the
1485 # first call to ComputeAllPropertyFiles(). In that case, we redo all the
1486 # preliminary signing works, based on the already ordered ZIP entries, to
1487 # address the issue.
1488 prelim_signing = ComputeAllPropertyFiles(
1489 prelim_signing, needed_property_files)
1490 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
Tao Baofe5b69a2018-03-02 09:47:43 -08001491
1492 # Replace the METADATA entry.
1493 common.ZipDelete(prelim_signing, METADATA_NAME)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001494 output_zip = zipfile.ZipFile(prelim_signing, 'a')
Tao Baofe5b69a2018-03-02 09:47:43 -08001495 WriteMetadata(metadata, output_zip)
1496 common.ZipClose(output_zip)
1497
1498 # Re-sign the package after updating the metadata entry.
Tao Bao491d7e22018-02-21 13:17:22 -08001499 if OPTIONS.no_signing:
1500 output_file = prelim_signing
1501 else:
1502 SignOutput(prelim_signing, output_file)
Tao Baofe5b69a2018-03-02 09:47:43 -08001503
1504 # Reopen the final signed zip to double check the streaming metadata.
Tao Baod2ce2ed2018-03-16 12:59:42 -07001505 with zipfile.ZipFile(output_file) as output_zip:
Tao Baod3fc38a2018-03-08 16:09:01 -08001506 for property_files in needed_property_files:
1507 property_files.Verify(output_zip, metadata[property_files.name].strip())
Tao Baofe5b69a2018-03-02 09:47:43 -08001508
xunchang1cfe2512019-02-19 14:14:48 -08001509 # If requested, dump the metadata to a separate file.
1510 output_metadata_path = OPTIONS.output_metadata_path
1511 if output_metadata_path:
1512 WriteMetadata(metadata, output_metadata_path)
1513
Tao Baofe5b69a2018-03-02 09:47:43 -08001514
Tao Bao491d7e22018-02-21 13:17:22 -08001515def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -08001516 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1517 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -08001518
Tao Bao481bab82017-12-21 11:23:09 -08001519 target_api_version = target_info["recovery_api_version"]
1520 source_api_version = source_info["recovery_api_version"]
1521 if source_api_version == 0:
Tao Bao32fcdab2018-10-12 10:30:39 -07001522 logger.warning(
1523 "Generating edify script for a source that can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001524
Tao Bao481bab82017-12-21 11:23:09 -08001525 script = edify_generator.EdifyGenerator(
1526 source_api_version, target_info, fstab=source_info["fstab"])
1527
1528 if target_info.oem_props or source_info.oem_props:
1529 if not OPTIONS.oem_no_mount:
1530 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -07001531
Tao Baodf3a48b2018-01-10 16:30:43 -08001532 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -08001533
Tao Bao491d7e22018-02-21 13:17:22 -08001534 if not OPTIONS.no_signing:
1535 staging_file = common.MakeTempFile(suffix='.zip')
1536 else:
1537 staging_file = output_file
1538
1539 output_zip = zipfile.ZipFile(
1540 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
1541
Geremy Condra36bd3652014-02-06 19:45:10 -08001542 device_specific = common.DeviceSpecificParams(
1543 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001544 source_version=source_api_version,
Yifan Hong8a66a712019-04-04 15:37:57 -07001545 source_tmp=OPTIONS.source_tmp,
Geremy Condra36bd3652014-02-06 19:45:10 -08001546 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001547 target_version=target_api_version,
Yifan Hong8a66a712019-04-04 15:37:57 -07001548 target_tmp=OPTIONS.target_tmp,
Geremy Condra36bd3652014-02-06 19:45:10 -08001549 output_zip=output_zip,
1550 script=script,
1551 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -08001552 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001553
Geremy Condra36bd3652014-02-06 19:45:10 -08001554 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001555 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001556 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001557 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001558 updating_boot = (not OPTIONS.two_step and
1559 (source_boot.data != target_boot.data))
1560
Geremy Condra36bd3652014-02-06 19:45:10 -08001561 target_recovery = common.GetBootableImage(
1562 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -08001563
Tianjie Xuf67dd802019-05-20 17:50:36 -07001564 block_diff_dict = GetBlockDifferences(target_zip=target_zip,
1565 source_zip=source_zip,
1566 target_info=target_info,
1567 source_info=source_info,
1568 device_specific=device_specific)
Geremy Condra36bd3652014-02-06 19:45:10 -08001569
Yifan Hong9276cf02019-08-21 16:37:04 -07001570 CheckVintfIfTrebleEnabled(OPTIONS.target_tmp, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001571
Tao Bao481bab82017-12-21 11:23:09 -08001572 # Assertions (e.g. device properties check).
1573 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -08001574 device_specific.IncrementalOTA_Assertions()
1575
1576 # Two-step incremental package strategy (in chronological order,
1577 # which is *not* the order in which the generated script has
1578 # things):
1579 #
1580 # if stage is not "2/3" or "3/3":
1581 # do verification on current system
1582 # write recovery image to boot partition
1583 # set stage to "2/3"
1584 # reboot to boot partition and restart recovery
1585 # else if stage is "2/3":
1586 # write recovery image to recovery partition
1587 # set stage to "3/3"
1588 # reboot to recovery partition and restart recovery
1589 # else:
1590 # (stage must be "3/3")
1591 # perform update:
1592 # patch system files, etc.
1593 # force full install of new boot image
1594 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001595 # complete script normally
1596 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001597
1598 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001599 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001600 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001601 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001602 assert fs.fs_type.upper() == "EMMC", \
1603 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001604 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001605 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1606 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001607if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001608""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001609
1610 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1611 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001612 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001613 script.WriteRawImage("/recovery", "recovery.img")
1614 script.AppendExtra("""
1615set_stage("%(bcb_dev)s", "3/3");
1616reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001617else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001618""" % bcb_dev)
1619
Tao Baod42e97e2016-11-30 12:11:57 -08001620 # Stage 1/3: (a) Verify the current system.
1621 script.Comment("Stage 1/3")
1622
Tao Bao6c55a8a2015-04-08 15:30:27 -07001623 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001624 script.Print("Source: {}".format(source_info.fingerprint))
1625 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001626
Geremy Condra36bd3652014-02-06 19:45:10 -08001627 script.Print("Verifying current system...")
1628
1629 device_specific.IncrementalOTA_VerifyBegin()
1630
Tao Bao481bab82017-12-21 11:23:09 -08001631 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001632
Tao Baod8d14be2016-02-04 14:26:02 -08001633 # Check the required cache size (i.e. stashed blocks).
Tianjie Xuf67dd802019-05-20 17:50:36 -07001634 required_cache_sizes = [diff.required_cache for diff in
1635 block_diff_dict.values()]
Geremy Condra36bd3652014-02-06 19:45:10 -08001636 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -08001637 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001638 d = common.Difference(target_boot, source_boot)
1639 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001640 if d is None:
1641 include_full_boot = True
1642 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1643 else:
1644 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001645
Tao Bao32fcdab2018-10-12 10:30:39 -07001646 logger.info(
1647 "boot target: %d source: %d diff: %d", target_boot.size,
1648 source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -08001649
Tao Bao51216552018-08-26 11:53:15 -07001650 common.ZipWriteStr(output_zip, "boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001651
Tao Bao51216552018-08-26 11:53:15 -07001652 script.PatchPartitionCheck(
1653 "{}:{}:{}:{}".format(
1654 boot_type, boot_device, target_boot.size, target_boot.sha1),
1655 "{}:{}:{}:{}".format(
1656 boot_type, boot_device, source_boot.size, source_boot.sha1))
1657
Tianjie Xuf67dd802019-05-20 17:50:36 -07001658 required_cache_sizes.append(target_boot.size)
Tao Baod8d14be2016-02-04 14:26:02 -08001659
Tianjie Xuf67dd802019-05-20 17:50:36 -07001660 if required_cache_sizes:
1661 script.CacheFreeSpaceCheck(max(required_cache_sizes))
1662
1663 # Verify the existing partitions.
1664 for diff in block_diff_dict.values():
1665 diff.WriteVerifyScript(script, touched_blocks_only=True)
Geremy Condra36bd3652014-02-06 19:45:10 -08001666
1667 device_specific.IncrementalOTA_VerifyEnd()
1668
1669 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001670 # Stage 1/3: (b) Write recovery image to /boot.
1671 _WriteRecoveryImageToBoot(script, output_zip)
1672
Geremy Condra36bd3652014-02-06 19:45:10 -08001673 script.AppendExtra("""
1674set_stage("%(bcb_dev)s", "2/3");
1675reboot_now("%(bcb_dev)s", "");
1676else
1677""" % bcb_dev)
1678
Tao Baod42e97e2016-11-30 12:11:57 -08001679 # Stage 3/3: Make changes.
1680 script.Comment("Stage 3/3")
1681
Geremy Condra36bd3652014-02-06 19:45:10 -08001682 script.Comment("---- start making changes here ----")
1683
1684 device_specific.IncrementalOTA_InstallBegin()
1685
Tianjie Xuf67dd802019-05-20 17:50:36 -07001686 progress_dict = {partition: 0.1 for partition in block_diff_dict}
1687 progress_dict["system"] = 1 - len(block_diff_dict) * 0.1
Yifan Hong10c530d2018-12-27 17:34:18 -08001688
1689 if OPTIONS.source_info_dict.get("use_dynamic_partitions") == "true":
1690 if OPTIONS.target_info_dict.get("use_dynamic_partitions") != "true":
1691 raise RuntimeError(
1692 "can't generate incremental that disables dynamic partitions")
1693 dynamic_partitions_diff = common.DynamicPartitionsDifference(
1694 info_dict=OPTIONS.target_info_dict,
1695 source_info_dict=OPTIONS.source_info_dict,
Tianjie Xuf67dd802019-05-20 17:50:36 -07001696 block_diffs=block_diff_dict.values(),
Yifan Hong10c530d2018-12-27 17:34:18 -08001697 progress_dict=progress_dict)
1698 dynamic_partitions_diff.WriteScript(
1699 script, output_zip, write_verify_script=OPTIONS.verify)
1700 else:
Tianjie Xuf67dd802019-05-20 17:50:36 -07001701 for block_diff in block_diff_dict.values():
Yifan Hong10c530d2018-12-27 17:34:18 -08001702 block_diff.WriteScript(script, output_zip,
1703 progress=progress_dict.get(block_diff.partition),
1704 write_verify_script=OPTIONS.verify)
Geremy Condra36bd3652014-02-06 19:45:10 -08001705
1706 if OPTIONS.two_step:
1707 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1708 script.WriteRawImage("/boot", "boot.img")
Tao Bao32fcdab2018-10-12 10:30:39 -07001709 logger.info("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001710
1711 if not OPTIONS.two_step:
1712 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001713 if include_full_boot:
Tao Bao32fcdab2018-10-12 10:30:39 -07001714 logger.info("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001715 script.Print("Installing boot image...")
1716 script.WriteRawImage("/boot", "boot.img")
1717 else:
1718 # Produce the boot image by applying a patch to the current
1719 # contents of the boot partition, and write it back to the
1720 # partition.
Tao Bao32fcdab2018-10-12 10:30:39 -07001721 logger.info("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001722 script.Print("Patching boot image...")
1723 script.ShowProgress(0.1, 10)
Tao Bao51216552018-08-26 11:53:15 -07001724 script.PatchPartition(
1725 '{}:{}:{}:{}'.format(
1726 boot_type, boot_device, target_boot.size, target_boot.sha1),
1727 '{}:{}:{}:{}'.format(
1728 boot_type, boot_device, source_boot.size, source_boot.sha1),
1729 'boot.img.p')
Geremy Condra36bd3652014-02-06 19:45:10 -08001730 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001731 logger.info("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001732
1733 # Do device-specific installation (eg, write radio image).
1734 device_specific.IncrementalOTA_InstallEnd()
1735
1736 if OPTIONS.extra_script is not None:
1737 script.AppendExtra(OPTIONS.extra_script)
1738
Doug Zongker922206e2014-03-04 13:16:24 -08001739 if OPTIONS.wipe_user_data:
1740 script.Print("Erasing user data...")
1741 script.FormatPartition("/data")
1742
Geremy Condra36bd3652014-02-06 19:45:10 -08001743 if OPTIONS.two_step:
1744 script.AppendExtra("""
1745set_stage("%(bcb_dev)s", "");
1746endif;
1747endif;
1748""" % bcb_dev)
1749
1750 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001751 # For downgrade OTAs, we prefer to use the update-binary in the source
1752 # build that is actually newer than the one in the target build.
1753 if OPTIONS.downgrade:
1754 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1755 else:
1756 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001757 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001758
1759 # We haven't written the metadata entry yet, which will be handled in
1760 # FinalizeMetadata().
1761 common.ZipClose(output_zip)
1762
1763 # Sign the generated zip package unless no_signing is specified.
1764 needed_property_files = (
1765 NonAbOtaPropertyFiles(),
1766 )
1767 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Geremy Condra36bd3652014-02-06 19:45:10 -08001768
Doug Zongker32b527d2014-03-04 10:03:02 -08001769
Tao Bao15a146a2018-02-21 16:06:59 -08001770def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
Tao Baof7140c02018-01-30 17:09:24 -08001771 """Returns a target-files.zip file for generating secondary payload.
1772
1773 Although the original target-files.zip already contains secondary slot
1774 images (i.e. IMAGES/system_other.img), we need to rename the files to the
1775 ones without _other suffix. Note that we cannot instead modify the names in
1776 META/ab_partitions.txt, because there are no matching partitions on device.
1777
1778 For the partitions that don't have secondary images, the ones for primary
1779 slot will be used. This is to ensure that we always have valid boot, vbmeta,
1780 bootloader images in the inactive slot.
1781
1782 Args:
1783 input_file: The input target-files.zip file.
Tao Bao15a146a2018-02-21 16:06:59 -08001784 skip_postinstall: Whether to skip copying the postinstall config file.
Tao Baof7140c02018-01-30 17:09:24 -08001785
1786 Returns:
1787 The filename of the target-files.zip for generating secondary payload.
1788 """
1789 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1790 target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
1791
Tao Baodba59ee2018-01-09 13:21:02 -08001792 with zipfile.ZipFile(input_file, 'r') as input_zip:
1793 infolist = input_zip.infolist()
Tao Bao12489802018-07-12 14:47:38 -07001794
Tao Bao0ff15de2019-03-20 11:26:06 -07001795 input_tmp = common.UnzipTemp(input_file, UNZIP_PATTERN)
Tao Baodba59ee2018-01-09 13:21:02 -08001796 for info in infolist:
Tao Baof7140c02018-01-30 17:09:24 -08001797 unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
1798 if info.filename == 'IMAGES/system_other.img':
1799 common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
1800
1801 # Primary images and friends need to be skipped explicitly.
1802 elif info.filename in ('IMAGES/system.img',
1803 'IMAGES/system.map'):
1804 pass
1805
Tao Bao15a146a2018-02-21 16:06:59 -08001806 # Skip copying the postinstall config if requested.
1807 elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
1808 pass
1809
Tao Bao12489802018-07-12 14:47:38 -07001810 elif info.filename.startswith(('META/', 'IMAGES/', 'RADIO/')):
Tao Baof7140c02018-01-30 17:09:24 -08001811 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
1812
Tao Baof7140c02018-01-30 17:09:24 -08001813 common.ZipClose(target_zip)
1814
1815 return target_file
1816
1817
Tao Bao15a146a2018-02-21 16:06:59 -08001818def GetTargetFilesZipWithoutPostinstallConfig(input_file):
1819 """Returns a target-files.zip that's not containing postinstall_config.txt.
1820
1821 This allows brillo_update_payload script to skip writing all the postinstall
1822 hooks in the generated payload. The input target-files.zip file will be
1823 duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
1824 contain the postinstall_config.txt entry, the input file will be returned.
1825
1826 Args:
1827 input_file: The input target-files.zip filename.
1828
1829 Returns:
1830 The filename of target-files.zip that doesn't contain postinstall config.
1831 """
1832 # We should only make a copy if postinstall_config entry exists.
1833 with zipfile.ZipFile(input_file, 'r') as input_zip:
1834 if POSTINSTALL_CONFIG not in input_zip.namelist():
1835 return input_file
1836
1837 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1838 shutil.copyfile(input_file, target_file)
1839 common.ZipDelete(target_file, POSTINSTALL_CONFIG)
1840 return target_file
1841
1842
Yifan Hong50e79542018-11-08 17:44:12 -08001843def GetTargetFilesZipForRetrofitDynamicPartitions(input_file,
Yifan Hongb433eba2019-03-06 12:42:53 -08001844 super_block_devices,
1845 dynamic_partition_list):
Yifan Hong50e79542018-11-08 17:44:12 -08001846 """Returns a target-files.zip for retrofitting dynamic partitions.
1847
1848 This allows brillo_update_payload to generate an OTA based on the exact
1849 bits on the block devices. Postinstall is disabled.
1850
1851 Args:
1852 input_file: The input target-files.zip filename.
1853 super_block_devices: The list of super block devices
Yifan Hongb433eba2019-03-06 12:42:53 -08001854 dynamic_partition_list: The list of dynamic partitions
Yifan Hong50e79542018-11-08 17:44:12 -08001855
1856 Returns:
1857 The filename of target-files.zip with *.img replaced with super_*.img for
1858 each block device in super_block_devices.
1859 """
1860 assert super_block_devices, "No super_block_devices are specified."
1861
1862 replace = {'OTA/super_{}.img'.format(dev): 'IMAGES/{}.img'.format(dev)
Tao Bao03fecb62018-11-28 10:59:23 -08001863 for dev in super_block_devices}
Yifan Hong50e79542018-11-08 17:44:12 -08001864
1865 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1866 shutil.copyfile(input_file, target_file)
1867
Tao Baoa3705452019-06-24 15:33:41 -07001868 with zipfile.ZipFile(input_file) as input_zip:
Yifan Hong50e79542018-11-08 17:44:12 -08001869 namelist = input_zip.namelist()
1870
Yifan Hongb433eba2019-03-06 12:42:53 -08001871 input_tmp = common.UnzipTemp(input_file, RETROFIT_DAP_UNZIP_PATTERN)
1872
1873 # Remove partitions from META/ab_partitions.txt that is in
1874 # dynamic_partition_list but not in super_block_devices so that
1875 # brillo_update_payload won't generate update for those logical partitions.
1876 ab_partitions_file = os.path.join(input_tmp, *AB_PARTITIONS.split('/'))
1877 with open(ab_partitions_file) as f:
1878 ab_partitions_lines = f.readlines()
1879 ab_partitions = [line.strip() for line in ab_partitions_lines]
1880 # Assert that all super_block_devices are in ab_partitions
1881 super_device_not_updated = [partition for partition in super_block_devices
1882 if partition not in ab_partitions]
1883 assert not super_device_not_updated, \
1884 "{} is in super_block_devices but not in {}".format(
1885 super_device_not_updated, AB_PARTITIONS)
1886 # ab_partitions -= (dynamic_partition_list - super_block_devices)
1887 new_ab_partitions = common.MakeTempFile(prefix="ab_partitions", suffix=".txt")
1888 with open(new_ab_partitions, 'w') as f:
1889 for partition in ab_partitions:
1890 if (partition in dynamic_partition_list and
1891 partition not in super_block_devices):
Tao Bao59cf0c52019-06-25 10:04:24 -07001892 logger.info("Dropping %s from ab_partitions.txt", partition)
1893 continue
Yifan Hongb433eba2019-03-06 12:42:53 -08001894 f.write(partition + "\n")
1895 to_delete = [AB_PARTITIONS]
1896
Yifan Hong50e79542018-11-08 17:44:12 -08001897 # Always skip postinstall for a retrofit update.
Yifan Hongb433eba2019-03-06 12:42:53 -08001898 to_delete += [POSTINSTALL_CONFIG]
Yifan Hong50e79542018-11-08 17:44:12 -08001899
1900 # Delete dynamic_partitions_info.txt so that brillo_update_payload thinks this
1901 # is a regular update on devices without dynamic partitions support.
1902 to_delete += [DYNAMIC_PARTITION_INFO]
1903
Tao Bao03fecb62018-11-28 10:59:23 -08001904 # Remove the existing partition images as well as the map files.
Tao Bao59cf0c52019-06-25 10:04:24 -07001905 to_delete += list(replace.values())
Tao Bao03fecb62018-11-28 10:59:23 -08001906 to_delete += ['IMAGES/{}.map'.format(dev) for dev in super_block_devices]
Yifan Hong50e79542018-11-08 17:44:12 -08001907
1908 common.ZipDelete(target_file, to_delete)
1909
Yifan Hong50e79542018-11-08 17:44:12 -08001910 target_zip = zipfile.ZipFile(target_file, 'a', allowZip64=True)
1911
1912 # Write super_{foo}.img as {foo}.img.
1913 for src, dst in replace.items():
1914 assert src in namelist, \
Tao Bao59cf0c52019-06-25 10:04:24 -07001915 'Missing {} in {}; {} cannot be written'.format(src, input_file, dst)
Yifan Hong50e79542018-11-08 17:44:12 -08001916 unzipped_file = os.path.join(input_tmp, *src.split('/'))
1917 common.ZipWrite(target_zip, unzipped_file, arcname=dst)
1918
Yifan Hongb433eba2019-03-06 12:42:53 -08001919 # Write new ab_partitions.txt file
1920 common.ZipWrite(target_zip, new_ab_partitions, arcname=AB_PARTITIONS)
1921
Yifan Hong50e79542018-11-08 17:44:12 -08001922 common.ZipClose(target_zip)
1923
1924 return target_file
1925
1926
Tao Baof0c4aa22018-04-30 20:29:30 -07001927def GenerateAbOtaPackage(target_file, output_file, source_file=None):
Tao Baofe5b69a2018-03-02 09:47:43 -08001928 """Generates an Android OTA package that has A/B update payload."""
Tao Baodea0f8b2016-06-20 17:55:06 -07001929 # Stage the output zip package for package signing.
Tao Bao491d7e22018-02-21 13:17:22 -08001930 if not OPTIONS.no_signing:
1931 staging_file = common.MakeTempFile(suffix='.zip')
1932 else:
1933 staging_file = output_file
Tao Baoa652c002018-03-01 19:31:38 -08001934 output_zip = zipfile.ZipFile(staging_file, "w",
Tao Baoc098e9e2016-01-07 13:03:56 -08001935 compression=zipfile.ZIP_DEFLATED)
1936
Tao Bao481bab82017-12-21 11:23:09 -08001937 if source_file is not None:
1938 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1939 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
1940 else:
1941 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
1942 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001943
Tao Bao481bab82017-12-21 11:23:09 -08001944 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001945 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001946
Yifan Hong50e79542018-11-08 17:44:12 -08001947 if OPTIONS.retrofit_dynamic_partitions:
1948 target_file = GetTargetFilesZipForRetrofitDynamicPartitions(
Yifan Hongb433eba2019-03-06 12:42:53 -08001949 target_file, target_info.get("super_block_devices").strip().split(),
1950 target_info.get("dynamic_partition_list").strip().split())
Yifan Hong50e79542018-11-08 17:44:12 -08001951 elif OPTIONS.skip_postinstall:
Tao Bao15a146a2018-02-21 16:06:59 -08001952 target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
1953
Tao Bao40b18822018-01-30 18:19:04 -08001954 # Generate payload.
1955 payload = Payload()
1956
1957 # Enforce a max timestamp this payload can be applied on top of.
Tao Baoff1b86e2017-10-03 14:17:57 -07001958 if OPTIONS.downgrade:
Tao Bao2a12ed72018-01-22 11:35:00 -08001959 max_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baoff1b86e2017-10-03 14:17:57 -07001960 else:
1961 max_timestamp = metadata["post-timestamp"]
Tao Bao40b18822018-01-30 18:19:04 -08001962 additional_args = ["--max_timestamp", max_timestamp]
Tao Baoc098e9e2016-01-07 13:03:56 -08001963
Tao Bao40b18822018-01-30 18:19:04 -08001964 payload.Generate(target_file, source_file, additional_args)
Tao Baoc098e9e2016-01-07 13:03:56 -08001965
Tao Bao40b18822018-01-30 18:19:04 -08001966 # Sign the payload.
Tao Baof7140c02018-01-30 17:09:24 -08001967 payload_signer = PayloadSigner()
1968 payload.Sign(payload_signer)
Tao Baoc098e9e2016-01-07 13:03:56 -08001969
Tao Bao40b18822018-01-30 18:19:04 -08001970 # Write the payload into output zip.
1971 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001972
Tao Baof7140c02018-01-30 17:09:24 -08001973 # Generate and include the secondary payload that installs secondary images
1974 # (e.g. system_other.img).
1975 if OPTIONS.include_secondary:
1976 # We always include a full payload for the secondary slot, even when
1977 # building an incremental OTA. See the comments for "--include_secondary".
Tao Bao15a146a2018-02-21 16:06:59 -08001978 secondary_target_file = GetTargetFilesZipForSecondaryImages(
1979 target_file, OPTIONS.skip_postinstall)
Tao Bao667ff572018-02-10 00:02:40 -08001980 secondary_payload = Payload(secondary=True)
Tao Baodb1fe412018-02-09 23:15:05 -08001981 secondary_payload.Generate(secondary_target_file,
1982 additional_args=additional_args)
Tao Baof7140c02018-01-30 17:09:24 -08001983 secondary_payload.Sign(payload_signer)
Tao Bao667ff572018-02-10 00:02:40 -08001984 secondary_payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08001985
Tianjie Xucfa86222016-03-07 16:31:19 -08001986 # If dm-verity is supported for the device, copy contents of care_map
1987 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001988 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08001989 if (target_info.get("verity") == "true" or
1990 target_info.get("avb_enable") == "true"):
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001991 care_map_list = [x for x in ["care_map.pb", "care_map.txt"] if
1992 "META/" + x in target_zip.namelist()]
1993
1994 # Adds care_map if either the protobuf format or the plain text one exists.
1995 if care_map_list:
1996 care_map_name = care_map_list[0]
1997 care_map_data = target_zip.read("META/" + care_map_name)
1998 # In order to support streaming, care_map needs to be packed as
Tao Bao40b18822018-01-30 18:19:04 -08001999 # ZIP_STORED.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07002000 common.ZipWriteStr(output_zip, care_map_name, care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08002001 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08002002 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002003 logger.warning("Cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07002004
Tao Bao21803d32017-04-19 10:16:09 -07002005 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08002006
Yifan Hong9276cf02019-08-21 16:37:04 -07002007 CheckVintfIfTrebleEnabled(target_file, target_info)
2008
Tao Baofe5b69a2018-03-02 09:47:43 -08002009 # We haven't written the metadata entry yet, which will be handled in
2010 # FinalizeMetadata().
Tao Baoc96316c2017-01-24 22:10:49 -08002011 common.ZipClose(output_zip)
2012
Tao Bao85f16982018-03-08 16:28:33 -08002013 # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers
2014 # all the info of the latter. However, system updaters and OTA servers need to
2015 # take time to switch to the new flag. We keep both of the flags for
2016 # P-timeframe, and will remove StreamingPropertyFiles in later release.
Tao Baod3fc38a2018-03-08 16:09:01 -08002017 needed_property_files = (
Tao Bao85f16982018-03-08 16:28:33 -08002018 AbOtaPropertyFiles(),
Tao Baod3fc38a2018-03-08 16:09:01 -08002019 StreamingPropertyFiles(),
2020 )
2021 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Tao Baoc96316c2017-01-24 22:10:49 -08002022
Tao Baoc098e9e2016-01-07 13:03:56 -08002023
Tao Baof0c4aa22018-04-30 20:29:30 -07002024def GenerateNonAbOtaPackage(target_file, output_file, source_file=None):
2025 """Generates a non-A/B OTA package."""
2026 # Sanity check the loaded info dicts first.
2027 if OPTIONS.info_dict.get("no_recovery") == "true":
2028 raise common.ExternalError(
2029 "--- target build has specified no recovery ---")
2030
2031 # Non-A/B OTAs rely on /cache partition to store temporary files.
2032 cache_size = OPTIONS.info_dict.get("cache_size")
2033 if cache_size is None:
2034 logger.warning("--- can't determine the cache partition size ---")
2035 OPTIONS.cache_size = cache_size
2036
2037 if OPTIONS.extra_script is not None:
2038 with open(OPTIONS.extra_script) as fp:
2039 OPTIONS.extra_script = fp.read()
2040
2041 if OPTIONS.extracted_input is not None:
2042 OPTIONS.input_tmp = OPTIONS.extracted_input
2043 else:
2044 logger.info("unzipping target target-files...")
2045 OPTIONS.input_tmp = common.UnzipTemp(target_file, UNZIP_PATTERN)
2046 OPTIONS.target_tmp = OPTIONS.input_tmp
2047
2048 # If the caller explicitly specified the device-specific extensions path via
2049 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
2050 # is present in the target target_files. Otherwise, take the path of the file
2051 # from 'tool_extensions' in the info dict and look for that in the local
2052 # filesystem, relative to the current directory.
2053 if OPTIONS.device_specific is None:
2054 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2055 if os.path.exists(from_input):
2056 logger.info("(using device-specific extensions from target_files)")
2057 OPTIONS.device_specific = from_input
2058 else:
2059 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
2060
2061 if OPTIONS.device_specific is not None:
2062 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
2063
2064 # Generate a full OTA.
2065 if source_file is None:
2066 with zipfile.ZipFile(target_file) as input_zip:
2067 WriteFullOTAPackage(
2068 input_zip,
2069 output_file)
2070
2071 # Generate an incremental OTA.
2072 else:
2073 logger.info("unzipping source target-files...")
2074 OPTIONS.source_tmp = common.UnzipTemp(
2075 OPTIONS.incremental_source, UNZIP_PATTERN)
2076 with zipfile.ZipFile(target_file) as input_zip, \
2077 zipfile.ZipFile(source_file) as source_zip:
2078 WriteBlockIncrementalOTAPackage(
2079 input_zip,
2080 source_zip,
2081 output_file)
2082
2083
Doug Zongkereef39442009-04-02 12:14:19 -07002084def main(argv):
2085
2086 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07002087 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07002088 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07002089 elif o in ("-i", "--incremental_from"):
2090 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07002091 elif o == "--full_radio":
2092 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07002093 elif o == "--full_bootloader":
2094 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08002095 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002096 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08002097 elif o == "--downgrade":
2098 OPTIONS.downgrade = True
2099 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08002100 elif o == "--override_timestamp":
Tao Baofaa8e0b2018-04-12 14:31:43 -07002101 OPTIONS.downgrade = True
Michael Runge6e836112014-04-15 17:40:21 -07002102 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08002103 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08002104 elif o == "--oem_no_mount":
2105 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07002106 elif o in ("-e", "--extra_script"):
2107 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02002108 elif o in ("-t", "--worker_threads"):
2109 if a.isdigit():
2110 OPTIONS.worker_threads = int(a)
2111 else:
2112 raise ValueError("Cannot parse value %r for option %r - only "
2113 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002114 elif o in ("-2", "--two_step"):
2115 OPTIONS.two_step = True
Tao Baof7140c02018-01-30 17:09:24 -08002116 elif o == "--include_secondary":
2117 OPTIONS.include_secondary = True
Doug Zongker26e66192014-02-20 13:22:07 -08002118 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002119 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07002120 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07002121 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08002122 elif o == "--block":
2123 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002124 elif o in ("-b", "--binary"):
2125 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07002126 elif o == "--stash_threshold":
2127 try:
2128 OPTIONS.stash_threshold = float(a)
2129 except ValueError:
2130 raise ValueError("Cannot parse value %r for option %r - expecting "
2131 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08002132 elif o == "--log_diff":
2133 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002134 elif o == "--payload_signer":
2135 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002136 elif o == "--payload_signer_args":
2137 OPTIONS.payload_signer_args = shlex.split(a)
xunchang376cc7c2019-04-08 23:04:58 -07002138 elif o == "--payload_signer_key_size":
2139 OPTIONS.payload_signer_key_size = a
Dan Willemsencea5cd22017-03-21 14:44:27 -07002140 elif o == "--extracted_input_target_files":
2141 OPTIONS.extracted_input = a
Tao Bao15a146a2018-02-21 16:06:59 -08002142 elif o == "--skip_postinstall":
2143 OPTIONS.skip_postinstall = True
Yifan Hong50e79542018-11-08 17:44:12 -08002144 elif o == "--retrofit_dynamic_partitions":
2145 OPTIONS.retrofit_dynamic_partitions = True
xunchangabfa2652019-02-19 16:27:10 -08002146 elif o == "--skip_compatibility_check":
2147 OPTIONS.skip_compatibility_check = True
xunchang1cfe2512019-02-19 14:14:48 -08002148 elif o == "--output_metadata_path":
2149 OPTIONS.output_metadata_path = a
Doug Zongkereef39442009-04-02 12:14:19 -07002150 else:
2151 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002152 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002153
2154 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08002155 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002156 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07002157 "package_key=",
2158 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002159 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002160 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002161 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08002162 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08002163 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07002164 "extra_script=",
2165 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002166 "two_step",
Tao Baof7140c02018-01-30 17:09:24 -08002167 "include_secondary",
Dan Albert8b72aef2015-03-23 19:13:21 -07002168 "no_signing",
2169 "block",
2170 "binary=",
2171 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08002172 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002173 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07002174 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002175 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002176 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002177 "payload_signer_args=",
xunchang376cc7c2019-04-08 23:04:58 -07002178 "payload_signer_key_size=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07002179 "extracted_input_target_files=",
Tao Bao15a146a2018-02-21 16:06:59 -08002180 "skip_postinstall",
Yifan Hong50e79542018-11-08 17:44:12 -08002181 "retrofit_dynamic_partitions",
xunchangabfa2652019-02-19 16:27:10 -08002182 "skip_compatibility_check",
xunchang1cfe2512019-02-19 14:14:48 -08002183 "output_metadata_path=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002184 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002185
2186 if len(args) != 2:
2187 common.Usage(__doc__)
2188 sys.exit(1)
2189
Tao Bao32fcdab2018-10-12 10:30:39 -07002190 common.InitLogging()
2191
Tao Bao5d182562016-02-23 11:38:39 -08002192 if OPTIONS.downgrade:
Tao Bao5d182562016-02-23 11:38:39 -08002193 # We should only allow downgrading incrementals (as opposed to full).
2194 # Otherwise the device may go back from arbitrary build with this full
2195 # OTA package.
2196 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002197 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08002198
Tao Bao2db13852018-01-08 22:28:57 -08002199 # Load the build info dicts from the zip directly or the extracted input
2200 # directory. We don't need to unzip the entire target-files zips, because they
2201 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
2202 # When loading the info dicts, we don't need to provide the second parameter
2203 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
2204 # some properties with their actual paths, such as 'selinux_fc',
2205 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07002206 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08002207 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07002208 else:
Tao Bao2db13852018-01-08 22:28:57 -08002209 with zipfile.ZipFile(args[0], 'r') as input_zip:
2210 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08002211
Tao Bao32fcdab2018-10-12 10:30:39 -07002212 logger.info("--- target info ---")
2213 common.DumpInfoDict(OPTIONS.info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002214
2215 # Load the source build dict if applicable.
2216 if OPTIONS.incremental_source is not None:
2217 OPTIONS.target_info_dict = OPTIONS.info_dict
2218 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
2219 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2220
Tao Bao32fcdab2018-10-12 10:30:39 -07002221 logger.info("--- source info ---")
2222 common.DumpInfoDict(OPTIONS.source_info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002223
2224 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08002225 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
2226
Yifan Hong50e79542018-11-08 17:44:12 -08002227 # Assume retrofitting dynamic partitions when base build does not set
Yifan Hong50611032018-11-20 14:27:38 -08002228 # use_dynamic_partitions but target build does.
Yifan Hong50e79542018-11-08 17:44:12 -08002229 if (OPTIONS.source_info_dict and
Yifan Hong50611032018-11-20 14:27:38 -08002230 OPTIONS.source_info_dict.get("use_dynamic_partitions") != "true" and
2231 OPTIONS.target_info_dict.get("use_dynamic_partitions") == "true"):
Yifan Hong50e79542018-11-08 17:44:12 -08002232 if OPTIONS.target_info_dict.get("dynamic_partition_retrofit") != "true":
2233 raise common.ExternalError(
2234 "Expect to generate incremental OTA for retrofitting dynamic "
2235 "partitions, but dynamic_partition_retrofit is not set in target "
2236 "build.")
2237 logger.info("Implicitly generating retrofit incremental OTA.")
2238 OPTIONS.retrofit_dynamic_partitions = True
2239
2240 # Skip postinstall for retrofitting dynamic partitions.
2241 if OPTIONS.retrofit_dynamic_partitions:
2242 OPTIONS.skip_postinstall = True
2243
Tao Baoc098e9e2016-01-07 13:03:56 -08002244 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2245
Christian Oderf63e2cd2017-05-01 22:30:15 +02002246 # Use the default key to sign the package if not specified with package_key.
2247 # package_keys are needed on ab_updates, so always define them if an
2248 # ab_update is getting created.
2249 if not OPTIONS.no_signing or ab_update:
2250 if OPTIONS.package_key is None:
2251 OPTIONS.package_key = OPTIONS.info_dict.get(
2252 "default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -07002253 "build/make/target/product/security/testkey")
Christian Oderf63e2cd2017-05-01 22:30:15 +02002254 # Get signing keys
2255 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
2256
Tao Baoc098e9e2016-01-07 13:03:56 -08002257 if ab_update:
Tao Baof0c4aa22018-04-30 20:29:30 -07002258 GenerateAbOtaPackage(
Tao Baoc098e9e2016-01-07 13:03:56 -08002259 target_file=args[0],
2260 output_file=args[1],
2261 source_file=OPTIONS.incremental_source)
2262
Dan Willemsencea5cd22017-03-21 14:44:27 -07002263 else:
Tao Baof0c4aa22018-04-30 20:29:30 -07002264 GenerateNonAbOtaPackage(
2265 target_file=args[0],
2266 output_file=args[1],
2267 source_file=OPTIONS.incremental_source)
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002268
Tao Baof0c4aa22018-04-30 20:29:30 -07002269 # Post OTA generation works.
2270 if OPTIONS.incremental_source is not None and OPTIONS.log_diff:
2271 logger.info("Generating diff logs...")
2272 logger.info("Unzipping target-files for diffing...")
2273 target_dir = common.UnzipTemp(args[0], TARGET_DIFFING_UNZIP_PATTERN)
2274 source_dir = common.UnzipTemp(
2275 OPTIONS.incremental_source, TARGET_DIFFING_UNZIP_PATTERN)
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002276
Tao Baof0c4aa22018-04-30 20:29:30 -07002277 with open(OPTIONS.log_diff, 'w') as out_file:
2278 import target_files_diff
2279 target_files_diff.recursiveDiff(
2280 '', source_dir, target_dir, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07002281
Tao Bao32fcdab2018-10-12 10:30:39 -07002282 logger.info("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002283
2284
2285if __name__ == '__main__':
2286 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002287 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002288 main(sys.argv[1:])
Tao Bao32fcdab2018-10-12 10:30:39 -07002289 except common.ExternalError:
2290 logger.exception("\n ERROR:\n")
Doug Zongkereef39442009-04-02 12:14:19 -07002291 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002292 finally:
2293 common.Cleanup()