blob: 4472b4a91d91fbdb96a07313394ac85767f33887 [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
75 Skip adding the compatibility package to the generated OTA package.
76
xunchang1cfe2512019-02-19 14:14:48 -080077 --output_metadata_path
78 Write a copy of the metadata to a separate file. Therefore, users can
79 read the post build fingerprint without extracting the OTA package.
80
Tao Bao30df8b42018-04-23 15:32:53 -070081Non-A/B OTA specific options
82
83 -b (--binary) <file>
84 Use the given binary as the update-binary in the output package, instead
85 of the binary in the build's target_files. Use for development only.
86
87 --block
88 Generate a block-based OTA for non-A/B device. We have deprecated the
89 support for file-based OTA since O. Block-based OTA will be used by
90 default for all non-A/B devices. Keeping this flag here to not break
91 existing callers.
92
93 -e (--extra_script) <file>
94 Insert the contents of file at the end of the update script.
Tao Bao43078aa2015-04-21 14:32:35 -070095
leozwangaa6c1a12015-08-14 10:57:58 -070096 --full_bootloader
97 Similar to --full_radio. When generating an incremental OTA, always
98 include a full copy of bootloader image.
99
Tao Bao30df8b42018-04-23 15:32:53 -0700100 --full_radio
101 When generating an incremental OTA, always include a full copy of radio
102 image. This option is only meaningful when -i is specified, because a full
103 radio is always included in a full OTA if applicable.
Michael Runge63f01de2014-10-28 19:24:19 -0700104
Tao Bao30df8b42018-04-23 15:32:53 -0700105 --log_diff <file>
106 Generate a log file that shows the differences in the source and target
107 builds for an incremental package. This option is only meaningful when -i
108 is specified.
109
110 -o (--oem_settings) <main_file[,additional_files...]>
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800111 Comma seperated list of files used to specify the expected OEM-specific
Tao Bao481bab82017-12-21 11:23:09 -0800112 properties on the OEM partition of the intended device. Multiple expected
113 values can be used by providing multiple files. Only the first dict will
114 be used to compute fingerprint, while the rest will be used to assert
115 OEM-specific properties.
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800116
Tao Bao8608cde2016-02-25 19:49:55 -0800117 --oem_no_mount
Tao Bao30df8b42018-04-23 15:32:53 -0700118 For devices with OEM-specific properties but without an OEM partition, do
119 not mount the OEM partition in the updater-script. This should be very
120 rarely used, since it's expected to have a dedicated OEM partition for
121 OEM-specific properties. Only meaningful when -o is specified.
Tao Bao8608cde2016-02-25 19:49:55 -0800122
Tao Bao30df8b42018-04-23 15:32:53 -0700123 --stash_threshold <float>
124 Specify the threshold that will be used to compute the maximum allowed
125 stash size (defaults to 0.8).
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700126
Tao Bao30df8b42018-04-23 15:32:53 -0700127 -t (--worker_threads) <int>
128 Specify the number of worker-threads that will be used when generating
129 patches for incremental updates (defaults to 3).
Tao Bao3e6161a2017-02-28 11:48:48 -0800130
Tao Bao30df8b42018-04-23 15:32:53 -0700131 --verify
132 Verify the checksums of the updated system and vendor (if any) partitions.
133 Non-A/B incremental OTAs only.
Doug Zongker1c390a22009-05-14 19:06:36 -0700134
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800135 -2 (--two_step)
Tao Bao30df8b42018-04-23 15:32:53 -0700136 Generate a 'two-step' OTA package, where recovery is updated first, so
137 that any changes made to the system partition are done using the new
138 recovery (new kernel, etc.).
139
140A/B OTA specific options
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800141
Tao Baof7140c02018-01-30 17:09:24 -0800142 --include_secondary
143 Additionally include the payload for secondary slot images (default:
144 False). Only meaningful when generating A/B OTAs.
145
146 By default, an A/B OTA package doesn't contain the images for the
147 secondary slot (e.g. system_other.img). Specifying this flag allows
148 generating a separate payload that will install secondary slot images.
149
150 Such a package needs to be applied in a two-stage manner, with a reboot
151 in-between. During the first stage, the updater applies the primary
152 payload only. Upon finishing, it reboots the device into the newly updated
153 slot. It then continues to install the secondary payload to the inactive
154 slot, but without switching the active slot at the end (needs the matching
155 support in update_engine, i.e. SWITCH_SLOT_ON_REBOOT flag).
156
157 Due to the special install procedure, the secondary payload will be always
158 generated as a full payload.
159
Tao Baodea0f8b2016-06-20 17:55:06 -0700160 --payload_signer <signer>
161 Specify the signer when signing the payload and metadata for A/B OTAs.
162 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
163 with the package private key. If the private key cannot be accessed
164 directly, a payload signer that knows how to do that should be specified.
165 The signer will be supplied with "-inkey <path_to_key>",
166 "-in <input_file>" and "-out <output_file>" parameters.
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700167
168 --payload_signer_args <args>
169 Specify the arguments needed for payload signer.
Tao Bao15a146a2018-02-21 16:06:59 -0800170
xunchang376cc7c2019-04-08 23:04:58 -0700171 --payload_signer_key_size <key_size>
172 Specify the key size in bytes of the payload signer.
173
Tao Bao15a146a2018-02-21 16:06:59 -0800174 --skip_postinstall
175 Skip the postinstall hooks when generating an A/B OTA package (default:
176 False). Note that this discards ALL the hooks, including non-optional
177 ones. Should only be used if caller knows it's safe to do so (e.g. all the
178 postinstall work is to dexopt apps and a data wipe will happen immediately
179 after). Only meaningful when generating A/B OTAs.
Doug Zongkereef39442009-04-02 12:14:19 -0700180"""
181
Tao Bao89fbb0f2017-01-10 10:47:58 -0800182from __future__ import print_function
183
Tao Bao32fcdab2018-10-12 10:30:39 -0700184import logging
Doug Zongkerfc44a512014-08-26 13:10:25 -0700185import multiprocessing
Tao Bao2dd1c482017-02-03 16:49:39 -0800186import os.path
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700187import shlex
Tao Bao15a146a2018-02-21 16:06:59 -0800188import shutil
Tao Bao85f16982018-03-08 16:28:33 -0800189import struct
Tao Bao481bab82017-12-21 11:23:09 -0800190import sys
Doug Zongkereef39442009-04-02 12:14:19 -0700191import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700192import zipfile
193
194import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700195import edify_generator
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700196import verity_utils
Doug Zongkereef39442009-04-02 12:14:19 -0700197
Tao Bao481bab82017-12-21 11:23:09 -0800198if sys.hexversion < 0x02070000:
199 print("Python 2.7 or newer is required.", file=sys.stderr)
200 sys.exit(1)
201
Tao Bao32fcdab2018-10-12 10:30:39 -0700202logger = logging.getLogger(__name__)
Tao Bao481bab82017-12-21 11:23:09 -0800203
Doug Zongkereef39442009-04-02 12:14:19 -0700204OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700205OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700206OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700207OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700208OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700209OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800210OPTIONS.downgrade = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700211OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700212OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
213if OPTIONS.worker_threads == 0:
214 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800215OPTIONS.two_step = False
Tao Baof7140c02018-01-30 17:09:24 -0800216OPTIONS.include_secondary = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900217OPTIONS.no_signing = False
Tao Bao457cbf62017-03-06 09:56:01 -0800218OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -0800219OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700220OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800221OPTIONS.oem_no_mount = False
Tao Bao43078aa2015-04-21 14:32:35 -0700222OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700223OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700224# Stash size cannot exceed cache_size * threshold.
225OPTIONS.cache_size = None
226OPTIONS.stash_threshold = 0.8
Tao Baod62c6032015-11-30 09:40:20 -0800227OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700228OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700229OPTIONS.payload_signer_args = []
xunchang376cc7c2019-04-08 23:04:58 -0700230OPTIONS.payload_signer_key_size = None
Tao Bao5f8ff932017-03-21 22:35:00 -0700231OPTIONS.extracted_input = None
Christian Oderf63e2cd2017-05-01 22:30:15 +0200232OPTIONS.key_passwords = []
Tao Bao15a146a2018-02-21 16:06:59 -0800233OPTIONS.skip_postinstall = False
Yifan Hong50e79542018-11-08 17:44:12 -0800234OPTIONS.retrofit_dynamic_partitions = False
xunchangabfa2652019-02-19 16:27:10 -0800235OPTIONS.skip_compatibility_check = False
xunchang1cfe2512019-02-19 14:14:48 -0800236OPTIONS.output_metadata_path = None
Tao Bao15a146a2018-02-21 16:06:59 -0800237
Tao Bao8dcf7382015-05-21 14:09:49 -0700238
Tao Bao2dd1c482017-02-03 16:49:39 -0800239METADATA_NAME = 'META-INF/com/android/metadata'
Tao Bao15a146a2018-02-21 16:06:59 -0800240POSTINSTALL_CONFIG = 'META/postinstall_config.txt'
Yifan Hong50e79542018-11-08 17:44:12 -0800241DYNAMIC_PARTITION_INFO = 'META/dynamic_partitions_info.txt'
Yifan Hongb433eba2019-03-06 12:42:53 -0800242AB_PARTITIONS = 'META/ab_partitions.txt'
Tao Bao04808502019-07-25 23:11:41 -0700243UNZIP_PATTERN = ['IMAGES/*', 'META/*', 'OTA/*', 'RADIO/*']
Yifan Hongb433eba2019-03-06 12:42:53 -0800244RETROFIT_DAP_UNZIP_PATTERN = ['OTA/super_*.img', AB_PARTITIONS]
Tao Bao6b0b2f92017-03-05 11:38:11 -0800245
Tao Bao2dd1c482017-02-03 16:49:39 -0800246
Tao Bao481bab82017-12-21 11:23:09 -0800247class BuildInfo(object):
248 """A class that holds the information for a given build.
249
250 This class wraps up the property querying for a given source or target build.
251 It abstracts away the logic of handling OEM-specific properties, and caches
252 the commonly used properties such as fingerprint.
253
254 There are two types of info dicts: a) build-time info dict, which is generated
255 at build time (i.e. included in a target_files zip); b) OEM info dict that is
256 specified at package generation time (via command line argument
257 '--oem_settings'). If a build doesn't use OEM-specific properties (i.e. not
258 having "oem_fingerprint_properties" in build-time info dict), all the queries
259 would be answered based on build-time info dict only. Otherwise if using
260 OEM-specific properties, some of them will be calculated from two info dicts.
261
262 Users can query properties similarly as using a dict() (e.g. info['fstab']),
263 or to query build properties via GetBuildProp() or GetVendorBuildProp().
264
265 Attributes:
266 info_dict: The build-time info dict.
267 is_ab: Whether it's a build that uses A/B OTA.
268 oem_dicts: A list of OEM dicts.
269 oem_props: A list of OEM properties that should be read from OEM dicts; None
270 if the build doesn't use any OEM-specific property.
271 fingerprint: The fingerprint of the build, which would be calculated based
272 on OEM properties if applicable.
273 device: The device name, which could come from OEM dicts if applicable.
274 """
275
Steven Laver9e73e822019-01-29 20:20:08 -0800276 _RO_PRODUCT_RESOLVE_PROPS = ["ro.product.brand", "ro.product.device",
277 "ro.product.manufacturer", "ro.product.model",
278 "ro.product.name"]
Justin Yun6151e3f2019-06-25 15:58:13 +0900279 _RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER = ["product", "odm", "vendor",
280 "system_ext", "system"]
Steven Laver9e73e822019-01-29 20:20:08 -0800281
Tao Bao481bab82017-12-21 11:23:09 -0800282 def __init__(self, info_dict, oem_dicts):
283 """Initializes a BuildInfo instance with the given dicts.
284
Tao Bao667c7532018-07-06 10:13:59 -0700285 Note that it only wraps up the given dicts, without making copies.
286
Tao Bao481bab82017-12-21 11:23:09 -0800287 Arguments:
288 info_dict: The build-time info dict.
289 oem_dicts: A list of OEM dicts (which is parsed from --oem_settings). Note
290 that it always uses the first dict to calculate the fingerprint or the
291 device name. The rest would be used for asserting OEM properties only
Tao Bao667c7532018-07-06 10:13:59 -0700292 (e.g. one package can be installed on one of these devices).
Tao Bao481bab82017-12-21 11:23:09 -0800293 """
294 self.info_dict = info_dict
295 self.oem_dicts = oem_dicts
296
297 self._is_ab = info_dict.get("ab_update") == "true"
298 self._oem_props = info_dict.get("oem_fingerprint_properties")
299
300 if self._oem_props:
301 assert oem_dicts, "OEM source required for this build"
302
303 # These two should be computed only after setting self._oem_props.
304 self._device = self.GetOemProperty("ro.product.device")
305 self._fingerprint = self.CalculateFingerprint()
306
307 @property
308 def is_ab(self):
309 return self._is_ab
310
311 @property
312 def device(self):
313 return self._device
314
315 @property
316 def fingerprint(self):
317 return self._fingerprint
318
319 @property
Tao Baoea6cbd02018-09-05 13:06:37 -0700320 def vendor_fingerprint(self):
Yifan Hong51d37562019-04-23 17:06:46 -0700321 return self._fingerprint_of("vendor")
322
323 @property
324 def product_fingerprint(self):
325 return self._fingerprint_of("product")
326
327 @property
328 def odm_fingerprint(self):
329 return self._fingerprint_of("odm")
330
331 def _fingerprint_of(self, partition):
332 if partition + ".build.prop" not in self.info_dict:
Tao Baoea6cbd02018-09-05 13:06:37 -0700333 return None
Yifan Hong51d37562019-04-23 17:06:46 -0700334 build_prop = self.info_dict[partition + ".build.prop"]
335 if "ro." + partition + ".build.fingerprint" in build_prop:
336 return build_prop["ro." + partition + ".build.fingerprint"]
337 if "ro." + partition + ".build.thumbprint" in build_prop:
338 return build_prop["ro." + partition + ".build.thumbprint"]
Tao Baoea6cbd02018-09-05 13:06:37 -0700339 return None
340
341 @property
Tao Bao481bab82017-12-21 11:23:09 -0800342 def oem_props(self):
343 return self._oem_props
344
345 def __getitem__(self, key):
346 return self.info_dict[key]
347
Tao Bao667c7532018-07-06 10:13:59 -0700348 def __setitem__(self, key, value):
349 self.info_dict[key] = value
350
Tao Bao481bab82017-12-21 11:23:09 -0800351 def get(self, key, default=None):
352 return self.info_dict.get(key, default)
353
Tao Bao667c7532018-07-06 10:13:59 -0700354 def items(self):
355 return self.info_dict.items()
356
Tao Bao481bab82017-12-21 11:23:09 -0800357 def GetBuildProp(self, prop):
358 """Returns the inquired build property."""
Steven Laver9e73e822019-01-29 20:20:08 -0800359 if prop in BuildInfo._RO_PRODUCT_RESOLVE_PROPS:
360 return self._ResolveRoProductBuildProp(prop)
361
Tao Bao481bab82017-12-21 11:23:09 -0800362 try:
363 return self.info_dict.get("build.prop", {})[prop]
364 except KeyError:
365 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
366
Steven Laver9e73e822019-01-29 20:20:08 -0800367 def _ResolveRoProductBuildProp(self, prop):
368 """Resolves the inquired ro.product.* build property"""
369 prop_val = self.info_dict.get("build.prop", {}).get(prop)
370 if prop_val:
371 return prop_val
372
373 source_order_val = self.info_dict.get("build.prop", {}).get(
Tao Bao59cf0c52019-06-25 10:04:24 -0700374 "ro.product.property_source_order")
Steven Laver9e73e822019-01-29 20:20:08 -0800375 if source_order_val:
376 source_order = source_order_val.split(",")
377 else:
378 source_order = BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER
379
380 # Check that all sources in ro.product.property_source_order are valid
381 if any([x not in BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER
382 for x in source_order]):
383 raise common.ExternalError(
Tao Bao59cf0c52019-06-25 10:04:24 -0700384 "Invalid ro.product.property_source_order '{}'".format(source_order))
Steven Laver9e73e822019-01-29 20:20:08 -0800385
386 for source in source_order:
Tao Bao59cf0c52019-06-25 10:04:24 -0700387 source_prop = prop.replace(
388 "ro.product", "ro.product.{}".format(source), 1)
389 prop_val = self.info_dict.get(
390 "{}.build.prop".format(source), {}).get(source_prop)
Steven Laver9e73e822019-01-29 20:20:08 -0800391 if prop_val:
392 return prop_val
393
394 raise common.ExternalError("couldn't resolve {}".format(prop))
395
Tao Bao481bab82017-12-21 11:23:09 -0800396 def GetVendorBuildProp(self, prop):
397 """Returns the inquired vendor build property."""
398 try:
399 return self.info_dict.get("vendor.build.prop", {})[prop]
400 except KeyError:
401 raise common.ExternalError(
402 "couldn't find %s in vendor.build.prop" % (prop,))
403
404 def GetOemProperty(self, key):
405 if self.oem_props is not None and key in self.oem_props:
406 return self.oem_dicts[0][key]
407 return self.GetBuildProp(key)
408
409 def CalculateFingerprint(self):
410 if self.oem_props is None:
Steven Laver9e73e822019-01-29 20:20:08 -0800411 try:
412 return self.GetBuildProp("ro.build.fingerprint")
413 except common.ExternalError:
414 return "{}/{}/{}:{}/{}/{}:{}/{}".format(
Tao Bao59cf0c52019-06-25 10:04:24 -0700415 self.GetBuildProp("ro.product.brand"),
416 self.GetBuildProp("ro.product.name"),
417 self.GetBuildProp("ro.product.device"),
418 self.GetBuildProp("ro.build.version.release"),
419 self.GetBuildProp("ro.build.id"),
420 self.GetBuildProp("ro.build.version.incremental"),
421 self.GetBuildProp("ro.build.type"),
422 self.GetBuildProp("ro.build.tags"))
Tao Bao481bab82017-12-21 11:23:09 -0800423 return "%s/%s/%s:%s" % (
424 self.GetOemProperty("ro.product.brand"),
425 self.GetOemProperty("ro.product.name"),
426 self.GetOemProperty("ro.product.device"),
427 self.GetBuildProp("ro.build.thumbprint"))
428
429 def WriteMountOemScript(self, script):
430 assert self.oem_props is not None
431 recovery_mount_options = self.info_dict.get("recovery_mount_options")
432 script.Mount("/oem", recovery_mount_options)
433
434 def WriteDeviceAssertions(self, script, oem_no_mount):
435 # Read the property directly if not using OEM properties.
436 if not self.oem_props:
437 script.AssertDevice(self.device)
438 return
439
440 # Otherwise assert OEM properties.
441 if not self.oem_dicts:
442 raise common.ExternalError(
443 "No OEM file provided to answer expected assertions")
444
445 for prop in self.oem_props.split():
446 values = []
447 for oem_dict in self.oem_dicts:
448 if prop in oem_dict:
449 values.append(oem_dict[prop])
450 if not values:
451 raise common.ExternalError(
452 "The OEM file is missing the property %s" % (prop,))
453 script.AssertOemProperty(prop, values, oem_no_mount)
454
455
Tao Baofabe0832018-01-17 15:52:28 -0800456class PayloadSigner(object):
457 """A class that wraps the payload signing works.
458
459 When generating a Payload, hashes of the payload and metadata files will be
460 signed with the device key, either by calling an external payload signer or
461 by calling openssl with the package key. This class provides a unified
462 interface, so that callers can just call PayloadSigner.Sign().
463
464 If an external payload signer has been specified (OPTIONS.payload_signer), it
465 calls the signer with the provided args (OPTIONS.payload_signer_args). Note
466 that the signing key should be provided as part of the payload_signer_args.
467 Otherwise without an external signer, it uses the package key
468 (OPTIONS.package_key) and calls openssl for the signing works.
469 """
470
471 def __init__(self):
472 if OPTIONS.payload_signer is None:
473 # Prepare the payload signing key.
474 private_key = OPTIONS.package_key + OPTIONS.private_key_suffix
475 pw = OPTIONS.key_passwords[OPTIONS.package_key]
476
477 cmd = ["openssl", "pkcs8", "-in", private_key, "-inform", "DER"]
478 cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"])
479 signing_key = common.MakeTempFile(prefix="key-", suffix=".key")
480 cmd.extend(["-out", signing_key])
Tao Baobec89c12018-10-15 11:53:28 -0700481 common.RunAndCheckOutput(cmd, verbose=False)
Tao Baofabe0832018-01-17 15:52:28 -0800482
483 self.signer = "openssl"
484 self.signer_args = ["pkeyutl", "-sign", "-inkey", signing_key,
485 "-pkeyopt", "digest:sha256"]
xunchang376cc7c2019-04-08 23:04:58 -0700486 self.key_size = self._GetKeySizeInBytes(signing_key)
Tao Baofabe0832018-01-17 15:52:28 -0800487 else:
488 self.signer = OPTIONS.payload_signer
489 self.signer_args = OPTIONS.payload_signer_args
xunchang376cc7c2019-04-08 23:04:58 -0700490 if OPTIONS.payload_signer_key_size:
491 self.key_size = int(OPTIONS.payload_signer_key_size)
492 assert self.key_size == 256 or self.key_size == 512, \
493 "Unsupported key size {}".format(OPTIONS.payload_signer_key_size)
494 else:
495 self.key_size = 256
496
497 @staticmethod
498 def _GetKeySizeInBytes(signing_key):
499 modulus_file = common.MakeTempFile(prefix="modulus-")
500 cmd = ["openssl", "rsa", "-inform", "PEM", "-in", signing_key, "-modulus",
501 "-noout", "-out", modulus_file]
502 common.RunAndCheckOutput(cmd, verbose=False)
503
504 with open(modulus_file) as f:
505 modulus_string = f.read()
506 # The modulus string has the format "Modulus=$data", where $data is the
507 # concatenation of hex dump of the modulus.
508 MODULUS_PREFIX = "Modulus="
509 assert modulus_string.startswith(MODULUS_PREFIX)
510 modulus_string = modulus_string[len(MODULUS_PREFIX):]
Tao Bao59cf0c52019-06-25 10:04:24 -0700511 key_size = len(modulus_string) // 2
xunchang376cc7c2019-04-08 23:04:58 -0700512 assert key_size == 256 or key_size == 512, \
513 "Unsupported key size {}".format(key_size)
514 return key_size
Tao Baofabe0832018-01-17 15:52:28 -0800515
516 def Sign(self, in_file):
517 """Signs the given input file. Returns the output filename."""
518 out_file = common.MakeTempFile(prefix="signed-", suffix=".bin")
519 cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file]
Regnier, Philippe2f7e11e2019-05-22 10:10:57 +0800520 common.RunAndCheckOutput(cmd, stdout=None, stderr=None)
Tao Baofabe0832018-01-17 15:52:28 -0800521 return out_file
522
523
Tao Bao40b18822018-01-30 18:19:04 -0800524class Payload(object):
525 """Manages the creation and the signing of an A/B OTA Payload."""
526
527 PAYLOAD_BIN = 'payload.bin'
528 PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
Tao Baof7140c02018-01-30 17:09:24 -0800529 SECONDARY_PAYLOAD_BIN = 'secondary/payload.bin'
530 SECONDARY_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt'
Tao Bao40b18822018-01-30 18:19:04 -0800531
Tao Bao667ff572018-02-10 00:02:40 -0800532 def __init__(self, secondary=False):
533 """Initializes a Payload instance.
534
535 Args:
536 secondary: Whether it's generating a secondary payload (default: False).
537 """
Tao Bao40b18822018-01-30 18:19:04 -0800538 self.payload_file = None
539 self.payload_properties = None
Tao Bao667ff572018-02-10 00:02:40 -0800540 self.secondary = secondary
Tao Bao40b18822018-01-30 18:19:04 -0800541
542 def Generate(self, target_file, source_file=None, additional_args=None):
543 """Generates a payload from the given target-files zip(s).
544
545 Args:
546 target_file: The filename of the target build target-files zip.
547 source_file: The filename of the source build target-files zip; or None if
548 generating a full OTA.
549 additional_args: A list of additional args that should be passed to
550 brillo_update_payload script; or None.
551 """
552 if additional_args is None:
553 additional_args = []
554
555 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
556 cmd = ["brillo_update_payload", "generate",
557 "--payload", payload_file,
558 "--target_image", target_file]
559 if source_file is not None:
560 cmd.extend(["--source_image", source_file])
561 cmd.extend(additional_args)
Regnier, Philippe2f7e11e2019-05-22 10:10:57 +0800562 common.RunAndCheckOutput(cmd, stdout=None, stderr=None)
Tao Bao40b18822018-01-30 18:19:04 -0800563
564 self.payload_file = payload_file
565 self.payload_properties = None
566
567 def Sign(self, payload_signer):
568 """Generates and signs the hashes of the payload and metadata.
569
570 Args:
571 payload_signer: A PayloadSigner() instance that serves the signing work.
572
573 Raises:
574 AssertionError: On any failure when calling brillo_update_payload script.
575 """
576 assert isinstance(payload_signer, PayloadSigner)
577
578 # 1. Generate hashes of the payload and metadata files.
579 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
580 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
581 cmd = ["brillo_update_payload", "hash",
582 "--unsigned_payload", self.payload_file,
xunchang376cc7c2019-04-08 23:04:58 -0700583 "--signature_size", str(payload_signer.key_size),
Tao Bao40b18822018-01-30 18:19:04 -0800584 "--metadata_hash_file", metadata_sig_file,
585 "--payload_hash_file", payload_sig_file]
Regnier, Philippe2f7e11e2019-05-22 10:10:57 +0800586 common.RunAndCheckOutput(cmd, stdout=None, stderr=None)
Tao Bao40b18822018-01-30 18:19:04 -0800587
588 # 2. Sign the hashes.
589 signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
590 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
591
592 # 3. Insert the signatures back into the payload file.
593 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
594 suffix=".bin")
595 cmd = ["brillo_update_payload", "sign",
596 "--unsigned_payload", self.payload_file,
597 "--payload", signed_payload_file,
xunchang376cc7c2019-04-08 23:04:58 -0700598 "--signature_size", str(payload_signer.key_size),
Tao Bao40b18822018-01-30 18:19:04 -0800599 "--metadata_signature_file", signed_metadata_sig_file,
600 "--payload_signature_file", signed_payload_sig_file]
Regnier, Philippe2f7e11e2019-05-22 10:10:57 +0800601 common.RunAndCheckOutput(cmd, stdout=None, stderr=None)
Tao Bao40b18822018-01-30 18:19:04 -0800602
603 # 4. Dump the signed payload properties.
604 properties_file = common.MakeTempFile(prefix="payload-properties-",
605 suffix=".txt")
606 cmd = ["brillo_update_payload", "properties",
607 "--payload", signed_payload_file,
608 "--properties_file", properties_file]
Regnier, Philippe2f7e11e2019-05-22 10:10:57 +0800609 common.RunAndCheckOutput(cmd, stdout=None, stderr=None)
Tao Bao40b18822018-01-30 18:19:04 -0800610
Tao Bao667ff572018-02-10 00:02:40 -0800611 if self.secondary:
612 with open(properties_file, "a") as f:
613 f.write("SWITCH_SLOT_ON_REBOOT=0\n")
614
Tao Bao40b18822018-01-30 18:19:04 -0800615 if OPTIONS.wipe_user_data:
616 with open(properties_file, "a") as f:
617 f.write("POWERWASH=1\n")
618
619 self.payload_file = signed_payload_file
620 self.payload_properties = properties_file
621
Tao Bao667ff572018-02-10 00:02:40 -0800622 def WriteToZip(self, output_zip):
Tao Bao40b18822018-01-30 18:19:04 -0800623 """Writes the payload to the given zip.
624
625 Args:
626 output_zip: The output ZipFile instance.
627 """
628 assert self.payload_file is not None
629 assert self.payload_properties is not None
630
Tao Bao667ff572018-02-10 00:02:40 -0800631 if self.secondary:
Tao Baof7140c02018-01-30 17:09:24 -0800632 payload_arcname = Payload.SECONDARY_PAYLOAD_BIN
633 payload_properties_arcname = Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT
634 else:
635 payload_arcname = Payload.PAYLOAD_BIN
636 payload_properties_arcname = Payload.PAYLOAD_PROPERTIES_TXT
637
Tao Bao40b18822018-01-30 18:19:04 -0800638 # Add the signed payload file and properties into the zip. In order to
639 # support streaming, we pack them as ZIP_STORED. So these entries can be
640 # read directly with the offset and length pairs.
Tao Baof7140c02018-01-30 17:09:24 -0800641 common.ZipWrite(output_zip, self.payload_file, arcname=payload_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800642 compress_type=zipfile.ZIP_STORED)
643 common.ZipWrite(output_zip, self.payload_properties,
Tao Baof7140c02018-01-30 17:09:24 -0800644 arcname=payload_properties_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800645 compress_type=zipfile.ZIP_STORED)
646
647
Doug Zongkereef39442009-04-02 12:14:19 -0700648def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200649 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700650
Doug Zongker951495f2009-08-14 12:44:19 -0700651 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
652 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700653
654
Tao Bao481bab82017-12-21 11:23:09 -0800655def _LoadOemDicts(oem_source):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800656 """Returns the list of loaded OEM properties dict."""
Tao Bao481bab82017-12-21 11:23:09 -0800657 if not oem_source:
658 return None
659
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800660 oem_dicts = []
Tao Bao481bab82017-12-21 11:23:09 -0800661 for oem_file in oem_source:
662 with open(oem_file) as fp:
663 oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines()))
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800664 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700665
Doug Zongkereef39442009-04-02 12:14:19 -0700666
Tao Baod42e97e2016-11-30 12:11:57 -0800667def _WriteRecoveryImageToBoot(script, output_zip):
668 """Find and write recovery image to /boot in two-step OTA.
669
670 In two-step OTAs, we write recovery image to /boot as the first step so that
671 we can reboot to there and install a new recovery image to /recovery.
672 A special "recovery-two-step.img" will be preferred, which encodes the correct
673 path of "/boot". Otherwise the device may show "device is corrupt" message
674 when booting into /boot.
675
676 Fall back to using the regular recovery.img if the two-step recovery image
677 doesn't exist. Note that rebuilding the special image at this point may be
678 infeasible, because we don't have the desired boot signer and keys when
679 calling ota_from_target_files.py.
680 """
681
682 recovery_two_step_img_name = "recovery-two-step.img"
683 recovery_two_step_img_path = os.path.join(
Tao Bao04808502019-07-25 23:11:41 -0700684 OPTIONS.input_tmp, "OTA", recovery_two_step_img_name)
Tao Baod42e97e2016-11-30 12:11:57 -0800685 if os.path.exists(recovery_two_step_img_path):
Tao Bao04808502019-07-25 23:11:41 -0700686 common.ZipWrite(
687 output_zip,
688 recovery_two_step_img_path,
689 arcname=recovery_two_step_img_name)
Tao Bao32fcdab2018-10-12 10:30:39 -0700690 logger.info(
691 "two-step package: using %s in stage 1/3", recovery_two_step_img_name)
Tao Baod42e97e2016-11-30 12:11:57 -0800692 script.WriteRawImage("/boot", recovery_two_step_img_name)
693 else:
Tao Bao32fcdab2018-10-12 10:30:39 -0700694 logger.info("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800695 # The "recovery.img" entry has been written into package earlier.
696 script.WriteRawImage("/boot", "recovery.img")
697
698
Doug Zongkerc9253822014-02-04 12:17:58 -0800699def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700700 namelist = [name for name in target_files_zip.namelist()]
701 return ("SYSTEM/recovery-from-boot.p" in namelist or
702 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700703
Tao Bao457cbf62017-03-06 09:56:01 -0800704
Yifan Hong51d37562019-04-23 17:06:46 -0700705def HasPartition(target_files_zip, partition):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700706 try:
Yifan Hong51d37562019-04-23 17:06:46 -0700707 target_files_zip.getinfo(partition.upper() + "/")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700708 return True
709 except KeyError:
710 return False
711
Tao Bao457cbf62017-03-06 09:56:01 -0800712
Yifan Hong51d37562019-04-23 17:06:46 -0700713def HasVendorPartition(target_files_zip):
714 return HasPartition(target_files_zip, "vendor")
715
716
717def HasProductPartition(target_files_zip):
718 return HasPartition(target_files_zip, "product")
719
720
721def HasOdmPartition(target_files_zip):
722 return HasPartition(target_files_zip, "odm")
723
724
Tao Bao481bab82017-12-21 11:23:09 -0800725def HasTrebleEnabled(target_files_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700726 return (HasVendorPartition(target_files_zip) and
Tao Bao481bab82017-12-21 11:23:09 -0800727 target_info.GetBuildProp("ro.treble.enabled") == "true")
Tao Baobcd1d162017-08-26 13:10:26 -0700728
729
Tao Bao481bab82017-12-21 11:23:09 -0800730def WriteFingerprintAssertion(script, target_info, source_info):
731 source_oem_props = source_info.oem_props
732 target_oem_props = target_info.oem_props
Michael Runge6e836112014-04-15 17:40:21 -0700733
Tao Bao481bab82017-12-21 11:23:09 -0800734 if source_oem_props is None and target_oem_props is None:
735 script.AssertSomeFingerprint(
736 source_info.fingerprint, target_info.fingerprint)
737 elif source_oem_props is not None and target_oem_props is not None:
738 script.AssertSomeThumbprint(
739 target_info.GetBuildProp("ro.build.thumbprint"),
740 source_info.GetBuildProp("ro.build.thumbprint"))
741 elif source_oem_props is None and target_oem_props is not None:
742 script.AssertFingerprintOrThumbprint(
743 source_info.fingerprint,
744 target_info.GetBuildProp("ro.build.thumbprint"))
745 else:
746 script.AssertFingerprintOrThumbprint(
747 target_info.fingerprint,
748 source_info.GetBuildProp("ro.build.thumbprint"))
Doug Zongker73ef8252009-07-23 15:12:53 -0700749
Doug Zongkerfc44a512014-08-26 13:10:25 -0700750
Tao Bao481bab82017-12-21 11:23:09 -0800751def AddCompatibilityArchiveIfTrebleEnabled(target_zip, output_zip, target_info,
752 source_info=None):
Tao Baobcd1d162017-08-26 13:10:26 -0700753 """Adds compatibility info into the output zip if it's Treble-enabled target.
Tao Bao21803d32017-04-19 10:16:09 -0700754
755 Metadata used for on-device compatibility verification is retrieved from
756 target_zip then added to compatibility.zip which is added to the output_zip
757 archive.
758
Tao Baobcd1d162017-08-26 13:10:26 -0700759 Compatibility archive should only be included for devices that have enabled
760 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700761
762 Args:
763 target_zip: Zip file containing the source files to be included for OTA.
764 output_zip: Zip file that will be sent for OTA.
Tao Bao481bab82017-12-21 11:23:09 -0800765 target_info: The BuildInfo instance that holds the target build info.
766 source_info: The BuildInfo instance that holds the source build info, if
767 generating an incremental OTA; None otherwise.
Tao Bao21803d32017-04-19 10:16:09 -0700768 """
769
Yifan Hong51d37562019-04-23 17:06:46 -0700770 def AddCompatibilityArchive(framework_updated, device_updated):
771 """Adds compatibility info based on update status of both sides of Treble
772 boundary.
Tao Bao21803d32017-04-19 10:16:09 -0700773
Tao Baobcd1d162017-08-26 13:10:26 -0700774 Args:
Yifan Hong51d37562019-04-23 17:06:46 -0700775 framework_updated: If True, the system / product image will be updated
776 and therefore their metadata should be included.
777 device_updated: If True, the vendor / odm image will be updated and
778 therefore their metadata should be included.
Tao Baobcd1d162017-08-26 13:10:26 -0700779 """
780 # Determine what metadata we need. Files are names relative to META/.
781 compatibility_files = []
Yifan Hong51d37562019-04-23 17:06:46 -0700782 device_metadata = ("vendor_manifest.xml", "vendor_matrix.xml")
783 framework_metadata = ("system_manifest.xml", "system_matrix.xml")
784 if device_updated:
785 compatibility_files += device_metadata
786 if framework_updated:
787 compatibility_files += framework_metadata
Tao Bao21803d32017-04-19 10:16:09 -0700788
Tao Baobcd1d162017-08-26 13:10:26 -0700789 # Create new archive.
790 compatibility_archive = tempfile.NamedTemporaryFile()
Tao Bao481bab82017-12-21 11:23:09 -0800791 compatibility_archive_zip = zipfile.ZipFile(
792 compatibility_archive, "w", compression=zipfile.ZIP_DEFLATED)
Tao Bao21803d32017-04-19 10:16:09 -0700793
Tao Baobcd1d162017-08-26 13:10:26 -0700794 # Add metadata.
795 for file_name in compatibility_files:
796 target_file_name = "META/" + file_name
Tao Bao21803d32017-04-19 10:16:09 -0700797
Tao Baobcd1d162017-08-26 13:10:26 -0700798 if target_file_name in target_zip.namelist():
799 data = target_zip.read(target_file_name)
800 common.ZipWriteStr(compatibility_archive_zip, file_name, data)
Tao Bao21803d32017-04-19 10:16:09 -0700801
Tao Baobcd1d162017-08-26 13:10:26 -0700802 # Ensure files are written before we copy into output_zip.
803 compatibility_archive_zip.close()
804
805 # Only add the archive if we have any compatibility info.
806 if compatibility_archive_zip.namelist():
807 common.ZipWrite(output_zip, compatibility_archive.name,
808 arcname="compatibility.zip",
809 compress_type=zipfile.ZIP_STORED)
810
Yifan Hong51d37562019-04-23 17:06:46 -0700811 def FingerprintChanged(source_fp, target_fp):
812 if source_fp is None or target_fp is None:
813 return True
814 return source_fp != target_fp
815
Tao Baobcd1d162017-08-26 13:10:26 -0700816 # Will only proceed if the target has enabled the Treble support (as well as
817 # having a /vendor partition).
Tao Bao481bab82017-12-21 11:23:09 -0800818 if not HasTrebleEnabled(target_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700819 return
820
xunchangabfa2652019-02-19 16:27:10 -0800821 # Skip adding the compatibility package as a workaround for b/114240221. The
822 # compatibility will always fail on devices without qualified kernels.
823 if OPTIONS.skip_compatibility_check:
824 return
825
Yifan Hong51d37562019-04-23 17:06:46 -0700826 # Full OTA carries the info for system/vendor/product/odm
Tao Bao481bab82017-12-21 11:23:09 -0800827 if source_info is None:
Tao Baobcd1d162017-08-26 13:10:26 -0700828 AddCompatibilityArchive(True, True)
829 return
830
Tao Bao481bab82017-12-21 11:23:09 -0800831 source_fp = source_info.fingerprint
832 target_fp = target_info.fingerprint
Tao Baobcd1d162017-08-26 13:10:26 -0700833 system_updated = source_fp != target_fp
834
Yifan Hong51d37562019-04-23 17:06:46 -0700835 # other build fingerprints could be possibly blacklisted at build time. For
836 # such a case, we consider those images being changed.
837 vendor_updated = FingerprintChanged(source_info.vendor_fingerprint,
838 target_info.vendor_fingerprint)
839 product_updated = HasProductPartition(target_zip) and \
840 FingerprintChanged(source_info.product_fingerprint,
841 target_info.product_fingerprint)
842 odm_updated = HasOdmPartition(target_zip) and \
843 FingerprintChanged(source_info.odm_fingerprint,
844 target_info.odm_fingerprint)
Tao Baobcd1d162017-08-26 13:10:26 -0700845
Yifan Hong51d37562019-04-23 17:06:46 -0700846 AddCompatibilityArchive(system_updated or product_updated,
847 vendor_updated or odm_updated)
Tao Bao21803d32017-04-19 10:16:09 -0700848
849
Tao Bao491d7e22018-02-21 13:17:22 -0800850def WriteFullOTAPackage(input_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -0800851 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700852
Tao Bao481bab82017-12-21 11:23:09 -0800853 # We don't know what version it will be installed on top of. We expect the API
854 # just won't change very often. Similarly for fstab, it might have changed in
855 # the target build.
856 target_api_version = target_info["recovery_api_version"]
857 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700858
Tao Bao481bab82017-12-21 11:23:09 -0800859 if target_info.oem_props and not OPTIONS.oem_no_mount:
860 target_info.WriteMountOemScript(script)
861
Tao Baodf3a48b2018-01-10 16:30:43 -0800862 metadata = GetPackageMetadata(target_info)
Doug Zongker2ea21062010-04-28 16:05:21 -0700863
Tao Bao491d7e22018-02-21 13:17:22 -0800864 if not OPTIONS.no_signing:
865 staging_file = common.MakeTempFile(suffix='.zip')
866 else:
867 staging_file = output_file
868
869 output_zip = zipfile.ZipFile(
870 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
871
Doug Zongker05d3dea2009-06-22 11:32:31 -0700872 device_specific = common.DeviceSpecificParams(
873 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800874 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700875 output_zip=output_zip,
876 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700877 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700878 metadata=metadata,
879 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700880
Tao Bao457cbf62017-03-06 09:56:01 -0800881 assert HasRecoveryPatch(input_zip)
Doug Zongkerc9253822014-02-04 12:17:58 -0800882
Tao Bao481bab82017-12-21 11:23:09 -0800883 # Assertions (e.g. downgrade check, device properties check).
884 ts = target_info.GetBuildProp("ro.build.date.utc")
885 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700886 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700887
Tao Bao481bab82017-12-21 11:23:09 -0800888 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700889 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800890
891 # Two-step package strategy (in chronological order, which is *not*
892 # the order in which the generated script has things):
893 #
894 # if stage is not "2/3" or "3/3":
895 # write recovery image to boot partition
896 # set stage to "2/3"
897 # reboot to boot partition and restart recovery
898 # else if stage is "2/3":
899 # write recovery image to recovery partition
900 # set stage to "3/3"
901 # reboot to recovery partition and restart recovery
902 # else:
903 # (stage must be "3/3")
904 # set stage to ""
905 # do normal full package installation:
906 # wipe and install system, boot image, etc.
907 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700908 # complete script normally
909 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800910
911 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
912 OPTIONS.input_tmp, "RECOVERY")
913 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800914 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800915 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800916 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800917 assert fs.fs_type.upper() == "EMMC", \
918 "two-step packages only supported on devices with EMMC /misc partitions"
919 bcb_dev = {"bcb_dev": fs.device}
920 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
921 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700922if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800923""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800924
925 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
926 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800927 script.WriteRawImage("/recovery", "recovery.img")
928 script.AppendExtra("""
929set_stage("%(bcb_dev)s", "3/3");
930reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700931else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800932""" % bcb_dev)
933
Tao Baod42e97e2016-11-30 12:11:57 -0800934 # Stage 3/3: Make changes.
935 script.Comment("Stage 3/3")
936
Tao Bao6c55a8a2015-04-08 15:30:27 -0700937 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800938 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700939
Doug Zongkere5ff5902012-01-17 10:55:37 -0800940 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700941
Doug Zongker01ce19c2014-02-04 13:48:15 -0800942 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700943
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700944 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800945 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700946 if HasVendorPartition(input_zip):
947 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700948
Doug Zongker4b9596f2014-06-09 14:15:45 -0700949 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800950
Yifan Hong10c530d2018-12-27 17:34:18 -0800951 def GetBlockDifference(partition):
952 # Full OTA is done as an "incremental" against an empty source image. This
953 # has the effect of writing new data from the package to the entire
954 # partition, but lets us reuse the updater code that writes incrementals to
955 # do it.
Yifan Hong8a66a712019-04-04 15:37:57 -0700956 tgt = common.GetUserImage(partition, OPTIONS.input_tmp, input_zip,
957 info_dict=target_info,
958 reset_file_map=True)
Yifan Hong10c530d2018-12-27 17:34:18 -0800959 diff = common.BlockDifference(partition, tgt, src=None)
960 return diff
Doug Zongkereef39442009-04-02 12:14:19 -0700961
Yifan Hong10c530d2018-12-27 17:34:18 -0800962 device_specific_diffs = device_specific.FullOTA_GetBlockDifferences()
963 if device_specific_diffs:
964 assert all(isinstance(diff, common.BlockDifference)
965 for diff in device_specific_diffs), \
966 "FullOTA_GetBlockDifferences is not returning a list of " \
967 "BlockDifference objects"
Doug Zongkerc9253822014-02-04 12:17:58 -0800968
Yifan Hong10c530d2018-12-27 17:34:18 -0800969 progress_dict = dict()
970 block_diffs = [GetBlockDifference("system")]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700971 if HasVendorPartition(input_zip):
Yifan Hong10c530d2018-12-27 17:34:18 -0800972 block_diffs.append(GetBlockDifference("vendor"))
973 progress_dict["vendor"] = 0.1
974 if device_specific_diffs:
975 block_diffs += device_specific_diffs
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700976
Yifan Hong10c530d2018-12-27 17:34:18 -0800977 if target_info.get('use_dynamic_partitions') == "true":
978 # Use empty source_info_dict to indicate that all partitions / groups must
979 # be re-added.
980 dynamic_partitions_diff = common.DynamicPartitionsDifference(
981 info_dict=OPTIONS.info_dict,
982 block_diffs=block_diffs,
983 progress_dict=progress_dict)
984 dynamic_partitions_diff.WriteScript(script, output_zip,
985 write_verify_script=OPTIONS.verify)
986 else:
987 for block_diff in block_diffs:
988 block_diff.WriteScript(script, output_zip,
989 progress=progress_dict.get(block_diff.partition),
990 write_verify_script=OPTIONS.verify)
Doug Zongker73ef8252009-07-23 15:12:53 -0700991
Tao Bao481bab82017-12-21 11:23:09 -0800992 AddCompatibilityArchiveIfTrebleEnabled(input_zip, output_zip, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700993
Yifan Hong10c530d2018-12-27 17:34:18 -0800994 boot_img = common.GetBootableImage(
995 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Tao Bao481bab82017-12-21 11:23:09 -0800996 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700997 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700998
Doug Zongker01ce19c2014-02-04 13:48:15 -0800999 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -07001000 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -07001001
Doug Zongker01ce19c2014-02-04 13:48:15 -08001002 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001003 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -07001004
Doug Zongker1c390a22009-05-14 19:06:36 -07001005 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001006 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001007
Doug Zongker14833602010-02-02 13:12:04 -08001008 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001009
Doug Zongker922206e2014-03-04 13:16:24 -08001010 if OPTIONS.wipe_user_data:
1011 script.ShowProgress(0.1, 10)
1012 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001013
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001014 if OPTIONS.two_step:
1015 script.AppendExtra("""
1016set_stage("%(bcb_dev)s", "");
1017""" % bcb_dev)
1018 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -08001019
1020 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
1021 script.Comment("Stage 1/3")
1022 _WriteRecoveryImageToBoot(script, output_zip)
1023
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001024 script.AppendExtra("""
1025set_stage("%(bcb_dev)s", "2/3");
1026reboot_now("%(bcb_dev)s", "");
1027endif;
1028endif;
1029""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -08001030
Tao Bao5d182562016-02-23 11:38:39 -08001031 script.SetProgress(1)
1032 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001033 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001034
1035 # We haven't written the metadata entry, which will be done in
1036 # FinalizeMetadata.
1037 common.ZipClose(output_zip)
1038
1039 needed_property_files = (
1040 NonAbOtaPropertyFiles(),
1041 )
1042 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Doug Zongker2ea21062010-04-28 16:05:21 -07001043
Doug Zongkerfc44a512014-08-26 13:10:25 -07001044
xunchang1cfe2512019-02-19 14:14:48 -08001045def WriteMetadata(metadata, output):
1046 """Writes the metadata to the zip archive or a file.
1047
1048 Args:
1049 metadata: The metadata dict for the package.
1050 output: A ZipFile object or a string of the output file path.
1051 """
1052
Tao Bao59cf0c52019-06-25 10:04:24 -07001053 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.items())])
xunchang1cfe2512019-02-19 14:14:48 -08001054 if isinstance(output, zipfile.ZipFile):
1055 common.ZipWriteStr(output, METADATA_NAME, value,
1056 compress_type=zipfile.ZIP_STORED)
1057 return
1058
1059 with open(output, 'w') as f:
1060 f.write(value)
Doug Zongkereef39442009-04-02 12:14:19 -07001061
Doug Zongkerfc44a512014-08-26 13:10:25 -07001062
Tao Bao481bab82017-12-21 11:23:09 -08001063def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -08001064 # Only incremental OTAs are allowed to reach here.
1065 assert OPTIONS.incremental_source is not None
1066
Tao Bao481bab82017-12-21 11:23:09 -08001067 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
1068 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Bao59cf0c52019-06-25 10:04:24 -07001069 is_downgrade = int(post_timestamp) < int(pre_timestamp)
Tao Baob31892e2017-02-07 11:21:17 -08001070
1071 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -08001072 if not is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -07001073 raise RuntimeError(
1074 "--downgrade or --override_timestamp specified but no downgrade "
1075 "detected: pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -08001076 metadata["ota-downgrade"] = "yes"
Tao Baob31892e2017-02-07 11:21:17 -08001077 else:
1078 if is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -07001079 raise RuntimeError(
1080 "Downgrade detected based on timestamp check: pre: %s, post: %s. "
1081 "Need to specify --override_timestamp OR --downgrade to allow "
1082 "building the incremental." % (pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -08001083
1084
Tao Baodf3a48b2018-01-10 16:30:43 -08001085def GetPackageMetadata(target_info, source_info=None):
1086 """Generates and returns the metadata dict.
1087
1088 It generates a dict() that contains the info to be written into an OTA
1089 package (META-INF/com/android/metadata). It also handles the detection of
Tao Baofaa8e0b2018-04-12 14:31:43 -07001090 downgrade / data wipe based on the global options.
Tao Baodf3a48b2018-01-10 16:30:43 -08001091
1092 Args:
1093 target_info: The BuildInfo instance that holds the target build info.
1094 source_info: The BuildInfo instance that holds the source build info, or
1095 None if generating full OTA.
1096
1097 Returns:
1098 A dict to be written into package metadata entry.
1099 """
1100 assert isinstance(target_info, BuildInfo)
1101 assert source_info is None or isinstance(source_info, BuildInfo)
1102
1103 metadata = {
1104 'post-build' : target_info.fingerprint,
1105 'post-build-incremental' : target_info.GetBuildProp(
1106 'ro.build.version.incremental'),
Tao Bao35dc2552018-02-01 13:18:00 -08001107 'post-sdk-level' : target_info.GetBuildProp(
1108 'ro.build.version.sdk'),
1109 'post-security-patch-level' : target_info.GetBuildProp(
1110 'ro.build.version.security_patch'),
Tao Baodf3a48b2018-01-10 16:30:43 -08001111 }
1112
1113 if target_info.is_ab:
1114 metadata['ota-type'] = 'AB'
1115 metadata['ota-required-cache'] = '0'
1116 else:
1117 metadata['ota-type'] = 'BLOCK'
1118
1119 if OPTIONS.wipe_user_data:
1120 metadata['ota-wipe'] = 'yes'
1121
Tao Bao393eeb42019-03-06 16:00:38 -08001122 if OPTIONS.retrofit_dynamic_partitions:
1123 metadata['ota-retrofit-dynamic-partitions'] = 'yes'
1124
Tao Baodf3a48b2018-01-10 16:30:43 -08001125 is_incremental = source_info is not None
1126 if is_incremental:
1127 metadata['pre-build'] = source_info.fingerprint
1128 metadata['pre-build-incremental'] = source_info.GetBuildProp(
1129 'ro.build.version.incremental')
1130 metadata['pre-device'] = source_info.device
1131 else:
1132 metadata['pre-device'] = target_info.device
1133
Tao Baofaa8e0b2018-04-12 14:31:43 -07001134 # Use the actual post-timestamp, even for a downgrade case.
1135 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
1136
1137 # Detect downgrades and set up downgrade flags accordingly.
Tao Baodf3a48b2018-01-10 16:30:43 -08001138 if is_incremental:
1139 HandleDowngradeMetadata(metadata, target_info, source_info)
Tao Baodf3a48b2018-01-10 16:30:43 -08001140
1141 return metadata
1142
1143
Tao Baod3fc38a2018-03-08 16:09:01 -08001144class PropertyFiles(object):
1145 """A class that computes the property-files string for an OTA package.
1146
1147 A property-files string is a comma-separated string that contains the
1148 offset/size info for an OTA package. The entries, which must be ZIP_STORED,
1149 can be fetched directly with the package URL along with the offset/size info.
1150 These strings can be used for streaming A/B OTAs, or allowing an updater to
1151 download package metadata entry directly, without paying the cost of
1152 downloading entire package.
Tao Baofe5b69a2018-03-02 09:47:43 -08001153
Tao Baocc8e2662018-03-01 19:30:00 -08001154 Computing the final property-files string requires two passes. Because doing
1155 the whole package signing (with signapk.jar) will possibly reorder the ZIP
1156 entries, which may in turn invalidate earlier computed ZIP entry offset/size
1157 values.
1158
1159 This class provides functions to be called for each pass. The general flow is
1160 as follows.
1161
Tao Baod3fc38a2018-03-08 16:09:01 -08001162 property_files = PropertyFiles()
Tao Baocc8e2662018-03-01 19:30:00 -08001163 # The first pass, which writes placeholders before doing initial signing.
1164 property_files.Compute()
1165 SignOutput()
1166
1167 # The second pass, by replacing the placeholders with actual data.
1168 property_files.Finalize()
1169 SignOutput()
1170
1171 And the caller can additionally verify the final result.
1172
1173 property_files.Verify()
Tao Baofe5b69a2018-03-02 09:47:43 -08001174 """
1175
Tao Baocc8e2662018-03-01 19:30:00 -08001176 def __init__(self):
Tao Baod3fc38a2018-03-08 16:09:01 -08001177 self.name = None
1178 self.required = ()
1179 self.optional = ()
Tao Baofe5b69a2018-03-02 09:47:43 -08001180
Tao Baocc8e2662018-03-01 19:30:00 -08001181 def Compute(self, input_zip):
1182 """Computes and returns a property-files string with placeholders.
Tao Baofe5b69a2018-03-02 09:47:43 -08001183
Tao Baocc8e2662018-03-01 19:30:00 -08001184 We reserve extra space for the offset and size of the metadata entry itself,
1185 although we don't know the final values until the package gets signed.
Tao Baofe5b69a2018-03-02 09:47:43 -08001186
Tao Baocc8e2662018-03-01 19:30:00 -08001187 Args:
1188 input_zip: The input ZIP file.
Tao Baofe5b69a2018-03-02 09:47:43 -08001189
Tao Baocc8e2662018-03-01 19:30:00 -08001190 Returns:
1191 A string with placeholders for the metadata offset/size info, e.g.
1192 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1193 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001194 return self.GetPropertyFilesString(input_zip, reserve_space=True)
Tao Baofe5b69a2018-03-02 09:47:43 -08001195
Tao Baod2ce2ed2018-03-16 12:59:42 -07001196 class InsufficientSpaceException(Exception):
1197 pass
1198
Tao Baocc8e2662018-03-01 19:30:00 -08001199 def Finalize(self, input_zip, reserved_length):
1200 """Finalizes a property-files string with actual METADATA offset/size info.
1201
1202 The input ZIP file has been signed, with the ZIP entries in the desired
1203 place (signapk.jar will possibly reorder the ZIP entries). Now we compute
1204 the ZIP entry offsets and construct the property-files string with actual
1205 data. Note that during this process, we must pad the property-files string
1206 to the reserved length, so that the METADATA entry size remains the same.
1207 Otherwise the entries' offsets and sizes may change again.
1208
1209 Args:
1210 input_zip: The input ZIP file.
1211 reserved_length: The reserved length of the property-files string during
1212 the call to Compute(). The final string must be no more than this
1213 size.
1214
1215 Returns:
1216 A property-files string including the metadata offset/size info, e.g.
1217 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379 ".
1218
1219 Raises:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001220 InsufficientSpaceException: If the reserved length is insufficient to hold
1221 the final string.
Tao Baocc8e2662018-03-01 19:30:00 -08001222 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001223 result = self.GetPropertyFilesString(input_zip, reserve_space=False)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001224 if len(result) > reserved_length:
1225 raise self.InsufficientSpaceException(
1226 'Insufficient reserved space: reserved={}, actual={}'.format(
1227 reserved_length, len(result)))
1228
Tao Baocc8e2662018-03-01 19:30:00 -08001229 result += ' ' * (reserved_length - len(result))
1230 return result
1231
1232 def Verify(self, input_zip, expected):
1233 """Verifies the input ZIP file contains the expected property-files string.
1234
1235 Args:
1236 input_zip: The input ZIP file.
1237 expected: The property-files string that's computed from Finalize().
1238
1239 Raises:
1240 AssertionError: On finding a mismatch.
1241 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001242 actual = self.GetPropertyFilesString(input_zip)
Tao Baocc8e2662018-03-01 19:30:00 -08001243 assert actual == expected, \
1244 "Mismatching streaming metadata: {} vs {}.".format(actual, expected)
1245
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001246 def GetPropertyFilesString(self, zip_file, reserve_space=False):
1247 """
1248 Constructs the property-files string per request.
1249
1250 Args:
1251 zip_file: The input ZIP file.
1252 reserved_length: The reserved length of the property-files string.
1253
1254 Returns:
1255 A property-files string including the metadata offset/size info, e.g.
1256 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1257 """
Tao Baocc8e2662018-03-01 19:30:00 -08001258
1259 def ComputeEntryOffsetSize(name):
1260 """Computes the zip entry offset and size."""
1261 info = zip_file.getinfo(name)
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001262 offset = info.header_offset
1263 offset += zipfile.sizeFileHeader
1264 offset += len(info.extra) + len(info.filename)
Tao Baocc8e2662018-03-01 19:30:00 -08001265 size = info.file_size
1266 return '%s:%d:%d' % (os.path.basename(name), offset, size)
1267
1268 tokens = []
Tao Bao85f16982018-03-08 16:28:33 -08001269 tokens.extend(self._GetPrecomputed(zip_file))
Tao Baocc8e2662018-03-01 19:30:00 -08001270 for entry in self.required:
1271 tokens.append(ComputeEntryOffsetSize(entry))
1272 for entry in self.optional:
1273 if entry in zip_file.namelist():
1274 tokens.append(ComputeEntryOffsetSize(entry))
1275
1276 # 'META-INF/com/android/metadata' is required. We don't know its actual
1277 # offset and length (as well as the values for other entries). So we reserve
Tao Baod2ce2ed2018-03-16 12:59:42 -07001278 # 15-byte as a placeholder ('offset:length'), which is sufficient to cover
1279 # the space for metadata entry. Because 'offset' allows a max of 10-digit
1280 # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the
1281 # reserved space serves the metadata entry only.
Tao Baocc8e2662018-03-01 19:30:00 -08001282 if reserve_space:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001283 tokens.append('metadata:' + ' ' * 15)
Tao Baocc8e2662018-03-01 19:30:00 -08001284 else:
1285 tokens.append(ComputeEntryOffsetSize(METADATA_NAME))
1286
1287 return ','.join(tokens)
Tao Baofe5b69a2018-03-02 09:47:43 -08001288
Tao Bao85f16982018-03-08 16:28:33 -08001289 def _GetPrecomputed(self, input_zip):
1290 """Computes the additional tokens to be included into the property-files.
1291
1292 This applies to tokens without actual ZIP entries, such as
1293 payload_metadadata.bin. We want to expose the offset/size to updaters, so
1294 that they can download the payload metadata directly with the info.
1295
1296 Args:
1297 input_zip: The input zip file.
1298
1299 Returns:
1300 A list of strings (tokens) to be added to the property-files string.
1301 """
1302 # pylint: disable=no-self-use
1303 # pylint: disable=unused-argument
1304 return []
1305
Tao Baofe5b69a2018-03-02 09:47:43 -08001306
Tao Baod3fc38a2018-03-08 16:09:01 -08001307class StreamingPropertyFiles(PropertyFiles):
1308 """A subclass for computing the property-files for streaming A/B OTAs."""
1309
1310 def __init__(self):
1311 super(StreamingPropertyFiles, self).__init__()
1312 self.name = 'ota-streaming-property-files'
1313 self.required = (
1314 # payload.bin and payload_properties.txt must exist.
1315 'payload.bin',
1316 'payload_properties.txt',
1317 )
1318 self.optional = (
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001319 # care_map is available only if dm-verity is enabled.
1320 'care_map.pb',
Tao Baod3fc38a2018-03-08 16:09:01 -08001321 'care_map.txt',
1322 # compatibility.zip is available only if target supports Treble.
1323 'compatibility.zip',
1324 )
1325
1326
Tao Bao85f16982018-03-08 16:28:33 -08001327class AbOtaPropertyFiles(StreamingPropertyFiles):
1328 """The property-files for A/B OTA that includes payload_metadata.bin info.
1329
1330 Since P, we expose one more token (aka property-file), in addition to the ones
1331 for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'.
1332 'payload_metadata.bin' is the header part of a payload ('payload.bin'), which
1333 doesn't exist as a separate ZIP entry, but can be used to verify if the
1334 payload can be applied on the given device.
1335
1336 For backward compatibility, we keep both of the 'ota-streaming-property-files'
1337 and the newly added 'ota-property-files' in P. The new token will only be
1338 available in 'ota-property-files'.
1339 """
1340
1341 def __init__(self):
1342 super(AbOtaPropertyFiles, self).__init__()
1343 self.name = 'ota-property-files'
1344
1345 def _GetPrecomputed(self, input_zip):
1346 offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip)
1347 return ['payload_metadata.bin:{}:{}'.format(offset, size)]
1348
1349 @staticmethod
1350 def _GetPayloadMetadataOffsetAndSize(input_zip):
1351 """Computes the offset and size of the payload metadata for a given package.
1352
1353 (From system/update_engine/update_metadata.proto)
1354 A delta update file contains all the deltas needed to update a system from
1355 one specific version to another specific version. The update format is
1356 represented by this struct pseudocode:
1357
1358 struct delta_update_file {
1359 char magic[4] = "CrAU";
1360 uint64 file_format_version;
1361 uint64 manifest_size; // Size of protobuf DeltaArchiveManifest
1362
1363 // Only present if format_version > 1:
1364 uint32 metadata_signature_size;
1365
1366 // The Bzip2 compressed DeltaArchiveManifest
1367 char manifest[metadata_signature_size];
1368
1369 // The signature of the metadata (from the beginning of the payload up to
1370 // this location, not including the signature itself). This is a
1371 // serialized Signatures message.
1372 char medatada_signature_message[metadata_signature_size];
1373
1374 // Data blobs for files, no specific format. The specific offset
1375 // and length of each data blob is recorded in the DeltaArchiveManifest.
1376 struct {
1377 char data[];
1378 } blobs[];
1379
1380 // These two are not signed:
1381 uint64 payload_signatures_message_size;
1382 char payload_signatures_message[];
1383 };
1384
1385 'payload-metadata.bin' contains all the bytes from the beginning of the
1386 payload, till the end of 'medatada_signature_message'.
1387 """
1388 payload_info = input_zip.getinfo('payload.bin')
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001389 payload_offset = payload_info.header_offset
1390 payload_offset += zipfile.sizeFileHeader
1391 payload_offset += len(payload_info.extra) + len(payload_info.filename)
Tao Bao85f16982018-03-08 16:28:33 -08001392 payload_size = payload_info.file_size
1393
Tao Bao59cf0c52019-06-25 10:04:24 -07001394 with input_zip.open('payload.bin') as payload_fp:
Tao Bao85f16982018-03-08 16:28:33 -08001395 header_bin = payload_fp.read(24)
1396
1397 # network byte order (big-endian)
1398 header = struct.unpack("!IQQL", header_bin)
1399
1400 # 'CrAU'
1401 magic = header[0]
1402 assert magic == 0x43724155, "Invalid magic: {:x}".format(magic)
1403
1404 manifest_size = header[2]
1405 metadata_signature_size = header[3]
1406 metadata_total = 24 + manifest_size + metadata_signature_size
1407 assert metadata_total < payload_size
1408
1409 return (payload_offset, metadata_total)
1410
1411
Tao Bao491d7e22018-02-21 13:17:22 -08001412class NonAbOtaPropertyFiles(PropertyFiles):
1413 """The property-files for non-A/B OTA.
1414
1415 For non-A/B OTA, the property-files string contains the info for METADATA
1416 entry, with which a system updater can be fetched the package metadata prior
1417 to downloading the entire package.
1418 """
1419
1420 def __init__(self):
1421 super(NonAbOtaPropertyFiles, self).__init__()
1422 self.name = 'ota-property-files'
1423
1424
Tao Baod3fc38a2018-03-08 16:09:01 -08001425def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
Tao Baofe5b69a2018-03-02 09:47:43 -08001426 """Finalizes the metadata and signs an A/B OTA package.
1427
1428 In order to stream an A/B OTA package, we need 'ota-streaming-property-files'
1429 that contains the offsets and sizes for the ZIP entries. An example
1430 property-files string is as follows.
1431
1432 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379"
1433
1434 OTA server can pass down this string, in addition to the package URL, to the
1435 system update client. System update client can then fetch individual ZIP
1436 entries (ZIP_STORED) directly at the given offset of the URL.
1437
1438 Args:
1439 metadata: The metadata dict for the package.
1440 input_file: The input ZIP filename that doesn't contain the package METADATA
1441 entry yet.
1442 output_file: The final output ZIP filename.
Tao Baod3fc38a2018-03-08 16:09:01 -08001443 needed_property_files: The list of PropertyFiles' to be generated.
Tao Baofe5b69a2018-03-02 09:47:43 -08001444 """
Tao Baofe5b69a2018-03-02 09:47:43 -08001445
Tao Baod2ce2ed2018-03-16 12:59:42 -07001446 def ComputeAllPropertyFiles(input_file, needed_property_files):
1447 # Write the current metadata entry with placeholders.
1448 with zipfile.ZipFile(input_file) as input_zip:
1449 for property_files in needed_property_files:
1450 metadata[property_files.name] = property_files.Compute(input_zip)
1451 namelist = input_zip.namelist()
Tao Baofe5b69a2018-03-02 09:47:43 -08001452
Tao Baod2ce2ed2018-03-16 12:59:42 -07001453 if METADATA_NAME in namelist:
1454 common.ZipDelete(input_file, METADATA_NAME)
1455 output_zip = zipfile.ZipFile(input_file, 'a')
1456 WriteMetadata(metadata, output_zip)
1457 common.ZipClose(output_zip)
1458
1459 if OPTIONS.no_signing:
1460 return input_file
1461
Tao Bao491d7e22018-02-21 13:17:22 -08001462 prelim_signing = common.MakeTempFile(suffix='.zip')
1463 SignOutput(input_file, prelim_signing)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001464 return prelim_signing
Tao Baofe5b69a2018-03-02 09:47:43 -08001465
Tao Baod2ce2ed2018-03-16 12:59:42 -07001466 def FinalizeAllPropertyFiles(prelim_signing, needed_property_files):
1467 with zipfile.ZipFile(prelim_signing) as prelim_signing_zip:
1468 for property_files in needed_property_files:
1469 metadata[property_files.name] = property_files.Finalize(
1470 prelim_signing_zip, len(metadata[property_files.name]))
1471
1472 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP
1473 # entries, as well as padding the entry headers. We do a preliminary signing
1474 # (with an incomplete metadata entry) to allow that to happen. Then compute
1475 # the ZIP entry offsets, write back the final metadata and do the final
1476 # signing.
1477 prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files)
1478 try:
1479 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
1480 except PropertyFiles.InsufficientSpaceException:
1481 # Even with the preliminary signing, the entry orders may change
1482 # dramatically, which leads to insufficiently reserved space during the
1483 # first call to ComputeAllPropertyFiles(). In that case, we redo all the
1484 # preliminary signing works, based on the already ordered ZIP entries, to
1485 # address the issue.
1486 prelim_signing = ComputeAllPropertyFiles(
1487 prelim_signing, needed_property_files)
1488 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
Tao Baofe5b69a2018-03-02 09:47:43 -08001489
1490 # Replace the METADATA entry.
1491 common.ZipDelete(prelim_signing, METADATA_NAME)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001492 output_zip = zipfile.ZipFile(prelim_signing, 'a')
Tao Baofe5b69a2018-03-02 09:47:43 -08001493 WriteMetadata(metadata, output_zip)
1494 common.ZipClose(output_zip)
1495
1496 # Re-sign the package after updating the metadata entry.
Tao Bao491d7e22018-02-21 13:17:22 -08001497 if OPTIONS.no_signing:
1498 output_file = prelim_signing
1499 else:
1500 SignOutput(prelim_signing, output_file)
Tao Baofe5b69a2018-03-02 09:47:43 -08001501
1502 # Reopen the final signed zip to double check the streaming metadata.
Tao Baod2ce2ed2018-03-16 12:59:42 -07001503 with zipfile.ZipFile(output_file) as output_zip:
Tao Baod3fc38a2018-03-08 16:09:01 -08001504 for property_files in needed_property_files:
1505 property_files.Verify(output_zip, metadata[property_files.name].strip())
Tao Baofe5b69a2018-03-02 09:47:43 -08001506
xunchang1cfe2512019-02-19 14:14:48 -08001507 # If requested, dump the metadata to a separate file.
1508 output_metadata_path = OPTIONS.output_metadata_path
1509 if output_metadata_path:
1510 WriteMetadata(metadata, output_metadata_path)
1511
Tao Baofe5b69a2018-03-02 09:47:43 -08001512
Tao Bao491d7e22018-02-21 13:17:22 -08001513def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -08001514 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1515 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -08001516
Tao Bao481bab82017-12-21 11:23:09 -08001517 target_api_version = target_info["recovery_api_version"]
1518 source_api_version = source_info["recovery_api_version"]
1519 if source_api_version == 0:
Tao Bao32fcdab2018-10-12 10:30:39 -07001520 logger.warning(
1521 "Generating edify script for a source that can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001522
Tao Bao481bab82017-12-21 11:23:09 -08001523 script = edify_generator.EdifyGenerator(
1524 source_api_version, target_info, fstab=source_info["fstab"])
1525
1526 if target_info.oem_props or source_info.oem_props:
1527 if not OPTIONS.oem_no_mount:
1528 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -07001529
Tao Baodf3a48b2018-01-10 16:30:43 -08001530 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -08001531
Tao Bao491d7e22018-02-21 13:17:22 -08001532 if not OPTIONS.no_signing:
1533 staging_file = common.MakeTempFile(suffix='.zip')
1534 else:
1535 staging_file = output_file
1536
1537 output_zip = zipfile.ZipFile(
1538 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
1539
Geremy Condra36bd3652014-02-06 19:45:10 -08001540 device_specific = common.DeviceSpecificParams(
1541 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001542 source_version=source_api_version,
Yifan Hong8a66a712019-04-04 15:37:57 -07001543 source_tmp=OPTIONS.source_tmp,
Geremy Condra36bd3652014-02-06 19:45:10 -08001544 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001545 target_version=target_api_version,
Yifan Hong8a66a712019-04-04 15:37:57 -07001546 target_tmp=OPTIONS.target_tmp,
Geremy Condra36bd3652014-02-06 19:45:10 -08001547 output_zip=output_zip,
1548 script=script,
1549 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -08001550 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001551
Geremy Condra36bd3652014-02-06 19:45:10 -08001552 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001553 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001554 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001555 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001556 updating_boot = (not OPTIONS.two_step and
1557 (source_boot.data != target_boot.data))
1558
Geremy Condra36bd3652014-02-06 19:45:10 -08001559 target_recovery = common.GetBootableImage(
1560 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -08001561
Yifan Hong8a66a712019-04-04 15:37:57 -07001562 # See notes in common.GetUserImage()
Tao Baoe709b092018-02-07 12:40:00 -08001563 allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or
1564 target_info.get('ext4_share_dup_blocks') == "true")
Yifan Hong8a66a712019-04-04 15:37:57 -07001565 system_src = common.GetUserImage("system", OPTIONS.source_tmp, source_zip,
1566 info_dict=source_info,
1567 allow_shared_blocks=allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001568
1569 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1570 "system", 4096, target_info)
Yifan Hong8a66a712019-04-04 15:37:57 -07001571 system_tgt = common.GetUserImage("system", OPTIONS.target_tmp, target_zip,
1572 info_dict=target_info,
1573 allow_shared_blocks=allow_shared_blocks,
1574 hashtree_info_generator=
1575 hashtree_info_generator)
Tao Baodd2a5892015-03-12 12:32:37 -07001576
Tao Bao0582cb62017-12-21 11:47:01 -08001577 blockimgdiff_version = max(
Tao Bao481bab82017-12-21 11:23:09 -08001578 int(i) for i in target_info.get("blockimgdiff_versions", "1").split(","))
Tao Bao0582cb62017-12-21 11:47:01 -08001579 assert blockimgdiff_version >= 3
Tao Baodd2a5892015-03-12 12:32:37 -07001580
Tao Baof8acad12016-07-07 09:09:58 -07001581 # Check the first block of the source system partition for remount R/W only
1582 # if the filesystem is ext4.
Tao Bao481bab82017-12-21 11:23:09 -08001583 system_src_partition = source_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001584 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001585 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
1586 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
1587 # b) the blocks listed in block map may not contain all the bytes for a given
1588 # file (because they're rounded to be 4K-aligned).
Tao Bao481bab82017-12-21 11:23:09 -08001589 system_tgt_partition = target_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001590 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
1591 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001592 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001593 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001594 version=blockimgdiff_version,
1595 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001596
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001597 if HasVendorPartition(target_zip):
1598 if not HasVendorPartition(source_zip):
1599 raise RuntimeError("can't generate incremental that adds /vendor")
Yifan Hong8a66a712019-04-04 15:37:57 -07001600 vendor_src = common.GetUserImage("vendor", OPTIONS.source_tmp, source_zip,
1601 info_dict=source_info,
1602 allow_shared_blocks=allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001603 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1604 "vendor", 4096, target_info)
Yifan Hong8a66a712019-04-04 15:37:57 -07001605 vendor_tgt = common.GetUserImage(
1606 "vendor", OPTIONS.target_tmp, target_zip,
1607 info_dict=target_info,
1608 allow_shared_blocks=allow_shared_blocks,
1609 hashtree_info_generator=hashtree_info_generator)
Tianjie Xufc3422a2015-12-15 11:53:59 -08001610
1611 # Check first block of vendor partition for remount R/W only if
1612 # disk type is ext4
Tao Bao481bab82017-12-21 11:23:09 -08001613 vendor_partition = source_info["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -08001614 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001615 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001616 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001617 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001618 version=blockimgdiff_version,
1619 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001620 else:
1621 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -08001622
Tao Baobcd1d162017-08-26 13:10:26 -07001623 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001624 target_zip, output_zip, target_info, source_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001625
Tao Bao481bab82017-12-21 11:23:09 -08001626 # Assertions (e.g. device properties check).
1627 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -08001628 device_specific.IncrementalOTA_Assertions()
1629
1630 # Two-step incremental package strategy (in chronological order,
1631 # which is *not* the order in which the generated script has
1632 # things):
1633 #
1634 # if stage is not "2/3" or "3/3":
1635 # do verification on current system
1636 # write recovery image to boot partition
1637 # set stage to "2/3"
1638 # reboot to boot partition and restart recovery
1639 # else if stage is "2/3":
1640 # write recovery image to recovery partition
1641 # set stage to "3/3"
1642 # reboot to recovery partition and restart recovery
1643 # else:
1644 # (stage must be "3/3")
1645 # perform update:
1646 # patch system files, etc.
1647 # force full install of new boot image
1648 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001649 # complete script normally
1650 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001651
1652 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001653 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001654 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001655 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001656 assert fs.fs_type.upper() == "EMMC", \
1657 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001658 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001659 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1660 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001661if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001662""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001663
1664 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1665 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001666 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001667 script.WriteRawImage("/recovery", "recovery.img")
1668 script.AppendExtra("""
1669set_stage("%(bcb_dev)s", "3/3");
1670reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001671else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001672""" % bcb_dev)
1673
Tao Baod42e97e2016-11-30 12:11:57 -08001674 # Stage 1/3: (a) Verify the current system.
1675 script.Comment("Stage 1/3")
1676
Tao Bao6c55a8a2015-04-08 15:30:27 -07001677 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001678 script.Print("Source: {}".format(source_info.fingerprint))
1679 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001680
Geremy Condra36bd3652014-02-06 19:45:10 -08001681 script.Print("Verifying current system...")
1682
1683 device_specific.IncrementalOTA_VerifyBegin()
1684
Tao Bao481bab82017-12-21 11:23:09 -08001685 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001686
Tao Baod8d14be2016-02-04 14:26:02 -08001687 # Check the required cache size (i.e. stashed blocks).
1688 size = []
1689 if system_diff:
1690 size.append(system_diff.required_cache)
1691 if vendor_diff:
1692 size.append(vendor_diff.required_cache)
1693
Geremy Condra36bd3652014-02-06 19:45:10 -08001694 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -08001695 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001696 d = common.Difference(target_boot, source_boot)
1697 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001698 if d is None:
1699 include_full_boot = True
1700 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1701 else:
1702 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001703
Tao Bao32fcdab2018-10-12 10:30:39 -07001704 logger.info(
1705 "boot target: %d source: %d diff: %d", target_boot.size,
1706 source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -08001707
Tao Bao51216552018-08-26 11:53:15 -07001708 common.ZipWriteStr(output_zip, "boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001709
Tao Bao51216552018-08-26 11:53:15 -07001710 script.PatchPartitionCheck(
1711 "{}:{}:{}:{}".format(
1712 boot_type, boot_device, target_boot.size, target_boot.sha1),
1713 "{}:{}:{}:{}".format(
1714 boot_type, boot_device, source_boot.size, source_boot.sha1))
1715
Tao Baod8d14be2016-02-04 14:26:02 -08001716 size.append(target_boot.size)
1717
1718 if size:
1719 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001720
1721 device_specific.IncrementalOTA_VerifyEnd()
1722
1723 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001724 # Stage 1/3: (b) Write recovery image to /boot.
1725 _WriteRecoveryImageToBoot(script, output_zip)
1726
Geremy Condra36bd3652014-02-06 19:45:10 -08001727 script.AppendExtra("""
1728set_stage("%(bcb_dev)s", "2/3");
1729reboot_now("%(bcb_dev)s", "");
1730else
1731""" % bcb_dev)
1732
Tao Baod42e97e2016-11-30 12:11:57 -08001733 # Stage 3/3: Make changes.
1734 script.Comment("Stage 3/3")
1735
Jesse Zhao75bcea02015-01-06 10:59:53 -08001736 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001737 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001738 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001739 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Yifan Hong10c530d2018-12-27 17:34:18 -08001740 device_specific_diffs = device_specific.IncrementalOTA_GetBlockDifferences()
1741 if device_specific_diffs:
1742 assert all(isinstance(diff, common.BlockDifference)
1743 for diff in device_specific_diffs), \
1744 "IncrementalOTA_GetBlockDifferences is not returning a list of " \
1745 "BlockDifference objects"
1746 for diff in device_specific_diffs:
1747 diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001748
Geremy Condra36bd3652014-02-06 19:45:10 -08001749 script.Comment("---- start making changes here ----")
1750
1751 device_specific.IncrementalOTA_InstallBegin()
1752
Yifan Hong10c530d2018-12-27 17:34:18 -08001753 block_diffs = [system_diff]
1754 progress_dict = {"system": 0.8 if vendor_diff else 0.9}
Doug Zongkerfc44a512014-08-26 13:10:25 -07001755 if vendor_diff:
Yifan Hong10c530d2018-12-27 17:34:18 -08001756 block_diffs.append(vendor_diff)
1757 progress_dict["vendor"] = 0.1
1758 if device_specific_diffs:
1759 block_diffs += device_specific_diffs
1760
1761 if OPTIONS.source_info_dict.get("use_dynamic_partitions") == "true":
1762 if OPTIONS.target_info_dict.get("use_dynamic_partitions") != "true":
1763 raise RuntimeError(
1764 "can't generate incremental that disables dynamic partitions")
1765 dynamic_partitions_diff = common.DynamicPartitionsDifference(
1766 info_dict=OPTIONS.target_info_dict,
1767 source_info_dict=OPTIONS.source_info_dict,
1768 block_diffs=block_diffs,
1769 progress_dict=progress_dict)
1770 dynamic_partitions_diff.WriteScript(
1771 script, output_zip, write_verify_script=OPTIONS.verify)
1772 else:
1773 for block_diff in block_diffs:
1774 block_diff.WriteScript(script, output_zip,
1775 progress=progress_dict.get(block_diff.partition),
1776 write_verify_script=OPTIONS.verify)
Geremy Condra36bd3652014-02-06 19:45:10 -08001777
1778 if OPTIONS.two_step:
1779 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1780 script.WriteRawImage("/boot", "boot.img")
Tao Bao32fcdab2018-10-12 10:30:39 -07001781 logger.info("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001782
1783 if not OPTIONS.two_step:
1784 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001785 if include_full_boot:
Tao Bao32fcdab2018-10-12 10:30:39 -07001786 logger.info("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001787 script.Print("Installing boot image...")
1788 script.WriteRawImage("/boot", "boot.img")
1789 else:
1790 # Produce the boot image by applying a patch to the current
1791 # contents of the boot partition, and write it back to the
1792 # partition.
Tao Bao32fcdab2018-10-12 10:30:39 -07001793 logger.info("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001794 script.Print("Patching boot image...")
1795 script.ShowProgress(0.1, 10)
Tao Bao51216552018-08-26 11:53:15 -07001796 script.PatchPartition(
1797 '{}:{}:{}:{}'.format(
1798 boot_type, boot_device, target_boot.size, target_boot.sha1),
1799 '{}:{}:{}:{}'.format(
1800 boot_type, boot_device, source_boot.size, source_boot.sha1),
1801 'boot.img.p')
Geremy Condra36bd3652014-02-06 19:45:10 -08001802 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001803 logger.info("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001804
1805 # Do device-specific installation (eg, write radio image).
1806 device_specific.IncrementalOTA_InstallEnd()
1807
1808 if OPTIONS.extra_script is not None:
1809 script.AppendExtra(OPTIONS.extra_script)
1810
Doug Zongker922206e2014-03-04 13:16:24 -08001811 if OPTIONS.wipe_user_data:
1812 script.Print("Erasing user data...")
1813 script.FormatPartition("/data")
1814
Geremy Condra36bd3652014-02-06 19:45:10 -08001815 if OPTIONS.two_step:
1816 script.AppendExtra("""
1817set_stage("%(bcb_dev)s", "");
1818endif;
1819endif;
1820""" % bcb_dev)
1821
1822 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001823 # For downgrade OTAs, we prefer to use the update-binary in the source
1824 # build that is actually newer than the one in the target build.
1825 if OPTIONS.downgrade:
1826 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1827 else:
1828 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001829 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001830
1831 # We haven't written the metadata entry yet, which will be handled in
1832 # FinalizeMetadata().
1833 common.ZipClose(output_zip)
1834
1835 # Sign the generated zip package unless no_signing is specified.
1836 needed_property_files = (
1837 NonAbOtaPropertyFiles(),
1838 )
1839 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Geremy Condra36bd3652014-02-06 19:45:10 -08001840
Doug Zongker32b527d2014-03-04 10:03:02 -08001841
Tao Bao15a146a2018-02-21 16:06:59 -08001842def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
Tao Baof7140c02018-01-30 17:09:24 -08001843 """Returns a target-files.zip file for generating secondary payload.
1844
1845 Although the original target-files.zip already contains secondary slot
1846 images (i.e. IMAGES/system_other.img), we need to rename the files to the
1847 ones without _other suffix. Note that we cannot instead modify the names in
1848 META/ab_partitions.txt, because there are no matching partitions on device.
1849
1850 For the partitions that don't have secondary images, the ones for primary
1851 slot will be used. This is to ensure that we always have valid boot, vbmeta,
1852 bootloader images in the inactive slot.
1853
1854 Args:
1855 input_file: The input target-files.zip file.
Tao Bao15a146a2018-02-21 16:06:59 -08001856 skip_postinstall: Whether to skip copying the postinstall config file.
Tao Baof7140c02018-01-30 17:09:24 -08001857
1858 Returns:
1859 The filename of the target-files.zip for generating secondary payload.
1860 """
1861 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1862 target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
1863
Tao Baodba59ee2018-01-09 13:21:02 -08001864 with zipfile.ZipFile(input_file, 'r') as input_zip:
1865 infolist = input_zip.infolist()
Tao Bao12489802018-07-12 14:47:38 -07001866
Tao Bao0ff15de2019-03-20 11:26:06 -07001867 input_tmp = common.UnzipTemp(input_file, UNZIP_PATTERN)
Tao Baodba59ee2018-01-09 13:21:02 -08001868 for info in infolist:
Tao Baof7140c02018-01-30 17:09:24 -08001869 unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
1870 if info.filename == 'IMAGES/system_other.img':
1871 common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
1872
1873 # Primary images and friends need to be skipped explicitly.
1874 elif info.filename in ('IMAGES/system.img',
1875 'IMAGES/system.map'):
1876 pass
1877
Tao Bao15a146a2018-02-21 16:06:59 -08001878 # Skip copying the postinstall config if requested.
1879 elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
1880 pass
1881
Tao Bao12489802018-07-12 14:47:38 -07001882 elif info.filename.startswith(('META/', 'IMAGES/', 'RADIO/')):
Tao Baof7140c02018-01-30 17:09:24 -08001883 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
1884
Tao Baof7140c02018-01-30 17:09:24 -08001885 common.ZipClose(target_zip)
1886
1887 return target_file
1888
1889
Tao Bao15a146a2018-02-21 16:06:59 -08001890def GetTargetFilesZipWithoutPostinstallConfig(input_file):
1891 """Returns a target-files.zip that's not containing postinstall_config.txt.
1892
1893 This allows brillo_update_payload script to skip writing all the postinstall
1894 hooks in the generated payload. The input target-files.zip file will be
1895 duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
1896 contain the postinstall_config.txt entry, the input file will be returned.
1897
1898 Args:
1899 input_file: The input target-files.zip filename.
1900
1901 Returns:
1902 The filename of target-files.zip that doesn't contain postinstall config.
1903 """
1904 # We should only make a copy if postinstall_config entry exists.
1905 with zipfile.ZipFile(input_file, 'r') as input_zip:
1906 if POSTINSTALL_CONFIG not in input_zip.namelist():
1907 return input_file
1908
1909 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1910 shutil.copyfile(input_file, target_file)
1911 common.ZipDelete(target_file, POSTINSTALL_CONFIG)
1912 return target_file
1913
1914
Yifan Hong50e79542018-11-08 17:44:12 -08001915def GetTargetFilesZipForRetrofitDynamicPartitions(input_file,
Yifan Hongb433eba2019-03-06 12:42:53 -08001916 super_block_devices,
1917 dynamic_partition_list):
Yifan Hong50e79542018-11-08 17:44:12 -08001918 """Returns a target-files.zip for retrofitting dynamic partitions.
1919
1920 This allows brillo_update_payload to generate an OTA based on the exact
1921 bits on the block devices. Postinstall is disabled.
1922
1923 Args:
1924 input_file: The input target-files.zip filename.
1925 super_block_devices: The list of super block devices
Yifan Hongb433eba2019-03-06 12:42:53 -08001926 dynamic_partition_list: The list of dynamic partitions
Yifan Hong50e79542018-11-08 17:44:12 -08001927
1928 Returns:
1929 The filename of target-files.zip with *.img replaced with super_*.img for
1930 each block device in super_block_devices.
1931 """
1932 assert super_block_devices, "No super_block_devices are specified."
1933
1934 replace = {'OTA/super_{}.img'.format(dev): 'IMAGES/{}.img'.format(dev)
Tao Bao03fecb62018-11-28 10:59:23 -08001935 for dev in super_block_devices}
Yifan Hong50e79542018-11-08 17:44:12 -08001936
1937 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1938 shutil.copyfile(input_file, target_file)
1939
Tao Baoa3705452019-06-24 15:33:41 -07001940 with zipfile.ZipFile(input_file) as input_zip:
Yifan Hong50e79542018-11-08 17:44:12 -08001941 namelist = input_zip.namelist()
1942
Yifan Hongb433eba2019-03-06 12:42:53 -08001943 input_tmp = common.UnzipTemp(input_file, RETROFIT_DAP_UNZIP_PATTERN)
1944
1945 # Remove partitions from META/ab_partitions.txt that is in
1946 # dynamic_partition_list but not in super_block_devices so that
1947 # brillo_update_payload won't generate update for those logical partitions.
1948 ab_partitions_file = os.path.join(input_tmp, *AB_PARTITIONS.split('/'))
1949 with open(ab_partitions_file) as f:
1950 ab_partitions_lines = f.readlines()
1951 ab_partitions = [line.strip() for line in ab_partitions_lines]
1952 # Assert that all super_block_devices are in ab_partitions
1953 super_device_not_updated = [partition for partition in super_block_devices
1954 if partition not in ab_partitions]
1955 assert not super_device_not_updated, \
1956 "{} is in super_block_devices but not in {}".format(
1957 super_device_not_updated, AB_PARTITIONS)
1958 # ab_partitions -= (dynamic_partition_list - super_block_devices)
1959 new_ab_partitions = common.MakeTempFile(prefix="ab_partitions", suffix=".txt")
1960 with open(new_ab_partitions, 'w') as f:
1961 for partition in ab_partitions:
1962 if (partition in dynamic_partition_list and
1963 partition not in super_block_devices):
Tao Bao59cf0c52019-06-25 10:04:24 -07001964 logger.info("Dropping %s from ab_partitions.txt", partition)
1965 continue
Yifan Hongb433eba2019-03-06 12:42:53 -08001966 f.write(partition + "\n")
1967 to_delete = [AB_PARTITIONS]
1968
Yifan Hong50e79542018-11-08 17:44:12 -08001969 # Always skip postinstall for a retrofit update.
Yifan Hongb433eba2019-03-06 12:42:53 -08001970 to_delete += [POSTINSTALL_CONFIG]
Yifan Hong50e79542018-11-08 17:44:12 -08001971
1972 # Delete dynamic_partitions_info.txt so that brillo_update_payload thinks this
1973 # is a regular update on devices without dynamic partitions support.
1974 to_delete += [DYNAMIC_PARTITION_INFO]
1975
Tao Bao03fecb62018-11-28 10:59:23 -08001976 # Remove the existing partition images as well as the map files.
Tao Bao59cf0c52019-06-25 10:04:24 -07001977 to_delete += list(replace.values())
Tao Bao03fecb62018-11-28 10:59:23 -08001978 to_delete += ['IMAGES/{}.map'.format(dev) for dev in super_block_devices]
Yifan Hong50e79542018-11-08 17:44:12 -08001979
1980 common.ZipDelete(target_file, to_delete)
1981
Yifan Hong50e79542018-11-08 17:44:12 -08001982 target_zip = zipfile.ZipFile(target_file, 'a', allowZip64=True)
1983
1984 # Write super_{foo}.img as {foo}.img.
1985 for src, dst in replace.items():
1986 assert src in namelist, \
Tao Bao59cf0c52019-06-25 10:04:24 -07001987 'Missing {} in {}; {} cannot be written'.format(src, input_file, dst)
Yifan Hong50e79542018-11-08 17:44:12 -08001988 unzipped_file = os.path.join(input_tmp, *src.split('/'))
1989 common.ZipWrite(target_zip, unzipped_file, arcname=dst)
1990
Yifan Hongb433eba2019-03-06 12:42:53 -08001991 # Write new ab_partitions.txt file
1992 common.ZipWrite(target_zip, new_ab_partitions, arcname=AB_PARTITIONS)
1993
Yifan Hong50e79542018-11-08 17:44:12 -08001994 common.ZipClose(target_zip)
1995
1996 return target_file
1997
1998
Tao Baoc098e9e2016-01-07 13:03:56 -08001999def WriteABOTAPackageWithBrilloScript(target_file, output_file,
2000 source_file=None):
Tao Baofe5b69a2018-03-02 09:47:43 -08002001 """Generates an Android OTA package that has A/B update payload."""
Tao Baodea0f8b2016-06-20 17:55:06 -07002002 # Stage the output zip package for package signing.
Tao Bao491d7e22018-02-21 13:17:22 -08002003 if not OPTIONS.no_signing:
2004 staging_file = common.MakeTempFile(suffix='.zip')
2005 else:
2006 staging_file = output_file
Tao Baoa652c002018-03-01 19:31:38 -08002007 output_zip = zipfile.ZipFile(staging_file, "w",
Tao Baoc098e9e2016-01-07 13:03:56 -08002008 compression=zipfile.ZIP_DEFLATED)
2009
Tao Bao481bab82017-12-21 11:23:09 -08002010 if source_file is not None:
2011 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
2012 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
2013 else:
2014 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
2015 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08002016
Tao Bao481bab82017-12-21 11:23:09 -08002017 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08002018 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08002019
Yifan Hong50e79542018-11-08 17:44:12 -08002020 if OPTIONS.retrofit_dynamic_partitions:
2021 target_file = GetTargetFilesZipForRetrofitDynamicPartitions(
Yifan Hongb433eba2019-03-06 12:42:53 -08002022 target_file, target_info.get("super_block_devices").strip().split(),
2023 target_info.get("dynamic_partition_list").strip().split())
Yifan Hong50e79542018-11-08 17:44:12 -08002024 elif OPTIONS.skip_postinstall:
Tao Bao15a146a2018-02-21 16:06:59 -08002025 target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
2026
Tao Bao40b18822018-01-30 18:19:04 -08002027 # Generate payload.
2028 payload = Payload()
2029
2030 # Enforce a max timestamp this payload can be applied on top of.
Tao Baoff1b86e2017-10-03 14:17:57 -07002031 if OPTIONS.downgrade:
Tao Bao2a12ed72018-01-22 11:35:00 -08002032 max_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baoff1b86e2017-10-03 14:17:57 -07002033 else:
2034 max_timestamp = metadata["post-timestamp"]
Tao Bao40b18822018-01-30 18:19:04 -08002035 additional_args = ["--max_timestamp", max_timestamp]
Tao Baoc098e9e2016-01-07 13:03:56 -08002036
Tao Bao40b18822018-01-30 18:19:04 -08002037 payload.Generate(target_file, source_file, additional_args)
Tao Baoc098e9e2016-01-07 13:03:56 -08002038
Tao Bao40b18822018-01-30 18:19:04 -08002039 # Sign the payload.
Tao Baof7140c02018-01-30 17:09:24 -08002040 payload_signer = PayloadSigner()
2041 payload.Sign(payload_signer)
Tao Baoc098e9e2016-01-07 13:03:56 -08002042
Tao Bao40b18822018-01-30 18:19:04 -08002043 # Write the payload into output zip.
2044 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08002045
Tao Baof7140c02018-01-30 17:09:24 -08002046 # Generate and include the secondary payload that installs secondary images
2047 # (e.g. system_other.img).
2048 if OPTIONS.include_secondary:
2049 # We always include a full payload for the secondary slot, even when
2050 # building an incremental OTA. See the comments for "--include_secondary".
Tao Bao15a146a2018-02-21 16:06:59 -08002051 secondary_target_file = GetTargetFilesZipForSecondaryImages(
2052 target_file, OPTIONS.skip_postinstall)
Tao Bao667ff572018-02-10 00:02:40 -08002053 secondary_payload = Payload(secondary=True)
Tao Baodb1fe412018-02-09 23:15:05 -08002054 secondary_payload.Generate(secondary_target_file,
2055 additional_args=additional_args)
Tao Baof7140c02018-01-30 17:09:24 -08002056 secondary_payload.Sign(payload_signer)
Tao Bao667ff572018-02-10 00:02:40 -08002057 secondary_payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08002058
Tianjie Xucfa86222016-03-07 16:31:19 -08002059 # If dm-verity is supported for the device, copy contents of care_map
2060 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07002061 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08002062 if (target_info.get("verity") == "true" or
2063 target_info.get("avb_enable") == "true"):
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07002064 care_map_list = [x for x in ["care_map.pb", "care_map.txt"] if
2065 "META/" + x in target_zip.namelist()]
2066
2067 # Adds care_map if either the protobuf format or the plain text one exists.
2068 if care_map_list:
2069 care_map_name = care_map_list[0]
2070 care_map_data = target_zip.read("META/" + care_map_name)
2071 # In order to support streaming, care_map needs to be packed as
Tao Bao40b18822018-01-30 18:19:04 -08002072 # ZIP_STORED.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07002073 common.ZipWriteStr(output_zip, care_map_name, care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08002074 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08002075 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002076 logger.warning("Cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07002077
Tao Baobcd1d162017-08-26 13:10:26 -07002078 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08002079 target_zip, output_zip, target_info, source_info)
Tao Bao21803d32017-04-19 10:16:09 -07002080
Tao Bao21803d32017-04-19 10:16:09 -07002081 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08002082
Tao Baofe5b69a2018-03-02 09:47:43 -08002083 # We haven't written the metadata entry yet, which will be handled in
2084 # FinalizeMetadata().
Tao Baoc96316c2017-01-24 22:10:49 -08002085 common.ZipClose(output_zip)
2086
Tao Bao85f16982018-03-08 16:28:33 -08002087 # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers
2088 # all the info of the latter. However, system updaters and OTA servers need to
2089 # take time to switch to the new flag. We keep both of the flags for
2090 # P-timeframe, and will remove StreamingPropertyFiles in later release.
Tao Baod3fc38a2018-03-08 16:09:01 -08002091 needed_property_files = (
Tao Bao85f16982018-03-08 16:28:33 -08002092 AbOtaPropertyFiles(),
Tao Baod3fc38a2018-03-08 16:09:01 -08002093 StreamingPropertyFiles(),
2094 )
2095 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Tao Baoc96316c2017-01-24 22:10:49 -08002096
Tao Baoc098e9e2016-01-07 13:03:56 -08002097
Doug Zongkereef39442009-04-02 12:14:19 -07002098def main(argv):
2099
2100 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07002101 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07002102 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07002103 elif o in ("-i", "--incremental_from"):
2104 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07002105 elif o == "--full_radio":
2106 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07002107 elif o == "--full_bootloader":
2108 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08002109 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002110 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08002111 elif o == "--downgrade":
2112 OPTIONS.downgrade = True
2113 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08002114 elif o == "--override_timestamp":
Tao Baofaa8e0b2018-04-12 14:31:43 -07002115 OPTIONS.downgrade = True
Michael Runge6e836112014-04-15 17:40:21 -07002116 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08002117 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08002118 elif o == "--oem_no_mount":
2119 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07002120 elif o in ("-e", "--extra_script"):
2121 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02002122 elif o in ("-t", "--worker_threads"):
2123 if a.isdigit():
2124 OPTIONS.worker_threads = int(a)
2125 else:
2126 raise ValueError("Cannot parse value %r for option %r - only "
2127 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002128 elif o in ("-2", "--two_step"):
2129 OPTIONS.two_step = True
Tao Baof7140c02018-01-30 17:09:24 -08002130 elif o == "--include_secondary":
2131 OPTIONS.include_secondary = True
Doug Zongker26e66192014-02-20 13:22:07 -08002132 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002133 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07002134 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07002135 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08002136 elif o == "--block":
2137 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002138 elif o in ("-b", "--binary"):
2139 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07002140 elif o == "--stash_threshold":
2141 try:
2142 OPTIONS.stash_threshold = float(a)
2143 except ValueError:
2144 raise ValueError("Cannot parse value %r for option %r - expecting "
2145 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08002146 elif o == "--log_diff":
2147 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002148 elif o == "--payload_signer":
2149 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002150 elif o == "--payload_signer_args":
2151 OPTIONS.payload_signer_args = shlex.split(a)
xunchang376cc7c2019-04-08 23:04:58 -07002152 elif o == "--payload_signer_key_size":
2153 OPTIONS.payload_signer_key_size = a
Dan Willemsencea5cd22017-03-21 14:44:27 -07002154 elif o == "--extracted_input_target_files":
2155 OPTIONS.extracted_input = a
Tao Bao15a146a2018-02-21 16:06:59 -08002156 elif o == "--skip_postinstall":
2157 OPTIONS.skip_postinstall = True
Yifan Hong50e79542018-11-08 17:44:12 -08002158 elif o == "--retrofit_dynamic_partitions":
2159 OPTIONS.retrofit_dynamic_partitions = True
xunchangabfa2652019-02-19 16:27:10 -08002160 elif o == "--skip_compatibility_check":
2161 OPTIONS.skip_compatibility_check = True
xunchang1cfe2512019-02-19 14:14:48 -08002162 elif o == "--output_metadata_path":
2163 OPTIONS.output_metadata_path = a
Doug Zongkereef39442009-04-02 12:14:19 -07002164 else:
2165 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002166 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002167
2168 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08002169 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002170 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07002171 "package_key=",
2172 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002173 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002174 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002175 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08002176 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08002177 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07002178 "extra_script=",
2179 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002180 "two_step",
Tao Baof7140c02018-01-30 17:09:24 -08002181 "include_secondary",
Dan Albert8b72aef2015-03-23 19:13:21 -07002182 "no_signing",
2183 "block",
2184 "binary=",
2185 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08002186 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002187 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07002188 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002189 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002190 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002191 "payload_signer_args=",
xunchang376cc7c2019-04-08 23:04:58 -07002192 "payload_signer_key_size=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07002193 "extracted_input_target_files=",
Tao Bao15a146a2018-02-21 16:06:59 -08002194 "skip_postinstall",
Yifan Hong50e79542018-11-08 17:44:12 -08002195 "retrofit_dynamic_partitions",
xunchangabfa2652019-02-19 16:27:10 -08002196 "skip_compatibility_check",
xunchang1cfe2512019-02-19 14:14:48 -08002197 "output_metadata_path=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002198 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002199
2200 if len(args) != 2:
2201 common.Usage(__doc__)
2202 sys.exit(1)
2203
Tao Bao32fcdab2018-10-12 10:30:39 -07002204 common.InitLogging()
2205
Tao Bao5d182562016-02-23 11:38:39 -08002206 if OPTIONS.downgrade:
Tao Bao5d182562016-02-23 11:38:39 -08002207 # We should only allow downgrading incrementals (as opposed to full).
2208 # Otherwise the device may go back from arbitrary build with this full
2209 # OTA package.
2210 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002211 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08002212
Tao Bao2db13852018-01-08 22:28:57 -08002213 # Load the build info dicts from the zip directly or the extracted input
2214 # directory. We don't need to unzip the entire target-files zips, because they
2215 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
2216 # When loading the info dicts, we don't need to provide the second parameter
2217 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
2218 # some properties with their actual paths, such as 'selinux_fc',
2219 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07002220 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08002221 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07002222 else:
Tao Bao2db13852018-01-08 22:28:57 -08002223 with zipfile.ZipFile(args[0], 'r') as input_zip:
2224 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08002225
Tao Bao32fcdab2018-10-12 10:30:39 -07002226 logger.info("--- target info ---")
2227 common.DumpInfoDict(OPTIONS.info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002228
2229 # Load the source build dict if applicable.
2230 if OPTIONS.incremental_source is not None:
2231 OPTIONS.target_info_dict = OPTIONS.info_dict
2232 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
2233 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2234
Tao Bao32fcdab2018-10-12 10:30:39 -07002235 logger.info("--- source info ---")
2236 common.DumpInfoDict(OPTIONS.source_info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002237
2238 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08002239 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
2240
Yifan Hong50e79542018-11-08 17:44:12 -08002241 # Assume retrofitting dynamic partitions when base build does not set
Yifan Hong50611032018-11-20 14:27:38 -08002242 # use_dynamic_partitions but target build does.
Yifan Hong50e79542018-11-08 17:44:12 -08002243 if (OPTIONS.source_info_dict and
Yifan Hong50611032018-11-20 14:27:38 -08002244 OPTIONS.source_info_dict.get("use_dynamic_partitions") != "true" and
2245 OPTIONS.target_info_dict.get("use_dynamic_partitions") == "true"):
Yifan Hong50e79542018-11-08 17:44:12 -08002246 if OPTIONS.target_info_dict.get("dynamic_partition_retrofit") != "true":
2247 raise common.ExternalError(
2248 "Expect to generate incremental OTA for retrofitting dynamic "
2249 "partitions, but dynamic_partition_retrofit is not set in target "
2250 "build.")
2251 logger.info("Implicitly generating retrofit incremental OTA.")
2252 OPTIONS.retrofit_dynamic_partitions = True
2253
2254 # Skip postinstall for retrofitting dynamic partitions.
2255 if OPTIONS.retrofit_dynamic_partitions:
2256 OPTIONS.skip_postinstall = True
2257
Tao Baoc098e9e2016-01-07 13:03:56 -08002258 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2259
Christian Oderf63e2cd2017-05-01 22:30:15 +02002260 # Use the default key to sign the package if not specified with package_key.
2261 # package_keys are needed on ab_updates, so always define them if an
2262 # ab_update is getting created.
2263 if not OPTIONS.no_signing or ab_update:
2264 if OPTIONS.package_key is None:
2265 OPTIONS.package_key = OPTIONS.info_dict.get(
2266 "default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -07002267 "build/make/target/product/security/testkey")
Christian Oderf63e2cd2017-05-01 22:30:15 +02002268 # Get signing keys
2269 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
2270
Tao Baoc098e9e2016-01-07 13:03:56 -08002271 if ab_update:
Tao Baoc098e9e2016-01-07 13:03:56 -08002272 WriteABOTAPackageWithBrilloScript(
2273 target_file=args[0],
2274 output_file=args[1],
2275 source_file=OPTIONS.incremental_source)
2276
Tao Bao32fcdab2018-10-12 10:30:39 -07002277 logger.info("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08002278 return
2279
Tao Bao2db13852018-01-08 22:28:57 -08002280 # Sanity check the loaded info dicts first.
2281 if OPTIONS.info_dict.get("no_recovery") == "true":
2282 raise common.ExternalError(
2283 "--- target build has specified no recovery ---")
2284
2285 # Non-A/B OTAs rely on /cache partition to store temporary files.
2286 cache_size = OPTIONS.info_dict.get("cache_size")
2287 if cache_size is None:
Tao Bao32fcdab2018-10-12 10:30:39 -07002288 logger.warning("--- can't determine the cache partition size ---")
Tao Bao2db13852018-01-08 22:28:57 -08002289 OPTIONS.cache_size = cache_size
2290
Doug Zongker1c390a22009-05-14 19:06:36 -07002291 if OPTIONS.extra_script is not None:
Tao Bao59cf0c52019-06-25 10:04:24 -07002292 with open(OPTIONS.extra_script) as fp:
2293 OPTIONS.extra_script = fp.read()
Doug Zongker1c390a22009-05-14 19:06:36 -07002294
Dan Willemsencea5cd22017-03-21 14:44:27 -07002295 if OPTIONS.extracted_input is not None:
2296 OPTIONS.input_tmp = OPTIONS.extracted_input
Dan Willemsencea5cd22017-03-21 14:44:27 -07002297 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002298 logger.info("unzipping target target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002299 OPTIONS.input_tmp = common.UnzipTemp(args[0], UNZIP_PATTERN)
Tao Bao2db13852018-01-08 22:28:57 -08002300 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002301
Tao Bao2db13852018-01-08 22:28:57 -08002302 # If the caller explicitly specified the device-specific extensions path via
2303 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
2304 # is present in the target target_files. Otherwise, take the path of the file
2305 # from 'tool_extensions' in the info dict and look for that in the local
2306 # filesystem, relative to the current directory.
Doug Zongker37974732010-09-16 17:44:38 -07002307 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002308 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2309 if os.path.exists(from_input):
Tao Bao32fcdab2018-10-12 10:30:39 -07002310 logger.info("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002311 OPTIONS.device_specific = from_input
2312 else:
Tao Bao2db13852018-01-08 22:28:57 -08002313 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002314
Doug Zongker37974732010-09-16 17:44:38 -07002315 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002316 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002317
Tao Bao767e3ac2015-11-10 12:19:19 -08002318 # Generate a full OTA.
Tao Bao0c6a4142017-12-15 10:11:19 -08002319 if OPTIONS.incremental_source is None:
Tao Baodba59ee2018-01-09 13:21:02 -08002320 with zipfile.ZipFile(args[0], 'r') as input_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002321 WriteFullOTAPackage(
2322 input_zip,
2323 output_file=args[1])
Tao Bao767e3ac2015-11-10 12:19:19 -08002324
Tao Bao32b80dc2018-01-08 22:50:47 -08002325 # Generate an incremental OTA.
Tao Bao767e3ac2015-11-10 12:19:19 -08002326 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002327 logger.info("unzipping source target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002328 OPTIONS.source_tmp = common.UnzipTemp(
2329 OPTIONS.incremental_source, UNZIP_PATTERN)
2330 with zipfile.ZipFile(args[0], 'r') as input_zip, \
2331 zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002332 WriteBlockIncrementalOTAPackage(
2333 input_zip,
2334 source_zip,
2335 output_file=args[1])
Tao Bao32b80dc2018-01-08 22:50:47 -08002336
2337 if OPTIONS.log_diff:
2338 with open(OPTIONS.log_diff, 'w') as out_file:
Tao Baod62c6032015-11-30 09:40:20 -08002339 import target_files_diff
Tao Bao32b80dc2018-01-08 22:50:47 -08002340 target_files_diff.recursiveDiff(
2341 '', OPTIONS.source_tmp, OPTIONS.input_tmp, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07002342
Tao Bao32fcdab2018-10-12 10:30:39 -07002343 logger.info("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002344
2345
2346if __name__ == '__main__':
2347 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002348 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002349 main(sys.argv[1:])
Tao Bao32fcdab2018-10-12 10:30:39 -07002350 except common.ExternalError:
2351 logger.exception("\n ERROR:\n")
Doug Zongkereef39442009-04-02 12:14:19 -07002352 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002353 finally:
2354 common.Cleanup()