blob: 67cec1c8b79b20183ba6cacf524d75ab44d98ba1 [file] [log] [blame]
Doug Zongkereef39442009-04-02 12:14:19 -07001#!/usr/bin/env python
2#
3# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""
Tao Bao30df8b42018-04-23 15:32:53 -070018Given a target-files zipfile, produces an OTA package that installs that build.
19An incremental OTA is produced if -i is given, otherwise a full OTA is produced.
Doug Zongkereef39442009-04-02 12:14:19 -070020
Tao Bao30df8b42018-04-23 15:32:53 -070021Usage: ota_from_target_files [options] input_target_files output_ota_package
Doug Zongkereef39442009-04-02 12:14:19 -070022
Tao Bao30df8b42018-04-23 15:32:53 -070023Common options that apply to both of non-A/B and A/B OTAs
24
25 --downgrade
26 Intentionally generate an incremental OTA that updates from a newer build
Tao Baofaa8e0b2018-04-12 14:31:43 -070027 to an older one (e.g. downgrading from P preview back to O MR1).
28 "ota-downgrade=yes" will be set in the package metadata file. A data wipe
29 will always be enforced when using this flag, so "ota-wipe=yes" will also
30 be included in the metadata file. The update-binary in the source build
31 will be used in the OTA package, unless --binary flag is specified. Please
32 also check the comment for --override_timestamp below.
Tao Bao30df8b42018-04-23 15:32:53 -070033
34 -i (--incremental_from) <file>
35 Generate an incremental OTA using the given target-files zip as the
36 starting build.
37
38 -k (--package_key) <key>
39 Key to use to sign the package (default is the value of
40 default_system_dev_certificate from the input target-files's
Tao Bao59cf0c52019-06-25 10:04:24 -070041 META/misc_info.txt, or "build/make/target/product/security/testkey" if
42 that value is not specified).
Doug Zongkerafb32ea2011-09-22 10:28:04 -070043
44 For incremental OTAs, the default value is based on the source
45 target-file, not the target build.
Doug Zongkereef39442009-04-02 12:14:19 -070046
Tao Bao30df8b42018-04-23 15:32:53 -070047 --override_timestamp
48 Intentionally generate an incremental OTA that updates from a newer build
Tao Baofaa8e0b2018-04-12 14:31:43 -070049 to an older one (based on timestamp comparison), by setting the downgrade
50 flag in the package metadata. This differs from --downgrade flag, as we
51 don't enforce a data wipe with this flag. Because we know for sure this is
52 NOT an actual downgrade case, but two builds happen to be cut in a reverse
53 order (e.g. from two branches). A legit use case is that we cut a new
54 build C (after having A and B), but want to enfore an update path of A ->
55 C -> B. Specifying --downgrade may not help since that would enforce a
56 data wipe for C -> B update.
57
58 We used to set a fake timestamp in the package metadata for this flow. But
59 now we consolidate the two cases (i.e. an actual downgrade, or a downgrade
60 based on timestamp) with the same "ota-downgrade=yes" flag, with the
61 difference being whether "ota-wipe=yes" is set.
Doug Zongkereef39442009-04-02 12:14:19 -070062
Tao Bao30df8b42018-04-23 15:32:53 -070063 --wipe_user_data
64 Generate an OTA package that will wipe the user data partition when
65 installed.
66
Yifan Hong50e79542018-11-08 17:44:12 -080067 --retrofit_dynamic_partitions
68 Generates an OTA package that updates a device to support dynamic
69 partitions (default False). This flag is implied when generating
70 an incremental OTA where the base build does not support dynamic
71 partitions but the target build does. For A/B, when this flag is set,
72 --skip_postinstall is implied.
73
xunchangabfa2652019-02-19 16:27:10 -080074 --skip_compatibility_check
Yifan Hong9276cf02019-08-21 16:37:04 -070075 Skip checking compatibility of the input target files package.
xunchangabfa2652019-02-19 16:27:10 -080076
xunchang1cfe2512019-02-19 14:14:48 -080077 --output_metadata_path
78 Write a copy of the metadata to a separate file. Therefore, users can
79 read the post build fingerprint without extracting the OTA package.
80
Tao Bao30df8b42018-04-23 15:32:53 -070081Non-A/B OTA specific options
82
83 -b (--binary) <file>
84 Use the given binary as the update-binary in the output package, instead
85 of the binary in the build's target_files. Use for development only.
86
87 --block
88 Generate a block-based OTA for non-A/B device. We have deprecated the
89 support for file-based OTA since O. Block-based OTA will be used by
90 default for all non-A/B devices. Keeping this flag here to not break
91 existing callers.
92
93 -e (--extra_script) <file>
94 Insert the contents of file at the end of the update script.
Tao Bao43078aa2015-04-21 14:32:35 -070095
leozwangaa6c1a12015-08-14 10:57:58 -070096 --full_bootloader
97 Similar to --full_radio. When generating an incremental OTA, always
98 include a full copy of bootloader image.
99
Tao Bao30df8b42018-04-23 15:32:53 -0700100 --full_radio
101 When generating an incremental OTA, always include a full copy of radio
102 image. This option is only meaningful when -i is specified, because a full
103 radio is always included in a full OTA if applicable.
Michael Runge63f01de2014-10-28 19:24:19 -0700104
Tao Bao30df8b42018-04-23 15:32:53 -0700105 --log_diff <file>
106 Generate a log file that shows the differences in the source and target
107 builds for an incremental package. This option is only meaningful when -i
108 is specified.
109
110 -o (--oem_settings) <main_file[,additional_files...]>
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800111 Comma seperated list of files used to specify the expected OEM-specific
Tao Bao481bab82017-12-21 11:23:09 -0800112 properties on the OEM partition of the intended device. Multiple expected
113 values can be used by providing multiple files. Only the first dict will
114 be used to compute fingerprint, while the rest will be used to assert
115 OEM-specific properties.
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800116
Tao Bao8608cde2016-02-25 19:49:55 -0800117 --oem_no_mount
Tao Bao30df8b42018-04-23 15:32:53 -0700118 For devices with OEM-specific properties but without an OEM partition, do
119 not mount the OEM partition in the updater-script. This should be very
120 rarely used, since it's expected to have a dedicated OEM partition for
121 OEM-specific properties. Only meaningful when -o is specified.
Tao Bao8608cde2016-02-25 19:49:55 -0800122
Tao Bao30df8b42018-04-23 15:32:53 -0700123 --stash_threshold <float>
124 Specify the threshold that will be used to compute the maximum allowed
125 stash size (defaults to 0.8).
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700126
Tao Bao30df8b42018-04-23 15:32:53 -0700127 -t (--worker_threads) <int>
128 Specify the number of worker-threads that will be used when generating
129 patches for incremental updates (defaults to 3).
Tao Bao3e6161a2017-02-28 11:48:48 -0800130
Tao Bao30df8b42018-04-23 15:32:53 -0700131 --verify
132 Verify the checksums of the updated system and vendor (if any) partitions.
133 Non-A/B incremental OTAs only.
Doug Zongker1c390a22009-05-14 19:06:36 -0700134
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800135 -2 (--two_step)
Tao Bao30df8b42018-04-23 15:32:53 -0700136 Generate a 'two-step' OTA package, where recovery is updated first, so
137 that any changes made to the system partition are done using the new
138 recovery (new kernel, etc.).
139
140A/B OTA specific options
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800141
Tianjie Xu1b079832019-08-28 12:19:23 -0700142 --disable_fec_computation
143 Disable the on device FEC data computation for incremental updates.
144
Tao Baof7140c02018-01-30 17:09:24 -0800145 --include_secondary
146 Additionally include the payload for secondary slot images (default:
147 False). Only meaningful when generating A/B OTAs.
148
149 By default, an A/B OTA package doesn't contain the images for the
150 secondary slot (e.g. system_other.img). Specifying this flag allows
151 generating a separate payload that will install secondary slot images.
152
153 Such a package needs to be applied in a two-stage manner, with a reboot
154 in-between. During the first stage, the updater applies the primary
155 payload only. Upon finishing, it reboots the device into the newly updated
156 slot. It then continues to install the secondary payload to the inactive
157 slot, but without switching the active slot at the end (needs the matching
158 support in update_engine, i.e. SWITCH_SLOT_ON_REBOOT flag).
159
160 Due to the special install procedure, the secondary payload will be always
161 generated as a full payload.
162
Tao Baodea0f8b2016-06-20 17:55:06 -0700163 --payload_signer <signer>
164 Specify the signer when signing the payload and metadata for A/B OTAs.
165 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
166 with the package private key. If the private key cannot be accessed
167 directly, a payload signer that knows how to do that should be specified.
168 The signer will be supplied with "-inkey <path_to_key>",
169 "-in <input_file>" and "-out <output_file>" parameters.
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700170
171 --payload_signer_args <args>
172 Specify the arguments needed for payload signer.
Tao Bao15a146a2018-02-21 16:06:59 -0800173
xunchang376cc7c2019-04-08 23:04:58 -0700174 --payload_signer_key_size <key_size>
175 Specify the key size in bytes of the payload signer.
176
Tao Bao15a146a2018-02-21 16:06:59 -0800177 --skip_postinstall
178 Skip the postinstall hooks when generating an A/B OTA package (default:
179 False). Note that this discards ALL the hooks, including non-optional
180 ones. Should only be used if caller knows it's safe to do so (e.g. all the
181 postinstall work is to dexopt apps and a data wipe will happen immediately
182 after). Only meaningful when generating A/B OTAs.
Doug Zongkereef39442009-04-02 12:14:19 -0700183"""
184
Tao Bao89fbb0f2017-01-10 10:47:58 -0800185from __future__ import print_function
186
Tianjie Xuf67dd802019-05-20 17:50:36 -0700187import collections
Tao Bao32fcdab2018-10-12 10:30:39 -0700188import logging
Doug Zongkerfc44a512014-08-26 13:10:25 -0700189import multiprocessing
Tao Bao2dd1c482017-02-03 16:49:39 -0800190import os.path
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700191import shlex
Tao Bao15a146a2018-02-21 16:06:59 -0800192import shutil
Tao Bao85f16982018-03-08 16:28:33 -0800193import struct
Tao Bao481bab82017-12-21 11:23:09 -0800194import sys
Doug Zongkereef39442009-04-02 12:14:19 -0700195import zipfile
196
Yifan Hong9276cf02019-08-21 16:37:04 -0700197import check_target_files_vintf
Doug Zongkereef39442009-04-02 12:14:19 -0700198import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700199import edify_generator
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700200import verity_utils
Doug Zongkereef39442009-04-02 12:14:19 -0700201
Tao Bao481bab82017-12-21 11:23:09 -0800202if sys.hexversion < 0x02070000:
203 print("Python 2.7 or newer is required.", file=sys.stderr)
204 sys.exit(1)
205
Tao Bao32fcdab2018-10-12 10:30:39 -0700206logger = logging.getLogger(__name__)
Tao Bao481bab82017-12-21 11:23:09 -0800207
Doug Zongkereef39442009-04-02 12:14:19 -0700208OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700209OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700210OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700211OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700212OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700213OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800214OPTIONS.downgrade = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700215OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700216OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
217if OPTIONS.worker_threads == 0:
218 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800219OPTIONS.two_step = False
Tao Baof7140c02018-01-30 17:09:24 -0800220OPTIONS.include_secondary = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900221OPTIONS.no_signing = False
Tao Bao457cbf62017-03-06 09:56:01 -0800222OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -0800223OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700224OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800225OPTIONS.oem_no_mount = False
Tao Bao43078aa2015-04-21 14:32:35 -0700226OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700227OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700228# Stash size cannot exceed cache_size * threshold.
229OPTIONS.cache_size = None
230OPTIONS.stash_threshold = 0.8
Tao Baod62c6032015-11-30 09:40:20 -0800231OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700232OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700233OPTIONS.payload_signer_args = []
xunchang376cc7c2019-04-08 23:04:58 -0700234OPTIONS.payload_signer_key_size = None
Tao Bao5f8ff932017-03-21 22:35:00 -0700235OPTIONS.extracted_input = None
Christian Oderf63e2cd2017-05-01 22:30:15 +0200236OPTIONS.key_passwords = []
Tao Bao15a146a2018-02-21 16:06:59 -0800237OPTIONS.skip_postinstall = False
Yifan Hong50e79542018-11-08 17:44:12 -0800238OPTIONS.retrofit_dynamic_partitions = False
xunchangabfa2652019-02-19 16:27:10 -0800239OPTIONS.skip_compatibility_check = False
xunchang1cfe2512019-02-19 14:14:48 -0800240OPTIONS.output_metadata_path = None
Tianjie Xu1b079832019-08-28 12:19:23 -0700241OPTIONS.disable_fec_computation = False
Tao Bao15a146a2018-02-21 16:06:59 -0800242
Tao Bao8dcf7382015-05-21 14:09:49 -0700243
Tao Bao2dd1c482017-02-03 16:49:39 -0800244METADATA_NAME = 'META-INF/com/android/metadata'
Tao Bao15a146a2018-02-21 16:06:59 -0800245POSTINSTALL_CONFIG = 'META/postinstall_config.txt'
Yifan Hong50e79542018-11-08 17:44:12 -0800246DYNAMIC_PARTITION_INFO = 'META/dynamic_partitions_info.txt'
Yifan Hongb433eba2019-03-06 12:42:53 -0800247AB_PARTITIONS = 'META/ab_partitions.txt'
Tao Bao04808502019-07-25 23:11:41 -0700248UNZIP_PATTERN = ['IMAGES/*', 'META/*', 'OTA/*', 'RADIO/*']
Tao Baof0c4aa22018-04-30 20:29:30 -0700249# Files to be unzipped for target diffing purpose.
250TARGET_DIFFING_UNZIP_PATTERN = ['BOOT', 'RECOVERY', 'SYSTEM/*', 'VENDOR/*',
251 'PRODUCT/*', 'SYSTEM_EXT/*', 'ODM/*']
Yifan Hongb433eba2019-03-06 12:42:53 -0800252RETROFIT_DAP_UNZIP_PATTERN = ['OTA/super_*.img', AB_PARTITIONS]
Tao Bao6b0b2f92017-03-05 11:38:11 -0800253
Tao Bao2dd1c482017-02-03 16:49:39 -0800254
Tao Bao481bab82017-12-21 11:23:09 -0800255class BuildInfo(object):
256 """A class that holds the information for a given build.
257
258 This class wraps up the property querying for a given source or target build.
259 It abstracts away the logic of handling OEM-specific properties, and caches
260 the commonly used properties such as fingerprint.
261
262 There are two types of info dicts: a) build-time info dict, which is generated
263 at build time (i.e. included in a target_files zip); b) OEM info dict that is
264 specified at package generation time (via command line argument
265 '--oem_settings'). If a build doesn't use OEM-specific properties (i.e. not
266 having "oem_fingerprint_properties" in build-time info dict), all the queries
267 would be answered based on build-time info dict only. Otherwise if using
268 OEM-specific properties, some of them will be calculated from two info dicts.
269
270 Users can query properties similarly as using a dict() (e.g. info['fstab']),
271 or to query build properties via GetBuildProp() or GetVendorBuildProp().
272
273 Attributes:
274 info_dict: The build-time info dict.
275 is_ab: Whether it's a build that uses A/B OTA.
276 oem_dicts: A list of OEM dicts.
277 oem_props: A list of OEM properties that should be read from OEM dicts; None
278 if the build doesn't use any OEM-specific property.
279 fingerprint: The fingerprint of the build, which would be calculated based
280 on OEM properties if applicable.
281 device: The device name, which could come from OEM dicts if applicable.
282 """
283
Steven Laver9e73e822019-01-29 20:20:08 -0800284 _RO_PRODUCT_RESOLVE_PROPS = ["ro.product.brand", "ro.product.device",
285 "ro.product.manufacturer", "ro.product.model",
286 "ro.product.name"]
Justin Yun6151e3f2019-06-25 15:58:13 +0900287 _RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER = ["product", "odm", "vendor",
288 "system_ext", "system"]
Steven Laver9e73e822019-01-29 20:20:08 -0800289
Tao Bao481bab82017-12-21 11:23:09 -0800290 def __init__(self, info_dict, oem_dicts):
291 """Initializes a BuildInfo instance with the given dicts.
292
Tao Bao667c7532018-07-06 10:13:59 -0700293 Note that it only wraps up the given dicts, without making copies.
294
Tao Bao481bab82017-12-21 11:23:09 -0800295 Arguments:
296 info_dict: The build-time info dict.
297 oem_dicts: A list of OEM dicts (which is parsed from --oem_settings). Note
298 that it always uses the first dict to calculate the fingerprint or the
299 device name. The rest would be used for asserting OEM properties only
Tao Bao667c7532018-07-06 10:13:59 -0700300 (e.g. one package can be installed on one of these devices).
Tao Bao481bab82017-12-21 11:23:09 -0800301 """
302 self.info_dict = info_dict
303 self.oem_dicts = oem_dicts
304
305 self._is_ab = info_dict.get("ab_update") == "true"
306 self._oem_props = info_dict.get("oem_fingerprint_properties")
307
308 if self._oem_props:
309 assert oem_dicts, "OEM source required for this build"
310
311 # These two should be computed only after setting self._oem_props.
312 self._device = self.GetOemProperty("ro.product.device")
313 self._fingerprint = self.CalculateFingerprint()
314
315 @property
316 def is_ab(self):
317 return self._is_ab
318
319 @property
320 def device(self):
321 return self._device
322
323 @property
324 def fingerprint(self):
325 return self._fingerprint
326
327 @property
Tao Baoea6cbd02018-09-05 13:06:37 -0700328 def vendor_fingerprint(self):
Yifan Hong51d37562019-04-23 17:06:46 -0700329 return self._fingerprint_of("vendor")
330
331 @property
332 def product_fingerprint(self):
333 return self._fingerprint_of("product")
334
335 @property
336 def odm_fingerprint(self):
337 return self._fingerprint_of("odm")
338
339 def _fingerprint_of(self, partition):
340 if partition + ".build.prop" not in self.info_dict:
Tao Baoea6cbd02018-09-05 13:06:37 -0700341 return None
Yifan Hong51d37562019-04-23 17:06:46 -0700342 build_prop = self.info_dict[partition + ".build.prop"]
343 if "ro." + partition + ".build.fingerprint" in build_prop:
344 return build_prop["ro." + partition + ".build.fingerprint"]
345 if "ro." + partition + ".build.thumbprint" in build_prop:
346 return build_prop["ro." + partition + ".build.thumbprint"]
Tao Baoea6cbd02018-09-05 13:06:37 -0700347 return None
348
349 @property
Tao Bao481bab82017-12-21 11:23:09 -0800350 def oem_props(self):
351 return self._oem_props
352
353 def __getitem__(self, key):
354 return self.info_dict[key]
355
Tao Bao667c7532018-07-06 10:13:59 -0700356 def __setitem__(self, key, value):
357 self.info_dict[key] = value
358
Tao Bao481bab82017-12-21 11:23:09 -0800359 def get(self, key, default=None):
360 return self.info_dict.get(key, default)
361
Tao Bao667c7532018-07-06 10:13:59 -0700362 def items(self):
363 return self.info_dict.items()
364
Tao Bao481bab82017-12-21 11:23:09 -0800365 def GetBuildProp(self, prop):
366 """Returns the inquired build property."""
Steven Laver9e73e822019-01-29 20:20:08 -0800367 if prop in BuildInfo._RO_PRODUCT_RESOLVE_PROPS:
368 return self._ResolveRoProductBuildProp(prop)
369
Tao Bao481bab82017-12-21 11:23:09 -0800370 try:
371 return self.info_dict.get("build.prop", {})[prop]
372 except KeyError:
373 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
374
Steven Laver9e73e822019-01-29 20:20:08 -0800375 def _ResolveRoProductBuildProp(self, prop):
376 """Resolves the inquired ro.product.* build property"""
377 prop_val = self.info_dict.get("build.prop", {}).get(prop)
378 if prop_val:
379 return prop_val
380
381 source_order_val = self.info_dict.get("build.prop", {}).get(
Tao Bao59cf0c52019-06-25 10:04:24 -0700382 "ro.product.property_source_order")
Steven Laver9e73e822019-01-29 20:20:08 -0800383 if source_order_val:
384 source_order = source_order_val.split(",")
385 else:
386 source_order = BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER
387
388 # Check that all sources in ro.product.property_source_order are valid
389 if any([x not in BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER
390 for x in source_order]):
391 raise common.ExternalError(
Tao Bao59cf0c52019-06-25 10:04:24 -0700392 "Invalid ro.product.property_source_order '{}'".format(source_order))
Steven Laver9e73e822019-01-29 20:20:08 -0800393
394 for source in source_order:
Tao Bao59cf0c52019-06-25 10:04:24 -0700395 source_prop = prop.replace(
396 "ro.product", "ro.product.{}".format(source), 1)
397 prop_val = self.info_dict.get(
398 "{}.build.prop".format(source), {}).get(source_prop)
Steven Laver9e73e822019-01-29 20:20:08 -0800399 if prop_val:
400 return prop_val
401
402 raise common.ExternalError("couldn't resolve {}".format(prop))
403
Tao Bao481bab82017-12-21 11:23:09 -0800404 def GetVendorBuildProp(self, prop):
405 """Returns the inquired vendor build property."""
406 try:
407 return self.info_dict.get("vendor.build.prop", {})[prop]
408 except KeyError:
409 raise common.ExternalError(
410 "couldn't find %s in vendor.build.prop" % (prop,))
411
412 def GetOemProperty(self, key):
413 if self.oem_props is not None and key in self.oem_props:
414 return self.oem_dicts[0][key]
415 return self.GetBuildProp(key)
416
417 def CalculateFingerprint(self):
418 if self.oem_props is None:
Steven Laver9e73e822019-01-29 20:20:08 -0800419 try:
420 return self.GetBuildProp("ro.build.fingerprint")
421 except common.ExternalError:
422 return "{}/{}/{}:{}/{}/{}:{}/{}".format(
Tao Bao59cf0c52019-06-25 10:04:24 -0700423 self.GetBuildProp("ro.product.brand"),
424 self.GetBuildProp("ro.product.name"),
425 self.GetBuildProp("ro.product.device"),
426 self.GetBuildProp("ro.build.version.release"),
427 self.GetBuildProp("ro.build.id"),
428 self.GetBuildProp("ro.build.version.incremental"),
429 self.GetBuildProp("ro.build.type"),
430 self.GetBuildProp("ro.build.tags"))
Tao Bao481bab82017-12-21 11:23:09 -0800431 return "%s/%s/%s:%s" % (
432 self.GetOemProperty("ro.product.brand"),
433 self.GetOemProperty("ro.product.name"),
434 self.GetOemProperty("ro.product.device"),
435 self.GetBuildProp("ro.build.thumbprint"))
436
437 def WriteMountOemScript(self, script):
438 assert self.oem_props is not None
439 recovery_mount_options = self.info_dict.get("recovery_mount_options")
440 script.Mount("/oem", recovery_mount_options)
441
442 def WriteDeviceAssertions(self, script, oem_no_mount):
443 # Read the property directly if not using OEM properties.
444 if not self.oem_props:
445 script.AssertDevice(self.device)
446 return
447
448 # Otherwise assert OEM properties.
449 if not self.oem_dicts:
450 raise common.ExternalError(
451 "No OEM file provided to answer expected assertions")
452
453 for prop in self.oem_props.split():
454 values = []
455 for oem_dict in self.oem_dicts:
456 if prop in oem_dict:
457 values.append(oem_dict[prop])
458 if not values:
459 raise common.ExternalError(
460 "The OEM file is missing the property %s" % (prop,))
461 script.AssertOemProperty(prop, values, oem_no_mount)
462
463
Tao Baofabe0832018-01-17 15:52:28 -0800464class PayloadSigner(object):
465 """A class that wraps the payload signing works.
466
467 When generating a Payload, hashes of the payload and metadata files will be
468 signed with the device key, either by calling an external payload signer or
469 by calling openssl with the package key. This class provides a unified
470 interface, so that callers can just call PayloadSigner.Sign().
471
472 If an external payload signer has been specified (OPTIONS.payload_signer), it
473 calls the signer with the provided args (OPTIONS.payload_signer_args). Note
474 that the signing key should be provided as part of the payload_signer_args.
475 Otherwise without an external signer, it uses the package key
476 (OPTIONS.package_key) and calls openssl for the signing works.
477 """
478
479 def __init__(self):
480 if OPTIONS.payload_signer is None:
481 # Prepare the payload signing key.
482 private_key = OPTIONS.package_key + OPTIONS.private_key_suffix
483 pw = OPTIONS.key_passwords[OPTIONS.package_key]
484
485 cmd = ["openssl", "pkcs8", "-in", private_key, "-inform", "DER"]
486 cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"])
487 signing_key = common.MakeTempFile(prefix="key-", suffix=".key")
488 cmd.extend(["-out", signing_key])
Tao Baobec89c12018-10-15 11:53:28 -0700489 common.RunAndCheckOutput(cmd, verbose=False)
Tao Baofabe0832018-01-17 15:52:28 -0800490
491 self.signer = "openssl"
492 self.signer_args = ["pkeyutl", "-sign", "-inkey", signing_key,
493 "-pkeyopt", "digest:sha256"]
xunchang376cc7c2019-04-08 23:04:58 -0700494 self.key_size = self._GetKeySizeInBytes(signing_key)
Tao Baofabe0832018-01-17 15:52:28 -0800495 else:
496 self.signer = OPTIONS.payload_signer
497 self.signer_args = OPTIONS.payload_signer_args
xunchang376cc7c2019-04-08 23:04:58 -0700498 if OPTIONS.payload_signer_key_size:
499 self.key_size = int(OPTIONS.payload_signer_key_size)
500 assert self.key_size == 256 or self.key_size == 512, \
501 "Unsupported key size {}".format(OPTIONS.payload_signer_key_size)
502 else:
503 self.key_size = 256
504
505 @staticmethod
506 def _GetKeySizeInBytes(signing_key):
507 modulus_file = common.MakeTempFile(prefix="modulus-")
508 cmd = ["openssl", "rsa", "-inform", "PEM", "-in", signing_key, "-modulus",
509 "-noout", "-out", modulus_file]
510 common.RunAndCheckOutput(cmd, verbose=False)
511
512 with open(modulus_file) as f:
513 modulus_string = f.read()
514 # The modulus string has the format "Modulus=$data", where $data is the
515 # concatenation of hex dump of the modulus.
516 MODULUS_PREFIX = "Modulus="
517 assert modulus_string.startswith(MODULUS_PREFIX)
518 modulus_string = modulus_string[len(MODULUS_PREFIX):]
Tao Bao59cf0c52019-06-25 10:04:24 -0700519 key_size = len(modulus_string) // 2
xunchang376cc7c2019-04-08 23:04:58 -0700520 assert key_size == 256 or key_size == 512, \
521 "Unsupported key size {}".format(key_size)
522 return key_size
Tao Baofabe0832018-01-17 15:52:28 -0800523
524 def Sign(self, in_file):
525 """Signs the given input file. Returns the output filename."""
526 out_file = common.MakeTempFile(prefix="signed-", suffix=".bin")
527 cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file]
Tao Bao718faed2019-08-02 13:24:19 -0700528 common.RunAndCheckOutput(cmd)
Tao Baofabe0832018-01-17 15:52:28 -0800529 return out_file
530
531
Tao Bao40b18822018-01-30 18:19:04 -0800532class Payload(object):
533 """Manages the creation and the signing of an A/B OTA Payload."""
534
535 PAYLOAD_BIN = 'payload.bin'
536 PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
Tao Baof7140c02018-01-30 17:09:24 -0800537 SECONDARY_PAYLOAD_BIN = 'secondary/payload.bin'
538 SECONDARY_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt'
Tao Bao40b18822018-01-30 18:19:04 -0800539
Tao Bao667ff572018-02-10 00:02:40 -0800540 def __init__(self, secondary=False):
541 """Initializes a Payload instance.
542
543 Args:
544 secondary: Whether it's generating a secondary payload (default: False).
545 """
Tao Bao40b18822018-01-30 18:19:04 -0800546 self.payload_file = None
547 self.payload_properties = None
Tao Bao667ff572018-02-10 00:02:40 -0800548 self.secondary = secondary
Tao Bao40b18822018-01-30 18:19:04 -0800549
Tao Baof0c4aa22018-04-30 20:29:30 -0700550 def _Run(self, cmd): # pylint: disable=no-self-use
Tao Bao718faed2019-08-02 13:24:19 -0700551 # Don't pipe (buffer) the output if verbose is set. Let
552 # brillo_update_payload write to stdout/stderr directly, so its progress can
553 # be monitored.
554 if OPTIONS.verbose:
555 common.RunAndCheckOutput(cmd, stdout=None, stderr=None)
556 else:
557 common.RunAndCheckOutput(cmd)
558
Tao Bao40b18822018-01-30 18:19:04 -0800559 def Generate(self, target_file, source_file=None, additional_args=None):
560 """Generates a payload from the given target-files zip(s).
561
562 Args:
563 target_file: The filename of the target build target-files zip.
564 source_file: The filename of the source build target-files zip; or None if
565 generating a full OTA.
566 additional_args: A list of additional args that should be passed to
567 brillo_update_payload script; or None.
568 """
569 if additional_args is None:
570 additional_args = []
571
572 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
573 cmd = ["brillo_update_payload", "generate",
574 "--payload", payload_file,
575 "--target_image", target_file]
576 if source_file is not None:
577 cmd.extend(["--source_image", source_file])
Tianjie Xu1b079832019-08-28 12:19:23 -0700578 if OPTIONS.disable_fec_computation:
579 cmd.extend(["--disable_fec_computation", "true"])
Tao Bao40b18822018-01-30 18:19:04 -0800580 cmd.extend(additional_args)
Tao Bao718faed2019-08-02 13:24:19 -0700581 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800582
583 self.payload_file = payload_file
584 self.payload_properties = None
585
586 def Sign(self, payload_signer):
587 """Generates and signs the hashes of the payload and metadata.
588
589 Args:
590 payload_signer: A PayloadSigner() instance that serves the signing work.
591
592 Raises:
593 AssertionError: On any failure when calling brillo_update_payload script.
594 """
595 assert isinstance(payload_signer, PayloadSigner)
596
597 # 1. Generate hashes of the payload and metadata files.
598 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
599 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
600 cmd = ["brillo_update_payload", "hash",
601 "--unsigned_payload", self.payload_file,
xunchang376cc7c2019-04-08 23:04:58 -0700602 "--signature_size", str(payload_signer.key_size),
Tao Bao40b18822018-01-30 18:19:04 -0800603 "--metadata_hash_file", metadata_sig_file,
604 "--payload_hash_file", payload_sig_file]
Tao Bao718faed2019-08-02 13:24:19 -0700605 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800606
607 # 2. Sign the hashes.
608 signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
609 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
610
611 # 3. Insert the signatures back into the payload file.
612 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
613 suffix=".bin")
614 cmd = ["brillo_update_payload", "sign",
615 "--unsigned_payload", self.payload_file,
616 "--payload", signed_payload_file,
xunchang376cc7c2019-04-08 23:04:58 -0700617 "--signature_size", str(payload_signer.key_size),
Tao Bao40b18822018-01-30 18:19:04 -0800618 "--metadata_signature_file", signed_metadata_sig_file,
619 "--payload_signature_file", signed_payload_sig_file]
Tao Bao718faed2019-08-02 13:24:19 -0700620 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800621
622 # 4. Dump the signed payload properties.
623 properties_file = common.MakeTempFile(prefix="payload-properties-",
624 suffix=".txt")
625 cmd = ["brillo_update_payload", "properties",
626 "--payload", signed_payload_file,
627 "--properties_file", properties_file]
Tao Bao718faed2019-08-02 13:24:19 -0700628 self._Run(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800629
Tao Bao667ff572018-02-10 00:02:40 -0800630 if self.secondary:
631 with open(properties_file, "a") as f:
632 f.write("SWITCH_SLOT_ON_REBOOT=0\n")
633
Tao Bao40b18822018-01-30 18:19:04 -0800634 if OPTIONS.wipe_user_data:
635 with open(properties_file, "a") as f:
636 f.write("POWERWASH=1\n")
637
638 self.payload_file = signed_payload_file
639 self.payload_properties = properties_file
640
Tao Bao667ff572018-02-10 00:02:40 -0800641 def WriteToZip(self, output_zip):
Tao Bao40b18822018-01-30 18:19:04 -0800642 """Writes the payload to the given zip.
643
644 Args:
645 output_zip: The output ZipFile instance.
646 """
647 assert self.payload_file is not None
648 assert self.payload_properties is not None
649
Tao Bao667ff572018-02-10 00:02:40 -0800650 if self.secondary:
Tao Baof7140c02018-01-30 17:09:24 -0800651 payload_arcname = Payload.SECONDARY_PAYLOAD_BIN
652 payload_properties_arcname = Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT
653 else:
654 payload_arcname = Payload.PAYLOAD_BIN
655 payload_properties_arcname = Payload.PAYLOAD_PROPERTIES_TXT
656
Tao Bao40b18822018-01-30 18:19:04 -0800657 # Add the signed payload file and properties into the zip. In order to
658 # support streaming, we pack them as ZIP_STORED. So these entries can be
659 # read directly with the offset and length pairs.
Tao Baof7140c02018-01-30 17:09:24 -0800660 common.ZipWrite(output_zip, self.payload_file, arcname=payload_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800661 compress_type=zipfile.ZIP_STORED)
662 common.ZipWrite(output_zip, self.payload_properties,
Tao Baof7140c02018-01-30 17:09:24 -0800663 arcname=payload_properties_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800664 compress_type=zipfile.ZIP_STORED)
665
666
Doug Zongkereef39442009-04-02 12:14:19 -0700667def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200668 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700669
Doug Zongker951495f2009-08-14 12:44:19 -0700670 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
671 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700672
673
Tao Bao481bab82017-12-21 11:23:09 -0800674def _LoadOemDicts(oem_source):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800675 """Returns the list of loaded OEM properties dict."""
Tao Bao481bab82017-12-21 11:23:09 -0800676 if not oem_source:
677 return None
678
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800679 oem_dicts = []
Tao Bao481bab82017-12-21 11:23:09 -0800680 for oem_file in oem_source:
681 with open(oem_file) as fp:
682 oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines()))
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800683 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700684
Doug Zongkereef39442009-04-02 12:14:19 -0700685
Tao Baod42e97e2016-11-30 12:11:57 -0800686def _WriteRecoveryImageToBoot(script, output_zip):
687 """Find and write recovery image to /boot in two-step OTA.
688
689 In two-step OTAs, we write recovery image to /boot as the first step so that
690 we can reboot to there and install a new recovery image to /recovery.
691 A special "recovery-two-step.img" will be preferred, which encodes the correct
692 path of "/boot". Otherwise the device may show "device is corrupt" message
693 when booting into /boot.
694
695 Fall back to using the regular recovery.img if the two-step recovery image
696 doesn't exist. Note that rebuilding the special image at this point may be
697 infeasible, because we don't have the desired boot signer and keys when
698 calling ota_from_target_files.py.
699 """
700
701 recovery_two_step_img_name = "recovery-two-step.img"
702 recovery_two_step_img_path = os.path.join(
Tao Bao04808502019-07-25 23:11:41 -0700703 OPTIONS.input_tmp, "OTA", recovery_two_step_img_name)
Tao Baod42e97e2016-11-30 12:11:57 -0800704 if os.path.exists(recovery_two_step_img_path):
Tao Bao04808502019-07-25 23:11:41 -0700705 common.ZipWrite(
706 output_zip,
707 recovery_two_step_img_path,
708 arcname=recovery_two_step_img_name)
Tao Bao32fcdab2018-10-12 10:30:39 -0700709 logger.info(
710 "two-step package: using %s in stage 1/3", recovery_two_step_img_name)
Tao Baod42e97e2016-11-30 12:11:57 -0800711 script.WriteRawImage("/boot", recovery_two_step_img_name)
712 else:
Tao Bao32fcdab2018-10-12 10:30:39 -0700713 logger.info("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800714 # The "recovery.img" entry has been written into package earlier.
715 script.WriteRawImage("/boot", "recovery.img")
716
717
Doug Zongkerc9253822014-02-04 12:17:58 -0800718def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700719 namelist = [name for name in target_files_zip.namelist()]
720 return ("SYSTEM/recovery-from-boot.p" in namelist or
721 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700722
Tao Bao457cbf62017-03-06 09:56:01 -0800723
Yifan Hong51d37562019-04-23 17:06:46 -0700724def HasPartition(target_files_zip, partition):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700725 try:
Yifan Hong51d37562019-04-23 17:06:46 -0700726 target_files_zip.getinfo(partition.upper() + "/")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700727 return True
728 except KeyError:
729 return False
730
Tao Bao457cbf62017-03-06 09:56:01 -0800731
Yifan Hong9276cf02019-08-21 16:37:04 -0700732def HasTrebleEnabled(target_files, target_info):
733 def HasVendorPartition(target_files):
734 if os.path.isdir(target_files):
735 return os.path.isdir(os.path.join(target_files, "VENDOR"))
736 if zipfile.is_zipfile(target_files):
737 return HasPartition(zipfile.ZipFile(target_files), "vendor")
738 raise ValueError("Unknown target_files argument")
Yifan Hong51d37562019-04-23 17:06:46 -0700739
Yifan Hong9276cf02019-08-21 16:37:04 -0700740 return (HasVendorPartition(target_files) and
Tao Bao481bab82017-12-21 11:23:09 -0800741 target_info.GetBuildProp("ro.treble.enabled") == "true")
Tao Baobcd1d162017-08-26 13:10:26 -0700742
743
Tao Bao481bab82017-12-21 11:23:09 -0800744def WriteFingerprintAssertion(script, target_info, source_info):
745 source_oem_props = source_info.oem_props
746 target_oem_props = target_info.oem_props
Michael Runge6e836112014-04-15 17:40:21 -0700747
Tao Bao481bab82017-12-21 11:23:09 -0800748 if source_oem_props is None and target_oem_props is None:
749 script.AssertSomeFingerprint(
750 source_info.fingerprint, target_info.fingerprint)
751 elif source_oem_props is not None and target_oem_props is not None:
752 script.AssertSomeThumbprint(
753 target_info.GetBuildProp("ro.build.thumbprint"),
754 source_info.GetBuildProp("ro.build.thumbprint"))
755 elif source_oem_props is None and target_oem_props is not None:
756 script.AssertFingerprintOrThumbprint(
757 source_info.fingerprint,
758 target_info.GetBuildProp("ro.build.thumbprint"))
759 else:
760 script.AssertFingerprintOrThumbprint(
761 target_info.fingerprint,
762 source_info.GetBuildProp("ro.build.thumbprint"))
Doug Zongker73ef8252009-07-23 15:12:53 -0700763
Doug Zongkerfc44a512014-08-26 13:10:25 -0700764
Yifan Hong9276cf02019-08-21 16:37:04 -0700765def CheckVintfIfTrebleEnabled(target_files, target_info):
766 """Checks compatibility info of the input target files.
Tao Bao21803d32017-04-19 10:16:09 -0700767
Yifan Hong9276cf02019-08-21 16:37:04 -0700768 Metadata used for compatibility verification is retrieved from target_zip.
Tao Bao21803d32017-04-19 10:16:09 -0700769
Yifan Hong9276cf02019-08-21 16:37:04 -0700770 Compatibility should only be checked for devices that have enabled
Tao Baobcd1d162017-08-26 13:10:26 -0700771 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700772
773 Args:
Yifan Hong9276cf02019-08-21 16:37:04 -0700774 target_files: Path to zip file containing the source files to be included
775 for OTA. Can also be the path to extracted directory.
Tao Bao481bab82017-12-21 11:23:09 -0800776 target_info: The BuildInfo instance that holds the target build info.
Tao Bao21803d32017-04-19 10:16:09 -0700777 """
778
Tao Baobcd1d162017-08-26 13:10:26 -0700779 # Will only proceed if the target has enabled the Treble support (as well as
780 # having a /vendor partition).
Yifan Hong9276cf02019-08-21 16:37:04 -0700781 if not HasTrebleEnabled(target_files, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700782 return
783
xunchangabfa2652019-02-19 16:27:10 -0800784 # Skip adding the compatibility package as a workaround for b/114240221. The
785 # compatibility will always fail on devices without qualified kernels.
786 if OPTIONS.skip_compatibility_check:
787 return
788
Yifan Hong9276cf02019-08-21 16:37:04 -0700789 if not check_target_files_vintf.CheckVintf(target_files, target_info):
790 raise RuntimeError("VINTF compatibility check failed")
Tao Bao21803d32017-04-19 10:16:09 -0700791
792
Tianjie Xuf67dd802019-05-20 17:50:36 -0700793def GetBlockDifferences(target_zip, source_zip, target_info, source_info,
794 device_specific):
795 """Returns a ordered dict of block differences with partition name as key."""
796
797 def GetIncrementalBlockDifferenceForPartition(name):
798 if not HasPartition(source_zip, name):
799 raise RuntimeError("can't generate incremental that adds {}".format(name))
800
801 partition_src = common.GetUserImage(name, OPTIONS.source_tmp, source_zip,
802 info_dict=source_info,
803 allow_shared_blocks=allow_shared_blocks)
804
805 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
806 name, 4096, target_info)
807 partition_tgt = common.GetUserImage(name, OPTIONS.target_tmp, target_zip,
808 info_dict=target_info,
809 allow_shared_blocks=allow_shared_blocks,
810 hashtree_info_generator=
811 hashtree_info_generator)
812
813 # Check the first block of the source system partition for remount R/W only
814 # if the filesystem is ext4.
815 partition_source_info = source_info["fstab"]["/" + name]
816 check_first_block = partition_source_info.fs_type == "ext4"
817 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
818 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
819 # b) the blocks listed in block map may not contain all the bytes for a
820 # given file (because they're rounded to be 4K-aligned).
821 partition_target_info = target_info["fstab"]["/" + name]
822 disable_imgdiff = (partition_source_info.fs_type == "squashfs" or
823 partition_target_info.fs_type == "squashfs")
824 return common.BlockDifference(name, partition_src, partition_tgt,
825 check_first_block,
826 version=blockimgdiff_version,
827 disable_imgdiff=disable_imgdiff)
828
829 if source_zip:
830 # See notes in common.GetUserImage()
831 allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or
832 target_info.get('ext4_share_dup_blocks') == "true")
833 blockimgdiff_version = max(
834 int(i) for i in target_info.get(
835 "blockimgdiff_versions", "1").split(","))
836 assert blockimgdiff_version >= 3
837
838 block_diff_dict = collections.OrderedDict()
839 partition_names = ["system", "vendor", "product", "odm", "system_ext"]
840 for partition in partition_names:
841 if not HasPartition(target_zip, partition):
842 continue
843 # Full OTA update.
844 if not source_zip:
845 tgt = common.GetUserImage(partition, OPTIONS.input_tmp, target_zip,
846 info_dict=target_info,
847 reset_file_map=True)
848 block_diff_dict[partition] = common.BlockDifference(partition, tgt,
849 src=None)
850 # Incremental OTA update.
851 else:
852 block_diff_dict[partition] = GetIncrementalBlockDifferenceForPartition(
853 partition)
854 assert "system" in block_diff_dict
855
856 # Get the block diffs from the device specific script. If there is a
857 # duplicate block diff for a partition, ignore the diff in the generic script
858 # and use the one in the device specific script instead.
859 if source_zip:
860 device_specific_diffs = device_specific.IncrementalOTA_GetBlockDifferences()
861 function_name = "IncrementalOTA_GetBlockDifferences"
862 else:
863 device_specific_diffs = device_specific.FullOTA_GetBlockDifferences()
864 function_name = "FullOTA_GetBlockDifferences"
865
866 if device_specific_diffs:
867 assert all(isinstance(diff, common.BlockDifference)
868 for diff in device_specific_diffs), \
869 "{} is not returning a list of BlockDifference objects".format(
870 function_name)
871 for diff in device_specific_diffs:
872 if diff.partition in block_diff_dict:
873 logger.warning("Duplicate block difference found. Device specific block"
874 " diff for partition '%s' overrides the one in generic"
875 " script.", diff.partition)
876 block_diff_dict[diff.partition] = diff
877
878 return block_diff_dict
879
880
Tao Bao491d7e22018-02-21 13:17:22 -0800881def WriteFullOTAPackage(input_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -0800882 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700883
Tao Bao481bab82017-12-21 11:23:09 -0800884 # We don't know what version it will be installed on top of. We expect the API
885 # just won't change very often. Similarly for fstab, it might have changed in
886 # the target build.
887 target_api_version = target_info["recovery_api_version"]
888 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700889
Tao Bao481bab82017-12-21 11:23:09 -0800890 if target_info.oem_props and not OPTIONS.oem_no_mount:
891 target_info.WriteMountOemScript(script)
892
Tao Baodf3a48b2018-01-10 16:30:43 -0800893 metadata = GetPackageMetadata(target_info)
Doug Zongker2ea21062010-04-28 16:05:21 -0700894
Tao Bao491d7e22018-02-21 13:17:22 -0800895 if not OPTIONS.no_signing:
896 staging_file = common.MakeTempFile(suffix='.zip')
897 else:
898 staging_file = output_file
899
900 output_zip = zipfile.ZipFile(
901 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
902
Doug Zongker05d3dea2009-06-22 11:32:31 -0700903 device_specific = common.DeviceSpecificParams(
904 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800905 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700906 output_zip=output_zip,
907 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700908 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700909 metadata=metadata,
910 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700911
Tao Bao457cbf62017-03-06 09:56:01 -0800912 assert HasRecoveryPatch(input_zip)
Doug Zongkerc9253822014-02-04 12:17:58 -0800913
Tao Bao481bab82017-12-21 11:23:09 -0800914 # Assertions (e.g. downgrade check, device properties check).
915 ts = target_info.GetBuildProp("ro.build.date.utc")
916 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700917 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700918
Tao Bao481bab82017-12-21 11:23:09 -0800919 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700920 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800921
Tianjie Xuf67dd802019-05-20 17:50:36 -0700922 block_diff_dict = GetBlockDifferences(target_zip=input_zip, source_zip=None,
923 target_info=target_info,
924 source_info=None,
925 device_specific=device_specific)
926
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800927 # Two-step package strategy (in chronological order, which is *not*
928 # the order in which the generated script has things):
929 #
930 # if stage is not "2/3" or "3/3":
931 # write recovery image to boot partition
932 # set stage to "2/3"
933 # reboot to boot partition and restart recovery
934 # else if stage is "2/3":
935 # write recovery image to recovery partition
936 # set stage to "3/3"
937 # reboot to recovery partition and restart recovery
938 # else:
939 # (stage must be "3/3")
940 # set stage to ""
941 # do normal full package installation:
942 # wipe and install system, boot image, etc.
943 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700944 # complete script normally
945 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800946
947 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
948 OPTIONS.input_tmp, "RECOVERY")
949 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800950 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800951 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800952 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800953 assert fs.fs_type.upper() == "EMMC", \
954 "two-step packages only supported on devices with EMMC /misc partitions"
955 bcb_dev = {"bcb_dev": fs.device}
956 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
957 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700958if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800959""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800960
961 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
962 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800963 script.WriteRawImage("/recovery", "recovery.img")
964 script.AppendExtra("""
965set_stage("%(bcb_dev)s", "3/3");
966reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700967else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800968""" % bcb_dev)
969
Tao Baod42e97e2016-11-30 12:11:57 -0800970 # Stage 3/3: Make changes.
971 script.Comment("Stage 3/3")
972
Tao Bao6c55a8a2015-04-08 15:30:27 -0700973 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800974 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700975
Doug Zongkere5ff5902012-01-17 10:55:37 -0800976 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700977
Tianjie Xuf67dd802019-05-20 17:50:36 -0700978 # All other partitions as well as the data wipe use 10% of the progress, and
979 # the update of the system partition takes the remaining progress.
980 system_progress = 0.9 - (len(block_diff_dict) - 1) * 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700981 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800982 system_progress -= 0.1
Tianjie Xuf67dd802019-05-20 17:50:36 -0700983 progress_dict = {partition: 0.1 for partition in block_diff_dict}
984 progress_dict["system"] = system_progress
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700985
Yifan Hong10c530d2018-12-27 17:34:18 -0800986 if target_info.get('use_dynamic_partitions') == "true":
987 # Use empty source_info_dict to indicate that all partitions / groups must
988 # be re-added.
989 dynamic_partitions_diff = common.DynamicPartitionsDifference(
990 info_dict=OPTIONS.info_dict,
Tianjie Xuf67dd802019-05-20 17:50:36 -0700991 block_diffs=block_diff_dict.values(),
Yifan Hong10c530d2018-12-27 17:34:18 -0800992 progress_dict=progress_dict)
993 dynamic_partitions_diff.WriteScript(script, output_zip,
994 write_verify_script=OPTIONS.verify)
995 else:
Tianjie Xuf67dd802019-05-20 17:50:36 -0700996 for block_diff in block_diff_dict.values():
Yifan Hong10c530d2018-12-27 17:34:18 -0800997 block_diff.WriteScript(script, output_zip,
998 progress=progress_dict.get(block_diff.partition),
999 write_verify_script=OPTIONS.verify)
Doug Zongker73ef8252009-07-23 15:12:53 -07001000
Yifan Hong9276cf02019-08-21 16:37:04 -07001001 CheckVintfIfTrebleEnabled(OPTIONS.input_tmp, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001002
Yifan Hong10c530d2018-12-27 17:34:18 -08001003 boot_img = common.GetBootableImage(
1004 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Tao Bao481bab82017-12-21 11:23:09 -08001005 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -07001006 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001007
Doug Zongker9ce0fb62010-09-20 18:04:41 -07001008 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -07001009
Tianjie Xuf67dd802019-05-20 17:50:36 -07001010 script.ShowProgress(0.1, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001011 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -07001012
Doug Zongker1c390a22009-05-14 19:06:36 -07001013 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001014 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001015
Doug Zongker14833602010-02-02 13:12:04 -08001016 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001017
Doug Zongker922206e2014-03-04 13:16:24 -08001018 if OPTIONS.wipe_user_data:
1019 script.ShowProgress(0.1, 10)
1020 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001021
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001022 if OPTIONS.two_step:
1023 script.AppendExtra("""
1024set_stage("%(bcb_dev)s", "");
1025""" % bcb_dev)
1026 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -08001027
1028 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
1029 script.Comment("Stage 1/3")
1030 _WriteRecoveryImageToBoot(script, output_zip)
1031
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001032 script.AppendExtra("""
1033set_stage("%(bcb_dev)s", "2/3");
1034reboot_now("%(bcb_dev)s", "");
1035endif;
1036endif;
1037""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -08001038
Tao Bao5d182562016-02-23 11:38:39 -08001039 script.SetProgress(1)
1040 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001041 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001042
1043 # We haven't written the metadata entry, which will be done in
1044 # FinalizeMetadata.
1045 common.ZipClose(output_zip)
1046
1047 needed_property_files = (
1048 NonAbOtaPropertyFiles(),
1049 )
1050 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Doug Zongker2ea21062010-04-28 16:05:21 -07001051
Doug Zongkerfc44a512014-08-26 13:10:25 -07001052
xunchang1cfe2512019-02-19 14:14:48 -08001053def WriteMetadata(metadata, output):
1054 """Writes the metadata to the zip archive or a file.
1055
1056 Args:
1057 metadata: The metadata dict for the package.
1058 output: A ZipFile object or a string of the output file path.
1059 """
1060
Tao Bao59cf0c52019-06-25 10:04:24 -07001061 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.items())])
xunchang1cfe2512019-02-19 14:14:48 -08001062 if isinstance(output, zipfile.ZipFile):
1063 common.ZipWriteStr(output, METADATA_NAME, value,
1064 compress_type=zipfile.ZIP_STORED)
1065 return
1066
1067 with open(output, 'w') as f:
1068 f.write(value)
Doug Zongkereef39442009-04-02 12:14:19 -07001069
Doug Zongkerfc44a512014-08-26 13:10:25 -07001070
Tao Bao481bab82017-12-21 11:23:09 -08001071def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -08001072 # Only incremental OTAs are allowed to reach here.
1073 assert OPTIONS.incremental_source is not None
1074
Tao Bao481bab82017-12-21 11:23:09 -08001075 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
1076 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Bao59cf0c52019-06-25 10:04:24 -07001077 is_downgrade = int(post_timestamp) < int(pre_timestamp)
Tao Baob31892e2017-02-07 11:21:17 -08001078
1079 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -08001080 if not is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -07001081 raise RuntimeError(
1082 "--downgrade or --override_timestamp specified but no downgrade "
1083 "detected: pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -08001084 metadata["ota-downgrade"] = "yes"
Tao Baob31892e2017-02-07 11:21:17 -08001085 else:
1086 if is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -07001087 raise RuntimeError(
1088 "Downgrade detected based on timestamp check: pre: %s, post: %s. "
1089 "Need to specify --override_timestamp OR --downgrade to allow "
1090 "building the incremental." % (pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -08001091
1092
Tao Baodf3a48b2018-01-10 16:30:43 -08001093def GetPackageMetadata(target_info, source_info=None):
1094 """Generates and returns the metadata dict.
1095
1096 It generates a dict() that contains the info to be written into an OTA
1097 package (META-INF/com/android/metadata). It also handles the detection of
Tao Baofaa8e0b2018-04-12 14:31:43 -07001098 downgrade / data wipe based on the global options.
Tao Baodf3a48b2018-01-10 16:30:43 -08001099
1100 Args:
1101 target_info: The BuildInfo instance that holds the target build info.
1102 source_info: The BuildInfo instance that holds the source build info, or
1103 None if generating full OTA.
1104
1105 Returns:
1106 A dict to be written into package metadata entry.
1107 """
1108 assert isinstance(target_info, BuildInfo)
1109 assert source_info is None or isinstance(source_info, BuildInfo)
1110
1111 metadata = {
1112 'post-build' : target_info.fingerprint,
1113 'post-build-incremental' : target_info.GetBuildProp(
1114 'ro.build.version.incremental'),
Tao Bao35dc2552018-02-01 13:18:00 -08001115 'post-sdk-level' : target_info.GetBuildProp(
1116 'ro.build.version.sdk'),
1117 'post-security-patch-level' : target_info.GetBuildProp(
1118 'ro.build.version.security_patch'),
Tao Baodf3a48b2018-01-10 16:30:43 -08001119 }
1120
1121 if target_info.is_ab:
1122 metadata['ota-type'] = 'AB'
1123 metadata['ota-required-cache'] = '0'
1124 else:
1125 metadata['ota-type'] = 'BLOCK'
1126
1127 if OPTIONS.wipe_user_data:
1128 metadata['ota-wipe'] = 'yes'
1129
Tao Bao393eeb42019-03-06 16:00:38 -08001130 if OPTIONS.retrofit_dynamic_partitions:
1131 metadata['ota-retrofit-dynamic-partitions'] = 'yes'
1132
Tao Baodf3a48b2018-01-10 16:30:43 -08001133 is_incremental = source_info is not None
1134 if is_incremental:
1135 metadata['pre-build'] = source_info.fingerprint
1136 metadata['pre-build-incremental'] = source_info.GetBuildProp(
1137 'ro.build.version.incremental')
1138 metadata['pre-device'] = source_info.device
1139 else:
1140 metadata['pre-device'] = target_info.device
1141
Tao Baofaa8e0b2018-04-12 14:31:43 -07001142 # Use the actual post-timestamp, even for a downgrade case.
1143 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
1144
1145 # Detect downgrades and set up downgrade flags accordingly.
Tao Baodf3a48b2018-01-10 16:30:43 -08001146 if is_incremental:
1147 HandleDowngradeMetadata(metadata, target_info, source_info)
Tao Baodf3a48b2018-01-10 16:30:43 -08001148
1149 return metadata
1150
1151
Tao Baod3fc38a2018-03-08 16:09:01 -08001152class PropertyFiles(object):
1153 """A class that computes the property-files string for an OTA package.
1154
1155 A property-files string is a comma-separated string that contains the
1156 offset/size info for an OTA package. The entries, which must be ZIP_STORED,
1157 can be fetched directly with the package URL along with the offset/size info.
1158 These strings can be used for streaming A/B OTAs, or allowing an updater to
1159 download package metadata entry directly, without paying the cost of
1160 downloading entire package.
Tao Baofe5b69a2018-03-02 09:47:43 -08001161
Tao Baocc8e2662018-03-01 19:30:00 -08001162 Computing the final property-files string requires two passes. Because doing
1163 the whole package signing (with signapk.jar) will possibly reorder the ZIP
1164 entries, which may in turn invalidate earlier computed ZIP entry offset/size
1165 values.
1166
1167 This class provides functions to be called for each pass. The general flow is
1168 as follows.
1169
Tao Baod3fc38a2018-03-08 16:09:01 -08001170 property_files = PropertyFiles()
Tao Baocc8e2662018-03-01 19:30:00 -08001171 # The first pass, which writes placeholders before doing initial signing.
1172 property_files.Compute()
1173 SignOutput()
1174
1175 # The second pass, by replacing the placeholders with actual data.
1176 property_files.Finalize()
1177 SignOutput()
1178
1179 And the caller can additionally verify the final result.
1180
1181 property_files.Verify()
Tao Baofe5b69a2018-03-02 09:47:43 -08001182 """
1183
Tao Baocc8e2662018-03-01 19:30:00 -08001184 def __init__(self):
Tao Baod3fc38a2018-03-08 16:09:01 -08001185 self.name = None
1186 self.required = ()
1187 self.optional = ()
Tao Baofe5b69a2018-03-02 09:47:43 -08001188
Tao Baocc8e2662018-03-01 19:30:00 -08001189 def Compute(self, input_zip):
1190 """Computes and returns a property-files string with placeholders.
Tao Baofe5b69a2018-03-02 09:47:43 -08001191
Tao Baocc8e2662018-03-01 19:30:00 -08001192 We reserve extra space for the offset and size of the metadata entry itself,
1193 although we don't know the final values until the package gets signed.
Tao Baofe5b69a2018-03-02 09:47:43 -08001194
Tao Baocc8e2662018-03-01 19:30:00 -08001195 Args:
1196 input_zip: The input ZIP file.
Tao Baofe5b69a2018-03-02 09:47:43 -08001197
Tao Baocc8e2662018-03-01 19:30:00 -08001198 Returns:
1199 A string with placeholders for the metadata offset/size info, e.g.
1200 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1201 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001202 return self.GetPropertyFilesString(input_zip, reserve_space=True)
Tao Baofe5b69a2018-03-02 09:47:43 -08001203
Tao Baod2ce2ed2018-03-16 12:59:42 -07001204 class InsufficientSpaceException(Exception):
1205 pass
1206
Tao Baocc8e2662018-03-01 19:30:00 -08001207 def Finalize(self, input_zip, reserved_length):
1208 """Finalizes a property-files string with actual METADATA offset/size info.
1209
1210 The input ZIP file has been signed, with the ZIP entries in the desired
1211 place (signapk.jar will possibly reorder the ZIP entries). Now we compute
1212 the ZIP entry offsets and construct the property-files string with actual
1213 data. Note that during this process, we must pad the property-files string
1214 to the reserved length, so that the METADATA entry size remains the same.
1215 Otherwise the entries' offsets and sizes may change again.
1216
1217 Args:
1218 input_zip: The input ZIP file.
1219 reserved_length: The reserved length of the property-files string during
1220 the call to Compute(). The final string must be no more than this
1221 size.
1222
1223 Returns:
1224 A property-files string including the metadata offset/size info, e.g.
1225 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379 ".
1226
1227 Raises:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001228 InsufficientSpaceException: If the reserved length is insufficient to hold
1229 the final string.
Tao Baocc8e2662018-03-01 19:30:00 -08001230 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001231 result = self.GetPropertyFilesString(input_zip, reserve_space=False)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001232 if len(result) > reserved_length:
1233 raise self.InsufficientSpaceException(
1234 'Insufficient reserved space: reserved={}, actual={}'.format(
1235 reserved_length, len(result)))
1236
Tao Baocc8e2662018-03-01 19:30:00 -08001237 result += ' ' * (reserved_length - len(result))
1238 return result
1239
1240 def Verify(self, input_zip, expected):
1241 """Verifies the input ZIP file contains the expected property-files string.
1242
1243 Args:
1244 input_zip: The input ZIP file.
1245 expected: The property-files string that's computed from Finalize().
1246
1247 Raises:
1248 AssertionError: On finding a mismatch.
1249 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001250 actual = self.GetPropertyFilesString(input_zip)
Tao Baocc8e2662018-03-01 19:30:00 -08001251 assert actual == expected, \
1252 "Mismatching streaming metadata: {} vs {}.".format(actual, expected)
1253
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001254 def GetPropertyFilesString(self, zip_file, reserve_space=False):
1255 """
1256 Constructs the property-files string per request.
1257
1258 Args:
1259 zip_file: The input ZIP file.
1260 reserved_length: The reserved length of the property-files string.
1261
1262 Returns:
1263 A property-files string including the metadata offset/size info, e.g.
1264 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1265 """
Tao Baocc8e2662018-03-01 19:30:00 -08001266
1267 def ComputeEntryOffsetSize(name):
1268 """Computes the zip entry offset and size."""
1269 info = zip_file.getinfo(name)
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001270 offset = info.header_offset
1271 offset += zipfile.sizeFileHeader
1272 offset += len(info.extra) + len(info.filename)
Tao Baocc8e2662018-03-01 19:30:00 -08001273 size = info.file_size
1274 return '%s:%d:%d' % (os.path.basename(name), offset, size)
1275
1276 tokens = []
Tao Bao85f16982018-03-08 16:28:33 -08001277 tokens.extend(self._GetPrecomputed(zip_file))
Tao Baocc8e2662018-03-01 19:30:00 -08001278 for entry in self.required:
1279 tokens.append(ComputeEntryOffsetSize(entry))
1280 for entry in self.optional:
1281 if entry in zip_file.namelist():
1282 tokens.append(ComputeEntryOffsetSize(entry))
1283
1284 # 'META-INF/com/android/metadata' is required. We don't know its actual
1285 # offset and length (as well as the values for other entries). So we reserve
Tao Baod2ce2ed2018-03-16 12:59:42 -07001286 # 15-byte as a placeholder ('offset:length'), which is sufficient to cover
1287 # the space for metadata entry. Because 'offset' allows a max of 10-digit
1288 # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the
1289 # reserved space serves the metadata entry only.
Tao Baocc8e2662018-03-01 19:30:00 -08001290 if reserve_space:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001291 tokens.append('metadata:' + ' ' * 15)
Tao Baocc8e2662018-03-01 19:30:00 -08001292 else:
1293 tokens.append(ComputeEntryOffsetSize(METADATA_NAME))
1294
1295 return ','.join(tokens)
Tao Baofe5b69a2018-03-02 09:47:43 -08001296
Tao Bao85f16982018-03-08 16:28:33 -08001297 def _GetPrecomputed(self, input_zip):
1298 """Computes the additional tokens to be included into the property-files.
1299
1300 This applies to tokens without actual ZIP entries, such as
1301 payload_metadadata.bin. We want to expose the offset/size to updaters, so
1302 that they can download the payload metadata directly with the info.
1303
1304 Args:
1305 input_zip: The input zip file.
1306
1307 Returns:
1308 A list of strings (tokens) to be added to the property-files string.
1309 """
1310 # pylint: disable=no-self-use
1311 # pylint: disable=unused-argument
1312 return []
1313
Tao Baofe5b69a2018-03-02 09:47:43 -08001314
Tao Baod3fc38a2018-03-08 16:09:01 -08001315class StreamingPropertyFiles(PropertyFiles):
1316 """A subclass for computing the property-files for streaming A/B OTAs."""
1317
1318 def __init__(self):
1319 super(StreamingPropertyFiles, self).__init__()
1320 self.name = 'ota-streaming-property-files'
1321 self.required = (
1322 # payload.bin and payload_properties.txt must exist.
1323 'payload.bin',
1324 'payload_properties.txt',
1325 )
1326 self.optional = (
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001327 # care_map is available only if dm-verity is enabled.
1328 'care_map.pb',
Tao Baod3fc38a2018-03-08 16:09:01 -08001329 'care_map.txt',
1330 # compatibility.zip is available only if target supports Treble.
1331 'compatibility.zip',
1332 )
1333
1334
Tao Bao85f16982018-03-08 16:28:33 -08001335class AbOtaPropertyFiles(StreamingPropertyFiles):
1336 """The property-files for A/B OTA that includes payload_metadata.bin info.
1337
1338 Since P, we expose one more token (aka property-file), in addition to the ones
1339 for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'.
1340 'payload_metadata.bin' is the header part of a payload ('payload.bin'), which
1341 doesn't exist as a separate ZIP entry, but can be used to verify if the
1342 payload can be applied on the given device.
1343
1344 For backward compatibility, we keep both of the 'ota-streaming-property-files'
1345 and the newly added 'ota-property-files' in P. The new token will only be
1346 available in 'ota-property-files'.
1347 """
1348
1349 def __init__(self):
1350 super(AbOtaPropertyFiles, self).__init__()
1351 self.name = 'ota-property-files'
1352
1353 def _GetPrecomputed(self, input_zip):
1354 offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip)
1355 return ['payload_metadata.bin:{}:{}'.format(offset, size)]
1356
1357 @staticmethod
1358 def _GetPayloadMetadataOffsetAndSize(input_zip):
1359 """Computes the offset and size of the payload metadata for a given package.
1360
1361 (From system/update_engine/update_metadata.proto)
1362 A delta update file contains all the deltas needed to update a system from
1363 one specific version to another specific version. The update format is
1364 represented by this struct pseudocode:
1365
1366 struct delta_update_file {
1367 char magic[4] = "CrAU";
1368 uint64 file_format_version;
1369 uint64 manifest_size; // Size of protobuf DeltaArchiveManifest
1370
1371 // Only present if format_version > 1:
1372 uint32 metadata_signature_size;
1373
1374 // The Bzip2 compressed DeltaArchiveManifest
1375 char manifest[metadata_signature_size];
1376
1377 // The signature of the metadata (from the beginning of the payload up to
1378 // this location, not including the signature itself). This is a
1379 // serialized Signatures message.
1380 char medatada_signature_message[metadata_signature_size];
1381
1382 // Data blobs for files, no specific format. The specific offset
1383 // and length of each data blob is recorded in the DeltaArchiveManifest.
1384 struct {
1385 char data[];
1386 } blobs[];
1387
1388 // These two are not signed:
1389 uint64 payload_signatures_message_size;
1390 char payload_signatures_message[];
1391 };
1392
1393 'payload-metadata.bin' contains all the bytes from the beginning of the
1394 payload, till the end of 'medatada_signature_message'.
1395 """
1396 payload_info = input_zip.getinfo('payload.bin')
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001397 payload_offset = payload_info.header_offset
1398 payload_offset += zipfile.sizeFileHeader
1399 payload_offset += len(payload_info.extra) + len(payload_info.filename)
Tao Bao85f16982018-03-08 16:28:33 -08001400 payload_size = payload_info.file_size
1401
Tao Bao59cf0c52019-06-25 10:04:24 -07001402 with input_zip.open('payload.bin') as payload_fp:
Tao Bao85f16982018-03-08 16:28:33 -08001403 header_bin = payload_fp.read(24)
1404
1405 # network byte order (big-endian)
1406 header = struct.unpack("!IQQL", header_bin)
1407
1408 # 'CrAU'
1409 magic = header[0]
1410 assert magic == 0x43724155, "Invalid magic: {:x}".format(magic)
1411
1412 manifest_size = header[2]
1413 metadata_signature_size = header[3]
1414 metadata_total = 24 + manifest_size + metadata_signature_size
1415 assert metadata_total < payload_size
1416
1417 return (payload_offset, metadata_total)
1418
1419
Tao Bao491d7e22018-02-21 13:17:22 -08001420class NonAbOtaPropertyFiles(PropertyFiles):
1421 """The property-files for non-A/B OTA.
1422
1423 For non-A/B OTA, the property-files string contains the info for METADATA
1424 entry, with which a system updater can be fetched the package metadata prior
1425 to downloading the entire package.
1426 """
1427
1428 def __init__(self):
1429 super(NonAbOtaPropertyFiles, self).__init__()
1430 self.name = 'ota-property-files'
1431
1432
Tao Baod3fc38a2018-03-08 16:09:01 -08001433def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
Tao Baofe5b69a2018-03-02 09:47:43 -08001434 """Finalizes the metadata and signs an A/B OTA package.
1435
1436 In order to stream an A/B OTA package, we need 'ota-streaming-property-files'
1437 that contains the offsets and sizes for the ZIP entries. An example
1438 property-files string is as follows.
1439
1440 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379"
1441
1442 OTA server can pass down this string, in addition to the package URL, to the
1443 system update client. System update client can then fetch individual ZIP
1444 entries (ZIP_STORED) directly at the given offset of the URL.
1445
1446 Args:
1447 metadata: The metadata dict for the package.
1448 input_file: The input ZIP filename that doesn't contain the package METADATA
1449 entry yet.
1450 output_file: The final output ZIP filename.
Tao Baod3fc38a2018-03-08 16:09:01 -08001451 needed_property_files: The list of PropertyFiles' to be generated.
Tao Baofe5b69a2018-03-02 09:47:43 -08001452 """
Tao Baofe5b69a2018-03-02 09:47:43 -08001453
Tao Baod2ce2ed2018-03-16 12:59:42 -07001454 def ComputeAllPropertyFiles(input_file, needed_property_files):
1455 # Write the current metadata entry with placeholders.
1456 with zipfile.ZipFile(input_file) as input_zip:
1457 for property_files in needed_property_files:
1458 metadata[property_files.name] = property_files.Compute(input_zip)
1459 namelist = input_zip.namelist()
Tao Baofe5b69a2018-03-02 09:47:43 -08001460
Tao Baod2ce2ed2018-03-16 12:59:42 -07001461 if METADATA_NAME in namelist:
1462 common.ZipDelete(input_file, METADATA_NAME)
1463 output_zip = zipfile.ZipFile(input_file, 'a')
1464 WriteMetadata(metadata, output_zip)
1465 common.ZipClose(output_zip)
1466
1467 if OPTIONS.no_signing:
1468 return input_file
1469
Tao Bao491d7e22018-02-21 13:17:22 -08001470 prelim_signing = common.MakeTempFile(suffix='.zip')
1471 SignOutput(input_file, prelim_signing)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001472 return prelim_signing
Tao Baofe5b69a2018-03-02 09:47:43 -08001473
Tao Baod2ce2ed2018-03-16 12:59:42 -07001474 def FinalizeAllPropertyFiles(prelim_signing, needed_property_files):
1475 with zipfile.ZipFile(prelim_signing) as prelim_signing_zip:
1476 for property_files in needed_property_files:
1477 metadata[property_files.name] = property_files.Finalize(
1478 prelim_signing_zip, len(metadata[property_files.name]))
1479
1480 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP
1481 # entries, as well as padding the entry headers. We do a preliminary signing
1482 # (with an incomplete metadata entry) to allow that to happen. Then compute
1483 # the ZIP entry offsets, write back the final metadata and do the final
1484 # signing.
1485 prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files)
1486 try:
1487 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
1488 except PropertyFiles.InsufficientSpaceException:
1489 # Even with the preliminary signing, the entry orders may change
1490 # dramatically, which leads to insufficiently reserved space during the
1491 # first call to ComputeAllPropertyFiles(). In that case, we redo all the
1492 # preliminary signing works, based on the already ordered ZIP entries, to
1493 # address the issue.
1494 prelim_signing = ComputeAllPropertyFiles(
1495 prelim_signing, needed_property_files)
1496 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
Tao Baofe5b69a2018-03-02 09:47:43 -08001497
1498 # Replace the METADATA entry.
1499 common.ZipDelete(prelim_signing, METADATA_NAME)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001500 output_zip = zipfile.ZipFile(prelim_signing, 'a')
Tao Baofe5b69a2018-03-02 09:47:43 -08001501 WriteMetadata(metadata, output_zip)
1502 common.ZipClose(output_zip)
1503
1504 # Re-sign the package after updating the metadata entry.
Tao Bao491d7e22018-02-21 13:17:22 -08001505 if OPTIONS.no_signing:
1506 output_file = prelim_signing
1507 else:
1508 SignOutput(prelim_signing, output_file)
Tao Baofe5b69a2018-03-02 09:47:43 -08001509
1510 # Reopen the final signed zip to double check the streaming metadata.
Tao Baod2ce2ed2018-03-16 12:59:42 -07001511 with zipfile.ZipFile(output_file) as output_zip:
Tao Baod3fc38a2018-03-08 16:09:01 -08001512 for property_files in needed_property_files:
1513 property_files.Verify(output_zip, metadata[property_files.name].strip())
Tao Baofe5b69a2018-03-02 09:47:43 -08001514
xunchang1cfe2512019-02-19 14:14:48 -08001515 # If requested, dump the metadata to a separate file.
1516 output_metadata_path = OPTIONS.output_metadata_path
1517 if output_metadata_path:
1518 WriteMetadata(metadata, output_metadata_path)
1519
Tao Baofe5b69a2018-03-02 09:47:43 -08001520
Tao Bao491d7e22018-02-21 13:17:22 -08001521def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -08001522 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1523 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -08001524
Tao Bao481bab82017-12-21 11:23:09 -08001525 target_api_version = target_info["recovery_api_version"]
1526 source_api_version = source_info["recovery_api_version"]
1527 if source_api_version == 0:
Tao Bao32fcdab2018-10-12 10:30:39 -07001528 logger.warning(
1529 "Generating edify script for a source that can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001530
Tao Bao481bab82017-12-21 11:23:09 -08001531 script = edify_generator.EdifyGenerator(
1532 source_api_version, target_info, fstab=source_info["fstab"])
1533
1534 if target_info.oem_props or source_info.oem_props:
1535 if not OPTIONS.oem_no_mount:
1536 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -07001537
Tao Baodf3a48b2018-01-10 16:30:43 -08001538 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -08001539
Tao Bao491d7e22018-02-21 13:17:22 -08001540 if not OPTIONS.no_signing:
1541 staging_file = common.MakeTempFile(suffix='.zip')
1542 else:
1543 staging_file = output_file
1544
1545 output_zip = zipfile.ZipFile(
1546 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
1547
Geremy Condra36bd3652014-02-06 19:45:10 -08001548 device_specific = common.DeviceSpecificParams(
1549 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001550 source_version=source_api_version,
Yifan Hong8a66a712019-04-04 15:37:57 -07001551 source_tmp=OPTIONS.source_tmp,
Geremy Condra36bd3652014-02-06 19:45:10 -08001552 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001553 target_version=target_api_version,
Yifan Hong8a66a712019-04-04 15:37:57 -07001554 target_tmp=OPTIONS.target_tmp,
Geremy Condra36bd3652014-02-06 19:45:10 -08001555 output_zip=output_zip,
1556 script=script,
1557 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -08001558 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001559
Geremy Condra36bd3652014-02-06 19:45:10 -08001560 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001561 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001562 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001563 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001564 updating_boot = (not OPTIONS.two_step and
1565 (source_boot.data != target_boot.data))
1566
Geremy Condra36bd3652014-02-06 19:45:10 -08001567 target_recovery = common.GetBootableImage(
1568 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -08001569
Tianjie Xuf67dd802019-05-20 17:50:36 -07001570 block_diff_dict = GetBlockDifferences(target_zip=target_zip,
1571 source_zip=source_zip,
1572 target_info=target_info,
1573 source_info=source_info,
1574 device_specific=device_specific)
Geremy Condra36bd3652014-02-06 19:45:10 -08001575
Yifan Hong9276cf02019-08-21 16:37:04 -07001576 CheckVintfIfTrebleEnabled(OPTIONS.target_tmp, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001577
Tao Bao481bab82017-12-21 11:23:09 -08001578 # Assertions (e.g. device properties check).
1579 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -08001580 device_specific.IncrementalOTA_Assertions()
1581
1582 # Two-step incremental package strategy (in chronological order,
1583 # which is *not* the order in which the generated script has
1584 # things):
1585 #
1586 # if stage is not "2/3" or "3/3":
1587 # do verification on current system
1588 # write recovery image to boot partition
1589 # set stage to "2/3"
1590 # reboot to boot partition and restart recovery
1591 # else if stage is "2/3":
1592 # write recovery image to recovery partition
1593 # set stage to "3/3"
1594 # reboot to recovery partition and restart recovery
1595 # else:
1596 # (stage must be "3/3")
1597 # perform update:
1598 # patch system files, etc.
1599 # force full install of new boot image
1600 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001601 # complete script normally
1602 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001603
1604 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001605 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001606 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001607 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001608 assert fs.fs_type.upper() == "EMMC", \
1609 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001610 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001611 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1612 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001613if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001614""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001615
1616 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1617 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001618 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001619 script.WriteRawImage("/recovery", "recovery.img")
1620 script.AppendExtra("""
1621set_stage("%(bcb_dev)s", "3/3");
1622reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001623else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001624""" % bcb_dev)
1625
Tao Baod42e97e2016-11-30 12:11:57 -08001626 # Stage 1/3: (a) Verify the current system.
1627 script.Comment("Stage 1/3")
1628
Tao Bao6c55a8a2015-04-08 15:30:27 -07001629 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001630 script.Print("Source: {}".format(source_info.fingerprint))
1631 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001632
Geremy Condra36bd3652014-02-06 19:45:10 -08001633 script.Print("Verifying current system...")
1634
1635 device_specific.IncrementalOTA_VerifyBegin()
1636
Tao Bao481bab82017-12-21 11:23:09 -08001637 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001638
Tao Baod8d14be2016-02-04 14:26:02 -08001639 # Check the required cache size (i.e. stashed blocks).
Tianjie Xuf67dd802019-05-20 17:50:36 -07001640 required_cache_sizes = [diff.required_cache for diff in
1641 block_diff_dict.values()]
Geremy Condra36bd3652014-02-06 19:45:10 -08001642 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -08001643 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001644 d = common.Difference(target_boot, source_boot)
1645 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001646 if d is None:
1647 include_full_boot = True
1648 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1649 else:
1650 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001651
Tao Bao32fcdab2018-10-12 10:30:39 -07001652 logger.info(
1653 "boot target: %d source: %d diff: %d", target_boot.size,
1654 source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -08001655
Tao Bao51216552018-08-26 11:53:15 -07001656 common.ZipWriteStr(output_zip, "boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001657
Tao Bao51216552018-08-26 11:53:15 -07001658 script.PatchPartitionCheck(
1659 "{}:{}:{}:{}".format(
1660 boot_type, boot_device, target_boot.size, target_boot.sha1),
1661 "{}:{}:{}:{}".format(
1662 boot_type, boot_device, source_boot.size, source_boot.sha1))
1663
Tianjie Xuf67dd802019-05-20 17:50:36 -07001664 required_cache_sizes.append(target_boot.size)
Tao Baod8d14be2016-02-04 14:26:02 -08001665
Tianjie Xuf67dd802019-05-20 17:50:36 -07001666 if required_cache_sizes:
1667 script.CacheFreeSpaceCheck(max(required_cache_sizes))
1668
1669 # Verify the existing partitions.
1670 for diff in block_diff_dict.values():
1671 diff.WriteVerifyScript(script, touched_blocks_only=True)
Geremy Condra36bd3652014-02-06 19:45:10 -08001672
1673 device_specific.IncrementalOTA_VerifyEnd()
1674
1675 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001676 # Stage 1/3: (b) Write recovery image to /boot.
1677 _WriteRecoveryImageToBoot(script, output_zip)
1678
Geremy Condra36bd3652014-02-06 19:45:10 -08001679 script.AppendExtra("""
1680set_stage("%(bcb_dev)s", "2/3");
1681reboot_now("%(bcb_dev)s", "");
1682else
1683""" % bcb_dev)
1684
Tao Baod42e97e2016-11-30 12:11:57 -08001685 # Stage 3/3: Make changes.
1686 script.Comment("Stage 3/3")
1687
Geremy Condra36bd3652014-02-06 19:45:10 -08001688 script.Comment("---- start making changes here ----")
1689
1690 device_specific.IncrementalOTA_InstallBegin()
1691
Tianjie Xuf67dd802019-05-20 17:50:36 -07001692 progress_dict = {partition: 0.1 for partition in block_diff_dict}
1693 progress_dict["system"] = 1 - len(block_diff_dict) * 0.1
Yifan Hong10c530d2018-12-27 17:34:18 -08001694
1695 if OPTIONS.source_info_dict.get("use_dynamic_partitions") == "true":
1696 if OPTIONS.target_info_dict.get("use_dynamic_partitions") != "true":
1697 raise RuntimeError(
1698 "can't generate incremental that disables dynamic partitions")
1699 dynamic_partitions_diff = common.DynamicPartitionsDifference(
1700 info_dict=OPTIONS.target_info_dict,
1701 source_info_dict=OPTIONS.source_info_dict,
Tianjie Xuf67dd802019-05-20 17:50:36 -07001702 block_diffs=block_diff_dict.values(),
Yifan Hong10c530d2018-12-27 17:34:18 -08001703 progress_dict=progress_dict)
1704 dynamic_partitions_diff.WriteScript(
1705 script, output_zip, write_verify_script=OPTIONS.verify)
1706 else:
Tianjie Xuf67dd802019-05-20 17:50:36 -07001707 for block_diff in block_diff_dict.values():
Yifan Hong10c530d2018-12-27 17:34:18 -08001708 block_diff.WriteScript(script, output_zip,
1709 progress=progress_dict.get(block_diff.partition),
1710 write_verify_script=OPTIONS.verify)
Geremy Condra36bd3652014-02-06 19:45:10 -08001711
1712 if OPTIONS.two_step:
1713 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1714 script.WriteRawImage("/boot", "boot.img")
Tao Bao32fcdab2018-10-12 10:30:39 -07001715 logger.info("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001716
1717 if not OPTIONS.two_step:
1718 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001719 if include_full_boot:
Tao Bao32fcdab2018-10-12 10:30:39 -07001720 logger.info("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001721 script.Print("Installing boot image...")
1722 script.WriteRawImage("/boot", "boot.img")
1723 else:
1724 # Produce the boot image by applying a patch to the current
1725 # contents of the boot partition, and write it back to the
1726 # partition.
Tao Bao32fcdab2018-10-12 10:30:39 -07001727 logger.info("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001728 script.Print("Patching boot image...")
1729 script.ShowProgress(0.1, 10)
Tao Bao51216552018-08-26 11:53:15 -07001730 script.PatchPartition(
1731 '{}:{}:{}:{}'.format(
1732 boot_type, boot_device, target_boot.size, target_boot.sha1),
1733 '{}:{}:{}:{}'.format(
1734 boot_type, boot_device, source_boot.size, source_boot.sha1),
1735 'boot.img.p')
Geremy Condra36bd3652014-02-06 19:45:10 -08001736 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001737 logger.info("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001738
1739 # Do device-specific installation (eg, write radio image).
1740 device_specific.IncrementalOTA_InstallEnd()
1741
1742 if OPTIONS.extra_script is not None:
1743 script.AppendExtra(OPTIONS.extra_script)
1744
Doug Zongker922206e2014-03-04 13:16:24 -08001745 if OPTIONS.wipe_user_data:
1746 script.Print("Erasing user data...")
1747 script.FormatPartition("/data")
1748
Geremy Condra36bd3652014-02-06 19:45:10 -08001749 if OPTIONS.two_step:
1750 script.AppendExtra("""
1751set_stage("%(bcb_dev)s", "");
1752endif;
1753endif;
1754""" % bcb_dev)
1755
1756 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001757 # For downgrade OTAs, we prefer to use the update-binary in the source
1758 # build that is actually newer than the one in the target build.
1759 if OPTIONS.downgrade:
1760 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1761 else:
1762 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001763 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001764
1765 # We haven't written the metadata entry yet, which will be handled in
1766 # FinalizeMetadata().
1767 common.ZipClose(output_zip)
1768
1769 # Sign the generated zip package unless no_signing is specified.
1770 needed_property_files = (
1771 NonAbOtaPropertyFiles(),
1772 )
1773 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Geremy Condra36bd3652014-02-06 19:45:10 -08001774
Doug Zongker32b527d2014-03-04 10:03:02 -08001775
Tao Bao15a146a2018-02-21 16:06:59 -08001776def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
Tao Baof7140c02018-01-30 17:09:24 -08001777 """Returns a target-files.zip file for generating secondary payload.
1778
1779 Although the original target-files.zip already contains secondary slot
1780 images (i.e. IMAGES/system_other.img), we need to rename the files to the
1781 ones without _other suffix. Note that we cannot instead modify the names in
1782 META/ab_partitions.txt, because there are no matching partitions on device.
1783
1784 For the partitions that don't have secondary images, the ones for primary
1785 slot will be used. This is to ensure that we always have valid boot, vbmeta,
1786 bootloader images in the inactive slot.
1787
1788 Args:
1789 input_file: The input target-files.zip file.
Tao Bao15a146a2018-02-21 16:06:59 -08001790 skip_postinstall: Whether to skip copying the postinstall config file.
Tao Baof7140c02018-01-30 17:09:24 -08001791
1792 Returns:
1793 The filename of the target-files.zip for generating secondary payload.
1794 """
1795 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1796 target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
1797
Tao Baodba59ee2018-01-09 13:21:02 -08001798 with zipfile.ZipFile(input_file, 'r') as input_zip:
1799 infolist = input_zip.infolist()
Tao Bao12489802018-07-12 14:47:38 -07001800
Tao Bao0ff15de2019-03-20 11:26:06 -07001801 input_tmp = common.UnzipTemp(input_file, UNZIP_PATTERN)
Tao Baodba59ee2018-01-09 13:21:02 -08001802 for info in infolist:
Tao Baof7140c02018-01-30 17:09:24 -08001803 unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
1804 if info.filename == 'IMAGES/system_other.img':
1805 common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
1806
1807 # Primary images and friends need to be skipped explicitly.
1808 elif info.filename in ('IMAGES/system.img',
1809 'IMAGES/system.map'):
1810 pass
1811
Tao Bao15a146a2018-02-21 16:06:59 -08001812 # Skip copying the postinstall config if requested.
1813 elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
1814 pass
1815
Tao Bao12489802018-07-12 14:47:38 -07001816 elif info.filename.startswith(('META/', 'IMAGES/', 'RADIO/')):
Tao Baof7140c02018-01-30 17:09:24 -08001817 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
1818
Tao Baof7140c02018-01-30 17:09:24 -08001819 common.ZipClose(target_zip)
1820
1821 return target_file
1822
1823
Tao Bao15a146a2018-02-21 16:06:59 -08001824def GetTargetFilesZipWithoutPostinstallConfig(input_file):
1825 """Returns a target-files.zip that's not containing postinstall_config.txt.
1826
1827 This allows brillo_update_payload script to skip writing all the postinstall
1828 hooks in the generated payload. The input target-files.zip file will be
1829 duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
1830 contain the postinstall_config.txt entry, the input file will be returned.
1831
1832 Args:
1833 input_file: The input target-files.zip filename.
1834
1835 Returns:
1836 The filename of target-files.zip that doesn't contain postinstall config.
1837 """
1838 # We should only make a copy if postinstall_config entry exists.
1839 with zipfile.ZipFile(input_file, 'r') as input_zip:
1840 if POSTINSTALL_CONFIG not in input_zip.namelist():
1841 return input_file
1842
1843 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1844 shutil.copyfile(input_file, target_file)
1845 common.ZipDelete(target_file, POSTINSTALL_CONFIG)
1846 return target_file
1847
1848
Yifan Hong50e79542018-11-08 17:44:12 -08001849def GetTargetFilesZipForRetrofitDynamicPartitions(input_file,
Yifan Hongb433eba2019-03-06 12:42:53 -08001850 super_block_devices,
1851 dynamic_partition_list):
Yifan Hong50e79542018-11-08 17:44:12 -08001852 """Returns a target-files.zip for retrofitting dynamic partitions.
1853
1854 This allows brillo_update_payload to generate an OTA based on the exact
1855 bits on the block devices. Postinstall is disabled.
1856
1857 Args:
1858 input_file: The input target-files.zip filename.
1859 super_block_devices: The list of super block devices
Yifan Hongb433eba2019-03-06 12:42:53 -08001860 dynamic_partition_list: The list of dynamic partitions
Yifan Hong50e79542018-11-08 17:44:12 -08001861
1862 Returns:
1863 The filename of target-files.zip with *.img replaced with super_*.img for
1864 each block device in super_block_devices.
1865 """
1866 assert super_block_devices, "No super_block_devices are specified."
1867
1868 replace = {'OTA/super_{}.img'.format(dev): 'IMAGES/{}.img'.format(dev)
Tao Bao03fecb62018-11-28 10:59:23 -08001869 for dev in super_block_devices}
Yifan Hong50e79542018-11-08 17:44:12 -08001870
1871 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1872 shutil.copyfile(input_file, target_file)
1873
Tao Baoa3705452019-06-24 15:33:41 -07001874 with zipfile.ZipFile(input_file) as input_zip:
Yifan Hong50e79542018-11-08 17:44:12 -08001875 namelist = input_zip.namelist()
1876
Yifan Hongb433eba2019-03-06 12:42:53 -08001877 input_tmp = common.UnzipTemp(input_file, RETROFIT_DAP_UNZIP_PATTERN)
1878
1879 # Remove partitions from META/ab_partitions.txt that is in
1880 # dynamic_partition_list but not in super_block_devices so that
1881 # brillo_update_payload won't generate update for those logical partitions.
1882 ab_partitions_file = os.path.join(input_tmp, *AB_PARTITIONS.split('/'))
1883 with open(ab_partitions_file) as f:
1884 ab_partitions_lines = f.readlines()
1885 ab_partitions = [line.strip() for line in ab_partitions_lines]
1886 # Assert that all super_block_devices are in ab_partitions
1887 super_device_not_updated = [partition for partition in super_block_devices
1888 if partition not in ab_partitions]
1889 assert not super_device_not_updated, \
1890 "{} is in super_block_devices but not in {}".format(
1891 super_device_not_updated, AB_PARTITIONS)
1892 # ab_partitions -= (dynamic_partition_list - super_block_devices)
1893 new_ab_partitions = common.MakeTempFile(prefix="ab_partitions", suffix=".txt")
1894 with open(new_ab_partitions, 'w') as f:
1895 for partition in ab_partitions:
1896 if (partition in dynamic_partition_list and
1897 partition not in super_block_devices):
Tao Bao59cf0c52019-06-25 10:04:24 -07001898 logger.info("Dropping %s from ab_partitions.txt", partition)
1899 continue
Yifan Hongb433eba2019-03-06 12:42:53 -08001900 f.write(partition + "\n")
1901 to_delete = [AB_PARTITIONS]
1902
Yifan Hong50e79542018-11-08 17:44:12 -08001903 # Always skip postinstall for a retrofit update.
Yifan Hongb433eba2019-03-06 12:42:53 -08001904 to_delete += [POSTINSTALL_CONFIG]
Yifan Hong50e79542018-11-08 17:44:12 -08001905
1906 # Delete dynamic_partitions_info.txt so that brillo_update_payload thinks this
1907 # is a regular update on devices without dynamic partitions support.
1908 to_delete += [DYNAMIC_PARTITION_INFO]
1909
Tao Bao03fecb62018-11-28 10:59:23 -08001910 # Remove the existing partition images as well as the map files.
Tao Bao59cf0c52019-06-25 10:04:24 -07001911 to_delete += list(replace.values())
Tao Bao03fecb62018-11-28 10:59:23 -08001912 to_delete += ['IMAGES/{}.map'.format(dev) for dev in super_block_devices]
Yifan Hong50e79542018-11-08 17:44:12 -08001913
1914 common.ZipDelete(target_file, to_delete)
1915
Yifan Hong50e79542018-11-08 17:44:12 -08001916 target_zip = zipfile.ZipFile(target_file, 'a', allowZip64=True)
1917
1918 # Write super_{foo}.img as {foo}.img.
1919 for src, dst in replace.items():
1920 assert src in namelist, \
Tao Bao59cf0c52019-06-25 10:04:24 -07001921 'Missing {} in {}; {} cannot be written'.format(src, input_file, dst)
Yifan Hong50e79542018-11-08 17:44:12 -08001922 unzipped_file = os.path.join(input_tmp, *src.split('/'))
1923 common.ZipWrite(target_zip, unzipped_file, arcname=dst)
1924
Yifan Hongb433eba2019-03-06 12:42:53 -08001925 # Write new ab_partitions.txt file
1926 common.ZipWrite(target_zip, new_ab_partitions, arcname=AB_PARTITIONS)
1927
Yifan Hong50e79542018-11-08 17:44:12 -08001928 common.ZipClose(target_zip)
1929
1930 return target_file
1931
1932
Tao Baof0c4aa22018-04-30 20:29:30 -07001933def GenerateAbOtaPackage(target_file, output_file, source_file=None):
Tao Baofe5b69a2018-03-02 09:47:43 -08001934 """Generates an Android OTA package that has A/B update payload."""
Tao Baodea0f8b2016-06-20 17:55:06 -07001935 # Stage the output zip package for package signing.
Tao Bao491d7e22018-02-21 13:17:22 -08001936 if not OPTIONS.no_signing:
1937 staging_file = common.MakeTempFile(suffix='.zip')
1938 else:
1939 staging_file = output_file
Tao Baoa652c002018-03-01 19:31:38 -08001940 output_zip = zipfile.ZipFile(staging_file, "w",
Tao Baoc098e9e2016-01-07 13:03:56 -08001941 compression=zipfile.ZIP_DEFLATED)
1942
Tao Bao481bab82017-12-21 11:23:09 -08001943 if source_file is not None:
1944 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1945 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
1946 else:
1947 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
1948 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001949
Tao Bao481bab82017-12-21 11:23:09 -08001950 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001951 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001952
Yifan Hong50e79542018-11-08 17:44:12 -08001953 if OPTIONS.retrofit_dynamic_partitions:
1954 target_file = GetTargetFilesZipForRetrofitDynamicPartitions(
Yifan Hongb433eba2019-03-06 12:42:53 -08001955 target_file, target_info.get("super_block_devices").strip().split(),
1956 target_info.get("dynamic_partition_list").strip().split())
Yifan Hong50e79542018-11-08 17:44:12 -08001957 elif OPTIONS.skip_postinstall:
Tao Bao15a146a2018-02-21 16:06:59 -08001958 target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
1959
Tao Bao40b18822018-01-30 18:19:04 -08001960 # Generate payload.
1961 payload = Payload()
1962
1963 # Enforce a max timestamp this payload can be applied on top of.
Tao Baoff1b86e2017-10-03 14:17:57 -07001964 if OPTIONS.downgrade:
Tao Bao2a12ed72018-01-22 11:35:00 -08001965 max_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baoff1b86e2017-10-03 14:17:57 -07001966 else:
1967 max_timestamp = metadata["post-timestamp"]
Tao Bao40b18822018-01-30 18:19:04 -08001968 additional_args = ["--max_timestamp", max_timestamp]
Tao Baoc098e9e2016-01-07 13:03:56 -08001969
Tao Bao40b18822018-01-30 18:19:04 -08001970 payload.Generate(target_file, source_file, additional_args)
Tao Baoc098e9e2016-01-07 13:03:56 -08001971
Tao Bao40b18822018-01-30 18:19:04 -08001972 # Sign the payload.
Tao Baof7140c02018-01-30 17:09:24 -08001973 payload_signer = PayloadSigner()
1974 payload.Sign(payload_signer)
Tao Baoc098e9e2016-01-07 13:03:56 -08001975
Tao Bao40b18822018-01-30 18:19:04 -08001976 # Write the payload into output zip.
1977 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001978
Tao Baof7140c02018-01-30 17:09:24 -08001979 # Generate and include the secondary payload that installs secondary images
1980 # (e.g. system_other.img).
1981 if OPTIONS.include_secondary:
1982 # We always include a full payload for the secondary slot, even when
1983 # building an incremental OTA. See the comments for "--include_secondary".
Tao Bao15a146a2018-02-21 16:06:59 -08001984 secondary_target_file = GetTargetFilesZipForSecondaryImages(
1985 target_file, OPTIONS.skip_postinstall)
Tao Bao667ff572018-02-10 00:02:40 -08001986 secondary_payload = Payload(secondary=True)
Tao Baodb1fe412018-02-09 23:15:05 -08001987 secondary_payload.Generate(secondary_target_file,
1988 additional_args=additional_args)
Tao Baof7140c02018-01-30 17:09:24 -08001989 secondary_payload.Sign(payload_signer)
Tao Bao667ff572018-02-10 00:02:40 -08001990 secondary_payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08001991
Tianjie Xucfa86222016-03-07 16:31:19 -08001992 # If dm-verity is supported for the device, copy contents of care_map
1993 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001994 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08001995 if (target_info.get("verity") == "true" or
1996 target_info.get("avb_enable") == "true"):
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001997 care_map_list = [x for x in ["care_map.pb", "care_map.txt"] if
1998 "META/" + x in target_zip.namelist()]
1999
2000 # Adds care_map if either the protobuf format or the plain text one exists.
2001 if care_map_list:
2002 care_map_name = care_map_list[0]
2003 care_map_data = target_zip.read("META/" + care_map_name)
2004 # In order to support streaming, care_map needs to be packed as
Tao Bao40b18822018-01-30 18:19:04 -08002005 # ZIP_STORED.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07002006 common.ZipWriteStr(output_zip, care_map_name, care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08002007 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08002008 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002009 logger.warning("Cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07002010
Tao Bao21803d32017-04-19 10:16:09 -07002011 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08002012
Yifan Hong9276cf02019-08-21 16:37:04 -07002013 CheckVintfIfTrebleEnabled(target_file, target_info)
2014
Tao Baofe5b69a2018-03-02 09:47:43 -08002015 # We haven't written the metadata entry yet, which will be handled in
2016 # FinalizeMetadata().
Tao Baoc96316c2017-01-24 22:10:49 -08002017 common.ZipClose(output_zip)
2018
Tao Bao85f16982018-03-08 16:28:33 -08002019 # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers
2020 # all the info of the latter. However, system updaters and OTA servers need to
2021 # take time to switch to the new flag. We keep both of the flags for
2022 # P-timeframe, and will remove StreamingPropertyFiles in later release.
Tao Baod3fc38a2018-03-08 16:09:01 -08002023 needed_property_files = (
Tao Bao85f16982018-03-08 16:28:33 -08002024 AbOtaPropertyFiles(),
Tao Baod3fc38a2018-03-08 16:09:01 -08002025 StreamingPropertyFiles(),
2026 )
2027 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Tao Baoc96316c2017-01-24 22:10:49 -08002028
Tao Baoc098e9e2016-01-07 13:03:56 -08002029
Tao Baof0c4aa22018-04-30 20:29:30 -07002030def GenerateNonAbOtaPackage(target_file, output_file, source_file=None):
2031 """Generates a non-A/B OTA package."""
2032 # Sanity check the loaded info dicts first.
2033 if OPTIONS.info_dict.get("no_recovery") == "true":
2034 raise common.ExternalError(
2035 "--- target build has specified no recovery ---")
2036
2037 # Non-A/B OTAs rely on /cache partition to store temporary files.
2038 cache_size = OPTIONS.info_dict.get("cache_size")
2039 if cache_size is None:
2040 logger.warning("--- can't determine the cache partition size ---")
2041 OPTIONS.cache_size = cache_size
2042
2043 if OPTIONS.extra_script is not None:
2044 with open(OPTIONS.extra_script) as fp:
2045 OPTIONS.extra_script = fp.read()
2046
2047 if OPTIONS.extracted_input is not None:
2048 OPTIONS.input_tmp = OPTIONS.extracted_input
2049 else:
2050 logger.info("unzipping target target-files...")
2051 OPTIONS.input_tmp = common.UnzipTemp(target_file, UNZIP_PATTERN)
2052 OPTIONS.target_tmp = OPTIONS.input_tmp
2053
2054 # If the caller explicitly specified the device-specific extensions path via
2055 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
2056 # is present in the target target_files. Otherwise, take the path of the file
2057 # from 'tool_extensions' in the info dict and look for that in the local
2058 # filesystem, relative to the current directory.
2059 if OPTIONS.device_specific is None:
2060 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2061 if os.path.exists(from_input):
2062 logger.info("(using device-specific extensions from target_files)")
2063 OPTIONS.device_specific = from_input
2064 else:
2065 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
2066
2067 if OPTIONS.device_specific is not None:
2068 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
2069
2070 # Generate a full OTA.
2071 if source_file is None:
2072 with zipfile.ZipFile(target_file) as input_zip:
2073 WriteFullOTAPackage(
2074 input_zip,
2075 output_file)
2076
2077 # Generate an incremental OTA.
2078 else:
2079 logger.info("unzipping source target-files...")
2080 OPTIONS.source_tmp = common.UnzipTemp(
2081 OPTIONS.incremental_source, UNZIP_PATTERN)
2082 with zipfile.ZipFile(target_file) as input_zip, \
2083 zipfile.ZipFile(source_file) as source_zip:
2084 WriteBlockIncrementalOTAPackage(
2085 input_zip,
2086 source_zip,
2087 output_file)
2088
2089
Doug Zongkereef39442009-04-02 12:14:19 -07002090def main(argv):
2091
2092 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07002093 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07002094 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07002095 elif o in ("-i", "--incremental_from"):
2096 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07002097 elif o == "--full_radio":
2098 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07002099 elif o == "--full_bootloader":
2100 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08002101 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002102 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08002103 elif o == "--downgrade":
2104 OPTIONS.downgrade = True
2105 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08002106 elif o == "--override_timestamp":
Tao Baofaa8e0b2018-04-12 14:31:43 -07002107 OPTIONS.downgrade = True
Michael Runge6e836112014-04-15 17:40:21 -07002108 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08002109 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08002110 elif o == "--oem_no_mount":
2111 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07002112 elif o in ("-e", "--extra_script"):
2113 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02002114 elif o in ("-t", "--worker_threads"):
2115 if a.isdigit():
2116 OPTIONS.worker_threads = int(a)
2117 else:
2118 raise ValueError("Cannot parse value %r for option %r - only "
2119 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002120 elif o in ("-2", "--two_step"):
2121 OPTIONS.two_step = True
Tao Baof7140c02018-01-30 17:09:24 -08002122 elif o == "--include_secondary":
2123 OPTIONS.include_secondary = True
Doug Zongker26e66192014-02-20 13:22:07 -08002124 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002125 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07002126 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07002127 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08002128 elif o == "--block":
2129 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002130 elif o in ("-b", "--binary"):
2131 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07002132 elif o == "--stash_threshold":
2133 try:
2134 OPTIONS.stash_threshold = float(a)
2135 except ValueError:
2136 raise ValueError("Cannot parse value %r for option %r - expecting "
2137 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08002138 elif o == "--log_diff":
2139 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002140 elif o == "--payload_signer":
2141 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002142 elif o == "--payload_signer_args":
2143 OPTIONS.payload_signer_args = shlex.split(a)
xunchang376cc7c2019-04-08 23:04:58 -07002144 elif o == "--payload_signer_key_size":
2145 OPTIONS.payload_signer_key_size = a
Dan Willemsencea5cd22017-03-21 14:44:27 -07002146 elif o == "--extracted_input_target_files":
2147 OPTIONS.extracted_input = a
Tao Bao15a146a2018-02-21 16:06:59 -08002148 elif o == "--skip_postinstall":
2149 OPTIONS.skip_postinstall = True
Yifan Hong50e79542018-11-08 17:44:12 -08002150 elif o == "--retrofit_dynamic_partitions":
2151 OPTIONS.retrofit_dynamic_partitions = True
xunchangabfa2652019-02-19 16:27:10 -08002152 elif o == "--skip_compatibility_check":
2153 OPTIONS.skip_compatibility_check = True
xunchang1cfe2512019-02-19 14:14:48 -08002154 elif o == "--output_metadata_path":
2155 OPTIONS.output_metadata_path = a
Tianjie Xu1b079832019-08-28 12:19:23 -07002156 elif o == "--disable_fec_computation":
2157 OPTIONS.disable_fec_computation = True
Doug Zongkereef39442009-04-02 12:14:19 -07002158 else:
2159 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002160 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002161
2162 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08002163 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002164 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07002165 "package_key=",
2166 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002167 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002168 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002169 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08002170 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08002171 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07002172 "extra_script=",
2173 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002174 "two_step",
Tao Baof7140c02018-01-30 17:09:24 -08002175 "include_secondary",
Dan Albert8b72aef2015-03-23 19:13:21 -07002176 "no_signing",
2177 "block",
2178 "binary=",
2179 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08002180 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002181 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07002182 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002183 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002184 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002185 "payload_signer_args=",
xunchang376cc7c2019-04-08 23:04:58 -07002186 "payload_signer_key_size=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07002187 "extracted_input_target_files=",
Tao Bao15a146a2018-02-21 16:06:59 -08002188 "skip_postinstall",
Yifan Hong50e79542018-11-08 17:44:12 -08002189 "retrofit_dynamic_partitions",
xunchangabfa2652019-02-19 16:27:10 -08002190 "skip_compatibility_check",
xunchang1cfe2512019-02-19 14:14:48 -08002191 "output_metadata_path=",
Tianjie Xu1b079832019-08-28 12:19:23 -07002192 "disable_fec_computation",
Dan Albert8b72aef2015-03-23 19:13:21 -07002193 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002194
2195 if len(args) != 2:
2196 common.Usage(__doc__)
2197 sys.exit(1)
2198
Tao Bao32fcdab2018-10-12 10:30:39 -07002199 common.InitLogging()
2200
Tao Bao5d182562016-02-23 11:38:39 -08002201 if OPTIONS.downgrade:
Tao Bao5d182562016-02-23 11:38:39 -08002202 # We should only allow downgrading incrementals (as opposed to full).
2203 # Otherwise the device may go back from arbitrary build with this full
2204 # OTA package.
2205 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002206 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08002207
Tao Bao2db13852018-01-08 22:28:57 -08002208 # Load the build info dicts from the zip directly or the extracted input
2209 # directory. We don't need to unzip the entire target-files zips, because they
2210 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
2211 # When loading the info dicts, we don't need to provide the second parameter
2212 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
2213 # some properties with their actual paths, such as 'selinux_fc',
2214 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07002215 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08002216 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07002217 else:
Tao Bao2db13852018-01-08 22:28:57 -08002218 with zipfile.ZipFile(args[0], 'r') as input_zip:
2219 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08002220
Tao Bao32fcdab2018-10-12 10:30:39 -07002221 logger.info("--- target info ---")
2222 common.DumpInfoDict(OPTIONS.info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002223
2224 # Load the source build dict if applicable.
2225 if OPTIONS.incremental_source is not None:
2226 OPTIONS.target_info_dict = OPTIONS.info_dict
2227 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
2228 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2229
Tao Bao32fcdab2018-10-12 10:30:39 -07002230 logger.info("--- source info ---")
2231 common.DumpInfoDict(OPTIONS.source_info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002232
2233 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08002234 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
2235
Yifan Hong50e79542018-11-08 17:44:12 -08002236 # Assume retrofitting dynamic partitions when base build does not set
Yifan Hong50611032018-11-20 14:27:38 -08002237 # use_dynamic_partitions but target build does.
Yifan Hong50e79542018-11-08 17:44:12 -08002238 if (OPTIONS.source_info_dict and
Yifan Hong50611032018-11-20 14:27:38 -08002239 OPTIONS.source_info_dict.get("use_dynamic_partitions") != "true" and
2240 OPTIONS.target_info_dict.get("use_dynamic_partitions") == "true"):
Yifan Hong50e79542018-11-08 17:44:12 -08002241 if OPTIONS.target_info_dict.get("dynamic_partition_retrofit") != "true":
2242 raise common.ExternalError(
2243 "Expect to generate incremental OTA for retrofitting dynamic "
2244 "partitions, but dynamic_partition_retrofit is not set in target "
2245 "build.")
2246 logger.info("Implicitly generating retrofit incremental OTA.")
2247 OPTIONS.retrofit_dynamic_partitions = True
2248
2249 # Skip postinstall for retrofitting dynamic partitions.
2250 if OPTIONS.retrofit_dynamic_partitions:
2251 OPTIONS.skip_postinstall = True
2252
Tao Baoc098e9e2016-01-07 13:03:56 -08002253 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2254
Christian Oderf63e2cd2017-05-01 22:30:15 +02002255 # Use the default key to sign the package if not specified with package_key.
2256 # package_keys are needed on ab_updates, so always define them if an
2257 # ab_update is getting created.
2258 if not OPTIONS.no_signing or ab_update:
2259 if OPTIONS.package_key is None:
2260 OPTIONS.package_key = OPTIONS.info_dict.get(
2261 "default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -07002262 "build/make/target/product/security/testkey")
Christian Oderf63e2cd2017-05-01 22:30:15 +02002263 # Get signing keys
2264 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
2265
Tao Baoc098e9e2016-01-07 13:03:56 -08002266 if ab_update:
Tao Baof0c4aa22018-04-30 20:29:30 -07002267 GenerateAbOtaPackage(
Tao Baoc098e9e2016-01-07 13:03:56 -08002268 target_file=args[0],
2269 output_file=args[1],
2270 source_file=OPTIONS.incremental_source)
2271
Dan Willemsencea5cd22017-03-21 14:44:27 -07002272 else:
Tao Baof0c4aa22018-04-30 20:29:30 -07002273 GenerateNonAbOtaPackage(
2274 target_file=args[0],
2275 output_file=args[1],
2276 source_file=OPTIONS.incremental_source)
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002277
Tao Baof0c4aa22018-04-30 20:29:30 -07002278 # Post OTA generation works.
2279 if OPTIONS.incremental_source is not None and OPTIONS.log_diff:
2280 logger.info("Generating diff logs...")
2281 logger.info("Unzipping target-files for diffing...")
2282 target_dir = common.UnzipTemp(args[0], TARGET_DIFFING_UNZIP_PATTERN)
2283 source_dir = common.UnzipTemp(
2284 OPTIONS.incremental_source, TARGET_DIFFING_UNZIP_PATTERN)
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002285
Tao Baof0c4aa22018-04-30 20:29:30 -07002286 with open(OPTIONS.log_diff, 'w') as out_file:
2287 import target_files_diff
2288 target_files_diff.recursiveDiff(
2289 '', source_dir, target_dir, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07002290
Tao Bao32fcdab2018-10-12 10:30:39 -07002291 logger.info("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002292
2293
2294if __name__ == '__main__':
2295 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002296 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002297 main(sys.argv[1:])
Tao Bao32fcdab2018-10-12 10:30:39 -07002298 except common.ExternalError:
2299 logger.exception("\n ERROR:\n")
Doug Zongkereef39442009-04-02 12:14:19 -07002300 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002301 finally:
2302 common.Cleanup()